<commit_before>// Copyright (c) 2018 <NAME>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package wag

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/tsavola/wag/dewag"
	"github.com/tsavola/wag/internal/test/runner"
	"github.com/tsavola/wag/types"
)

type testDuration struct {
	time.Duration
}

func (target *testDuration) set(d time.Duration) {
	if target.Duration <= 0 || d < target.Duration {
		target.Duration = d
	}
}

func makeNoTrigger() chan struct{} { return nil }
func makeTrigger() chan struct{}   { return make(chan struct{}) }

type dummyEnv struct{}

func (*dummyEnv) ImportFunction(module, field string, sig types.Function) (variadic bool, absAddr uint64, err error) {
	return
}

func (*dummyEnv) ImportGlobal(module, field string, t types.T) (valueBits uint64, err error) {
	return
}

var loadBenchmarkEnv Environment = new(dummyEnv)

const (
	loadBenchmarkFilename      = "testdata/large.wasm"
	loadBenchmarkEntrySymbol   = "run"
	loadBenchmarkEntryNumArgs  = 2
	loadBenchmarkMaxTextSize   = 16 * 1024 * 1024
	loadBenchmarkMaxRODataSize = 4096
	loadBenchmarkRODataAddr    = 0x10000
	loadBenchmarkTextSum       = "dea5d76345f70be24fc3f28b3baf52b5c03401c4009c87cdc6d1f609e525b35e"
	loadBenchmarkRODataSum     = "ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7"
)

func BenchmarkLoad(b *testing.B)                { benchmarkLoad(b, makeNoTrigger) }
func BenchmarkLoadSections(b *testing.B)        { benchmarkLoadSections(b, makeNoTrigger) }
func BenchmarkLoadTrigger(b *testing.B)         { benchmarkLoad(b, makeTrigger) }
func BenchmarkLoadTriggerSections(b *testing.B) { benchmarkLoadSections(b, makeTrigger) }

func benchmarkLoad(b *testing.B, makeOptionalTrigger func() chan struct{}) {
	b.Helper()

	wasm, err := ioutil.ReadFile(loadBenchmarkFilename)
	if err != nil {
		b.Fatal(err)
	}

	text := make([]byte, 0, loadBenchmarkMaxTextSize)
	roData := make([]byte, loadBenchmarkMaxRODataSize)

	b.StopTimer()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m := Module{
			EntrySymbol: loadBenchmarkEntrySymbol,
			EntryArgs:   make([]uint64, loadBenchmarkEntryNumArgs),
		}

		r := bytes.NewReader(wasm)
		textBuf := bytes.NewBuffer(text)
		trigger := makeOptionalTrigger()

		b.StartTimer()
		m.load(r, loadBenchmarkEnv, textBuf, roData, loadBenchmarkRODataAddr, trigger)
		b.StopTimer()

		checkLoadBenchmarkOutput(b, textBuf, roData)
	}
}

func benchmarkLoadSections(b *testing.B, makeOptionalTrigger func() chan struct{}) {
	b.Helper()

	wasm, err := ioutil.ReadFile(loadBenchmarkFilename)
	if err != nil {
		b.Fatal(err)
	}

	text := make([]byte, 0, loadBenchmarkMaxTextSize)
	roData := make([]byte, loadBenchmarkMaxRODataSize)

	var (
		elapPre  testDuration
		elapCode testDuration
		elapData testDuration
	)

	b.StopTimer()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m := Module{
			EntrySymbol: loadBenchmarkEntrySymbol,
			EntryArgs:   make([]uint64, loadBenchmarkEntryNumArgs),
		}

		r := bytes.NewReader(wasm)
		textBuf := bytes.NewBuffer(text)
		trigger := makeOptionalTrigger()

		b.StartTimer()

		t0 := time.Now()
		m.loadPreliminarySections(r, loadBenchmarkEnv)
		t1 := time.Now()
		m.loadCodeSection(r, textBuf, roData, loadBenchmarkRODataAddr, trigger)
		t2 := time.Now()
		m.loadDataSection(r)
		t3 := time.Now()

		b.StopTimer()

		elapPre.set(t1.Sub(t0))
		elapCode.set(t2.Sub(t1))
		elapData.set(t3.Sub(t2))

		checkLoadBenchmarkOutput(b, textBuf, roData)
	}

	b.Logf("pre:  %v", elapPre)
	b.Logf("code: %v", elapCode)
	b.Logf("data: %v", elapData)
}

func checkLoadBenchmarkOutput(b *testing.B, textBuf *bytes.Buffer, roData []byte) {
	b.Helper()

	if textBuf.Len() > loadBenchmarkMaxTextSize {
		b.Errorf("loadBenchmarkMaxTextSize is too small (text size is %d)", textBuf.Len())
	}

	sum := sha256.Sum256(textBuf.Bytes())
	textSum := hex.EncodeToString(sum[:])
	if textSum != loadBenchmarkTextSum {
		b.Errorf("text checksum changed: %s", textSum)
	}

	sum = sha256.Sum256(roData)
	roDataSum := hex.EncodeToString(sum[:])
	if roDataSum != loadBenchmarkRODataSum {
		b.Errorf("read-only data checksum changed: %s", roDataSum)
	}
}

func TestBenchmarkRunNqueens(t *testing.T) {
	const (
		filename = "testdata/nqueens.wasm"

		maxTextSize   = 65536
		maxRODataSize = 4096
		stackSize     = 65536
<commit_msg>TestBenchmarkRunNqueens is useless without output<commit_after>// Copyright (c) 2018 <NAME>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package wag

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"io/ioutil"
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/tsavola/wag/dewag"
	"github.com/tsavola/wag/internal/test/runner"
	"github.com/tsavola/wag/types"
)

type testDuration struct {
	time.Duration
}

func (target *testDuration) set(d time.Duration) {
	if target.Duration <= 0 || d < target.Duration {
		target.Duration = d
	}
}

func makeNoTrigger() chan struct{} { return nil }
func makeTrigger() chan struct{}   { return make(chan struct{}) }

type dummyEnv struct{}

func (*dummyEnv) ImportFunction(module, field string, sig types.Function) (variadic bool, absAddr uint64, err error) {
	return
}

func (*dummyEnv) ImportGlobal(module, field string, t types.T) (valueBits uint64, err error) {
	return
}

var loadBenchmarkEnv Environment = new(dummyEnv)

const (
	loadBenchmarkFilename      = "testdata/large.wasm"
	loadBenchmarkEntrySymbol   = "run"
	loadBenchmarkEntryNumArgs  = 2
	loadBenchmarkMaxTextSize   = 16 * 1024 * 1024
	loadBenchmarkMaxRODataSize = 4096
	loadBenchmarkRODataAddr    = 0x10000
	loadBenchmarkTextSum       = "dea5d76345f70be24fc3f28b3baf52b5c03401c4009c87cdc6d1f609e525b35e"
	loadBenchmarkRODataSum     = "ad7facb2586fc6e966c004d7d1d16b024f5805ff7cb47c7a85dabd8b48892ca7"
)

func BenchmarkLoad(b *testing.B)                { benchmarkLoad(b, makeNoTrigger) }
func BenchmarkLoadSections(b *testing.B)        { benchmarkLoadSections(b, makeNoTrigger) }
func BenchmarkLoadTrigger(b *testing.B)         { benchmarkLoad(b, makeTrigger) }
func BenchmarkLoadTriggerSections(b *testing.B) { benchmarkLoadSections(b, makeTrigger) }

func benchmarkLoad(b *testing.B, makeOptionalTrigger func() chan struct{}) {
	b.Helper()

	wasm, err := ioutil.ReadFile(loadBenchmarkFilename)
	if err != nil {
		b.Fatal(err)
	}

	text := make([]byte, 0, loadBenchmarkMaxTextSize)
	roData := make([]byte, loadBenchmarkMaxRODataSize)

	b.StopTimer()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m := Module{
			EntrySymbol: loadBenchmarkEntrySymbol,
			EntryArgs:   make([]uint64, loadBenchmarkEntryNumArgs),
		}

		r := bytes.NewReader(wasm)
		textBuf := bytes.NewBuffer(text)
		trigger := makeOptionalTrigger()

		b.StartTimer()
		m.load(r, loadBenchmarkEnv, textBuf, roData, loadBenchmarkRODataAddr, trigger)
		b.StopTimer()

		checkLoadBenchmarkOutput(b, textBuf, roData)
	}
}

func benchmarkLoadSections(b *testing.B, makeOptionalTrigger func() chan struct{}) {
	b.Helper()

	wasm, err := ioutil.ReadFile(loadBenchmarkFilename)
	if err != nil {
		b.Fatal(err)
	}

	text := make([]byte, 0, loadBenchmarkMaxTextSize)
	roData := make([]byte, loadBenchmarkMaxRODataSize)

	var (
		elapPre  testDuration
		elapCode testDuration
		elapData testDuration
	)

	b.StopTimer()
	b.ResetTimer()

	for i := 0; i < b.N; i++ {
		m := Module{
			EntrySymbol: loadBenchmarkEntrySymbol,
			EntryArgs:   make([]uint64, loadBenchmarkEntryNumArgs),
		}

		r := bytes.NewReader(wasm)
		textBuf := bytes.NewBuffer(text)
		trigger := makeOptionalTrigger()

		b.StartTimer()

		t0 := time.Now()
		m.loadPreliminarySections(r, loadBenchmarkEnv)
		t1 := time.Now()
		m.loadCodeSection(r, textBuf, roData, loadBenchmarkRODataAddr, trigger)
		t2 := time.Now()
		m.loadDataSection(r)
		t3 := time.Now()

		b.StopTimer()

		elapPre.set(t1.Sub(t0))
		elapCode.set(t2.Sub(t1))
		elapData.set(t3.Sub(t2))

		checkLoadBenchmarkOutput(b, textBuf, roData)
	}

	b.Logf("pre:  %v", elapPre)
	b.Logf("code: %v", elapCode)
	b.Logf("data: %v", elapData)
}

func checkLoadBenchmarkOutput(b *testing.B, textBuf *bytes.Buffer, roData []byte) {
	b.Helper()

	if textBuf.Len() > loadBenchmarkMaxTextSize {
		b.Errorf("loadBenchmarkMaxTextSize is too small (text size is %d)", textBuf.Len())
	}

	sum := sha256.Sum256(textBuf.Bytes())
	textSum := hex.EncodeToString(sum[:])
	if textSum != loadBenchmarkTextSum {
		b.Errorf("text checksum changed: %s", textSum)
	}

	sum = sha256.Sum256(roData)
	roDataSum := hex.EncodeToString(sum[:])
	if roDataSum != loadBenchmarkRODataSum {
		b.Errorf("read-only data checksum changed: %s", roDataSum)
	}
}

func TestBenchmarkRunNqueens(t *testing.T) {
	if !testing.Verbose() {
		t.SkipNow()
	}

	const (
		filename = "testdata/nqueens.wasm"

		maxTextSize   = 65536
		maxRODataSize = 4096
		stackSize     = 65536

{'fraction_non_alphanumeric': 0.12706067062913537, 'fraction_numerical': 0.03386789278905462, 'mean_word_length': 4.549471064094586, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2800384', 'n_tokens_mistral': 3691, 'n_tokens_neox': 3336, 'n_words': 679}