diff --git a/internal/integration_test/filecache/filecache_test.go b/internal/integration_test/filecache/filecache_test.go new file mode 100644 index 00000000..9c31f968 --- /dev/null +++ b/internal/integration_test/filecache/filecache_test.go @@ -0,0 +1,66 @@ +package filecache + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "strings" + "testing" + + "github.com/tetratelabs/wazero/experimental" + "github.com/tetratelabs/wazero/internal/engine/compiler" + "github.com/tetratelabs/wazero/internal/integration_test/spectest" + v1 "github.com/tetratelabs/wazero/internal/integration_test/spectest/v1" + "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/internal/testing/require" +) + +func TestSpecTestCompilerCache(t *testing.T) { + if !platform.CompilerSupported() { + return + } + + const cachePathKey = "FILE_CACHE_DIR" + cacheDir := os.Getenv(cachePathKey) + if len(cacheDir) == 0 { + // This case, this is the parent of the test. + cacheDir = t.TempDir() + + // Before running test, no file should exist in the directory. + files, err := os.ReadDir(cacheDir) + require.NoError(t, err) + require.True(t, len(files) == 0) + + // Get the executable path of this test. + testExecutable, err := os.Executable() + require.NoError(t, err) + + // Execute this test multiple times with the env $cachePathKey=cacheDir, so that + // the subsequent execution of this test will enter the following "else" block. + var exp []string + buf := bytes.NewBuffer(nil) + for i := 0; i < 5; i++ { + cmd := exec.Command(testExecutable) + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", cachePathKey, cacheDir)) + cmd.Stdout = buf + cmd.Stderr = buf + err = cmd.Run() + require.NoError(t, err) + exp = append(exp, "PASS\n") + } + + // Ensures that the tests actually run 5 times. + require.Equal(t, strings.Join(exp, ""), buf.String()) + + // Check the number of cache files is greater than zero. + files, err = os.ReadDir(cacheDir) + require.NoError(t, err) + require.True(t, len(files) > 0) + } else { + // Run the spectest with the file cache. + ctx := experimental.WithCompilationCacheDirName(context.Background(), cacheDir) + spectest.Run(t, v1.Testcases, ctx, compiler.NewEngine, v1.EnabledFeatures) + } +} diff --git a/internal/integration_test/spectest/spectest.go b/internal/integration_test/spectest/spectest.go index e8b230ca..626cf253 100644 --- a/internal/integration_test/spectest/spectest.go +++ b/internal/integration_test/spectest/spectest.go @@ -24,9 +24,6 @@ import ( "github.com/tetratelabs/wazero/internal/watzero" ) -// testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors. -var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary") - // TODO: complete porting this to wazero API type ( testbase struct { @@ -323,7 +320,7 @@ func (c command) expectedError() (err error) { // // See https://github.com/WebAssembly/spec/blob/wg-1.0/test/core/imports.wast // See https://github.com/WebAssembly/spec/blob/wg-1.0/interpreter/script/js.ml#L13-L25 -func addSpectestModule(t *testing.T, s *wasm.Store, ns *wasm.Namespace) { +func addSpectestModule(t *testing.T, ctx context.Context, s *wasm.Store, ns *wasm.Namespace) { w, err := watzero.Wat2Wasm(`(module $spectest (; TODO (global (export "global_i32") i32) @@ -401,10 +398,10 @@ func addSpectestModule(t *testing.T, s *wasm.Store, ns *wasm.Namespace) { maybeSetMemoryCap(mod) mod.BuildFunctionDefinitions() - err = s.Engine.CompileModule(testCtx, mod) + err = s.Engine.CompileModule(ctx, mod) require.NoError(t, err) - _, err = s.Instantiate(testCtx, ns, mod, mod.NameSection.ModuleName, sys.DefaultContext(nil), nil) + _, err = s.Instantiate(ctx, ns, mod, mod.NameSection.ModuleName, sys.DefaultContext(nil), nil) require.NoError(t, err) } @@ -417,7 +414,7 @@ func maybeSetMemoryCap(mod *wasm.Module) { // Run runs all the test inside the testDataFS file system where all the cases are described // via JSON files created from wast2json. -func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm.Features) wasm.Engine, enabledFeatures wasm.Features) { +func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, newEngine func(context.Context, wasm.Features) wasm.Engine, enabledFeatures wasm.Features) { files, err := testDataFS.ReadDir("testdata") require.NoError(t, err) @@ -443,8 +440,8 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm wastName := basename(base.SourceFile) t.Run(wastName, func(t *testing.T) { - s, ns := wasm.NewStore(enabledFeatures, newEngine(testCtx, enabledFeatures)) - addSpectestModule(t, s, ns) + s, ns := wasm.NewStore(enabledFeatures, newEngine(ctx, enabledFeatures)) + addSpectestModule(t, ctx, s, ns) var lastInstantiatedModuleName string for _, c := range base.Commands { @@ -467,10 +464,10 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm maybeSetMemoryCap(mod) mod.BuildFunctionDefinitions() - err = s.Engine.CompileModule(testCtx, mod) + err = s.Engine.CompileModule(ctx, mod) require.NoError(t, err, msg) - _, err = s.Instantiate(testCtx, ns, mod, moduleName, nil, nil) + _, err = s.Instantiate(ctx, ns, mod, moduleName, nil, nil) lastInstantiatedModuleName = moduleName require.NoError(t, err) case "register": @@ -492,7 +489,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm if c.Action.Module != "" { msg += " in module " + c.Action.Module } - vals, types, err := callFunction(ns, moduleName, c.Action.Field, args...) + vals, types, err := callFunction(ns, ctx, moduleName, c.Action.Field, args...) require.NoError(t, err, msg) require.Equal(t, len(exps), len(vals), msg) laneTypes := map[int]string{} @@ -526,7 +523,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm expType = wasm.ValueTypeF64 } require.Equal(t, expType, global.Type(), msg) - require.Equal(t, exps[0], global.Get(testCtx), msg) + require.Equal(t, exps[0], global.Get(ctx), msg) default: t.Fatalf("unsupported action type type: %v", c) } @@ -535,7 +532,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm // We don't support direct loading of wast yet. buf, err := testDataFS.ReadFile(testdataPath(c.Filename)) require.NoError(t, err, msg) - requireInstantiationError(t, s, ns, buf, msg) + requireInstantiationError(t, ctx, s, ns, buf, msg) } case "assert_trap": moduleName := lastInstantiatedModuleName @@ -549,7 +546,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm if c.Action.Module != "" { msg += " in module " + c.Action.Module } - _, _, err := callFunction(ns, moduleName, c.Action.Field, args...) + _, _, err := callFunction(ns, ctx, moduleName, c.Action.Field, args...) require.ErrorIs(t, err, c.expectedError(), msg) default: t.Fatalf("unsupported action type type: %v", c) @@ -561,7 +558,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm } buf, err := testDataFS.ReadFile(testdataPath(c.Filename)) require.NoError(t, err, msg) - requireInstantiationError(t, s, ns, buf, msg) + requireInstantiationError(t, ctx, s, ns, buf, msg) case "assert_exhaustion": moduleName := lastInstantiatedModuleName switch c.Action.ActionType { @@ -571,7 +568,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm if c.Action.Module != "" { msg += " in module " + c.Action.Module } - _, _, err := callFunction(ns, moduleName, c.Action.Field, args...) + _, _, err := callFunction(ns, ctx, moduleName, c.Action.Field, args...) require.ErrorIs(t, err, wasmruntime.ErrRuntimeCallStackOverflow, msg) default: t.Fatalf("unsupported action type type: %v", c) @@ -583,7 +580,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm } buf, err := testDataFS.ReadFile(testdataPath(c.Filename)) require.NoError(t, err, msg) - requireInstantiationError(t, s, ns, buf, msg) + requireInstantiationError(t, ctx, s, ns, buf, msg) case "assert_uninstantiable": buf, err := testDataFS.ReadFile(testdataPath(c.Filename)) require.NoError(t, err, msg) @@ -605,13 +602,13 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm maybeSetMemoryCap(mod) mod.BuildFunctionDefinitions() - err = s.Engine.CompileModule(testCtx, mod) + err = s.Engine.CompileModule(ctx, mod) require.NoError(t, err, msg) - _, err = s.Instantiate(testCtx, ns, mod, t.Name(), nil, nil) + _, err = s.Instantiate(ctx, ns, mod, t.Name(), nil, nil) require.NoError(t, err, msg) } else { - requireInstantiationError(t, s, ns, buf, msg) + requireInstantiationError(t, ctx, s, ns, buf, msg) } default: @@ -623,7 +620,7 @@ func Run(t *testing.T, testDataFS embed.FS, newEngine func(context.Context, wasm } } -func requireInstantiationError(t *testing.T, s *wasm.Store, ns *wasm.Namespace, buf []byte, msg string) { +func requireInstantiationError(t *testing.T, ctx context.Context, s *wasm.Store, ns *wasm.Namespace, buf []byte, msg string) { mod, err := binaryformat.DecodeModule(buf, s.EnabledFeatures, wasm.MemorySizer) if err != nil { return @@ -638,12 +635,12 @@ func requireInstantiationError(t *testing.T, s *wasm.Store, ns *wasm.Namespace, maybeSetMemoryCap(mod) mod.BuildFunctionDefinitions() - err = s.Engine.CompileModule(testCtx, mod) + err = s.Engine.CompileModule(ctx, mod) if err != nil { return } - _, err = s.Instantiate(testCtx, ns, mod, t.Name(), nil, nil) + _, err = s.Instantiate(ctx, ns, mod, t.Name(), nil, nil) require.Error(t, err, msg) } @@ -831,9 +828,9 @@ func f64Equal(expected, actual float64) (matched bool) { // callFunction is inlined here as the spectest needs to validate the signature was correct // TODO: This is likely already covered with unit tests! -func callFunction(ns *wasm.Namespace, moduleName, funcName string, params ...uint64) ([]uint64, []wasm.ValueType, error) { +func callFunction(ns *wasm.Namespace, ctx context.Context, moduleName, funcName string, params ...uint64) ([]uint64, []wasm.ValueType, error) { fn := ns.Module(moduleName).ExportedFunction(funcName) - results, err := fn.Call(testCtx, params...) + results, err := fn.Call(ctx, params...) return results, fn.Definition().ResultTypes(), err } diff --git a/internal/integration_test/spectest/v1/spec_test.go b/internal/integration_test/spectest/v1/spec_test.go index 49ae2621..b1e2aca0 100644 --- a/internal/integration_test/spectest/v1/spec_test.go +++ b/internal/integration_test/spectest/v1/spec_test.go @@ -1,33 +1,26 @@ -package spectest +package v1 import ( - "embed" + "context" "testing" "github.com/tetratelabs/wazero/internal/engine/compiler" "github.com/tetratelabs/wazero/internal/engine/interpreter" "github.com/tetratelabs/wazero/internal/integration_test/spectest" "github.com/tetratelabs/wazero/internal/platform" - "github.com/tetratelabs/wazero/internal/wasm" ) -//go:embed testdata/*.wasm -//go:embed testdata/*.json -var testcases embed.FS - -const enabledFeatures = wasm.Features20191205 - func TestCompiler(t *testing.T) { if !platform.CompilerSupported() { t.Skip() } - spectest.Run(t, testcases, compiler.NewEngine, enabledFeatures) + spectest.Run(t, Testcases, context.Background(), compiler.NewEngine, EnabledFeatures) } func TestInterpreter(t *testing.T) { - spectest.Run(t, testcases, interpreter.NewEngine, enabledFeatures) + spectest.Run(t, Testcases, context.Background(), interpreter.NewEngine, EnabledFeatures) } func TestBinaryEncoder(t *testing.T) { - spectest.TestBinaryEncoder(t, testcases, enabledFeatures) + spectest.TestBinaryEncoder(t, Testcases, EnabledFeatures) } diff --git a/internal/integration_test/spectest/v1/spectest.go b/internal/integration_test/spectest/v1/spectest.go new file mode 100644 index 00000000..a1acb45f --- /dev/null +++ b/internal/integration_test/spectest/v1/spectest.go @@ -0,0 +1,16 @@ +package v1 + +import ( + "embed" + + "github.com/tetratelabs/wazero/internal/wasm" +) + +// Testcases is exported for cross-process file cache tests. +// +//go:embed testdata/*.wasm +//go:embed testdata/*.json +var Testcases embed.FS + +// EnabledFeatures is exported for cross-process file cache tests. +const EnabledFeatures = wasm.Features20191205 diff --git a/internal/integration_test/spectest/v2/spec_test.go b/internal/integration_test/spectest/v2/spec_test.go index 4ad0e27d..4a4574ad 100644 --- a/internal/integration_test/spectest/v2/spec_test.go +++ b/internal/integration_test/spectest/v2/spec_test.go @@ -1,6 +1,7 @@ package spectest import ( + "context" "embed" "testing" @@ -21,9 +22,9 @@ func TestCompiler(t *testing.T) { if !platform.CompilerSupported() { t.Skip() } - spectest.Run(t, testcases, compiler.NewEngine, enabledFeatures) + spectest.Run(t, testcases, context.Background(), compiler.NewEngine, enabledFeatures) } func TestInterpreter(t *testing.T) { - spectest.Run(t, testcases, interpreter.NewEngine, enabledFeatures) + spectest.Run(t, testcases, context.Background(), interpreter.NewEngine, enabledFeatures) }