Files
wazero/internal/gojs/compiler_test.go
Crypt Keeper efc72de1e6 gojs: implements timeout events (#984)
Signed-off-by: Adrian Cole <adrian@tetrate.io>
2022-12-31 08:26:32 +08:00

169 lines
4.5 KiB
Go

package gojs_test
import (
"bytes"
"context"
_ "embed"
"fmt"
"io/fs"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
"runtime"
"testing"
"testing/fstest"
"time"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/experimental"
gojs "github.com/tetratelabs/wazero/imports/go"
internalgojs "github.com/tetratelabs/wazero/internal/gojs"
"github.com/tetratelabs/wazero/internal/gojs/run"
)
func compileAndRun(ctx context.Context, arg string, config wazero.ModuleConfig) (stdout, stderr string, err error) {
return compileAndRunWithRuntime(ctx, rt, arg, config) // use global runtime
}
func compileAndRunWithRuntime(ctx context.Context, r wazero.Runtime, arg string, config wazero.ModuleConfig) (stdout, stderr string, err error) {
var stdoutBuf, stderrBuf bytes.Buffer
ns := r.NewNamespace(ctx)
builder := r.NewHostModuleBuilder("go")
gojs.NewFunctionExporter().ExportFunctions(builder)
if _, err = builder.Instantiate(ctx, ns); err != nil {
return
}
// Note: this hits the file cache.
compiled, err := r.CompileModule(testCtx, testBin)
if err != nil {
log.Panicln(err)
}
var s *internalgojs.State
s, err = run.RunAndReturnState(ctx, ns, compiled, config.
WithStdout(&stdoutBuf).
WithStderr(&stderrBuf).
WithArgs("test", arg))
if err == nil {
if !reflect.DeepEqual(s, internalgojs.NewState(ctx)) {
log.Panicf("unexpected state: %v\n", s)
}
}
stdout = stdoutBuf.String()
stderr = stderrBuf.String()
return
}
// testBin is not checked in as it is >7.5MB
var testBin []byte
// testCtx is configured in TestMain to re-use wazero's compilation cache.
var (
testCtx context.Context
testFS = fstest.MapFS{
"empty.txt": {},
"test.txt": {Data: []byte("animals\n"), Mode: 0o644},
"sub": {Mode: fs.ModeDir | 0o755},
"sub/test.txt": {Data: []byte("greet sub dir\n"), Mode: 0o444},
}
rt wazero.Runtime
)
func TestMain(m *testing.M) {
// For some reason, windows and freebsd fail to compile with exit status 1.
if o := runtime.GOOS; o != "darwin" && o != "linux" {
log.Println("gojs: skipping due to not yet supported OS:", o)
os.Exit(0)
}
// Find the go binary (if present), and compile the Wasm binary.
goBin, err := findGoBin()
if err != nil {
log.Println("gojs: skipping due missing Go binary:", err)
os.Exit(0)
}
if err = compileJsWasm(goBin); err != nil {
log.Panicln(err)
}
// Define a compilation cache so that tests run faster. This works because
// all tests use the same binary.
compilationCacheDir, err := os.MkdirTemp("", "gojs")
if err != nil {
log.Panicln(err)
}
defer os.RemoveAll(compilationCacheDir)
testCtx, err = experimental.WithCompilationCacheDirName(context.Background(), compilationCacheDir)
if err != nil {
log.Panicln(err)
}
// Seed wazero's compilation cache to see any error up-front and to prevent
// one test from a cache-miss performance penalty.
rt = wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig())
_, err = rt.CompileModule(testCtx, testBin)
if err != nil {
log.Panicln(err)
}
var exit int
defer func() {
rt.Close(testCtx)
os.Exit(exit)
}()
// Configure fs test data
if d, err := fs.Sub(testFS, "sub"); err != nil {
log.Panicln(err)
} else if err = fstest.TestFS(d, "test.txt"); err != nil {
log.Panicln(err)
}
exit = m.Run()
}
// compileJsWasm allows us to generate a binary with runtime.GOOS=js and
// runtime.GOARCH=wasm. This intentionally does so on-demand, as it allows us
// to test the user's current version of Go, as opposed to a specific one.
// For example, this allows testing both Go 1.18 and 1.19 in CI.
func compileJsWasm(goBin string) error {
// Prepare the working directory.
workDir, err := os.MkdirTemp("", "example")
if err != nil {
return err
}
defer os.RemoveAll(workDir)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
bin := path.Join(workDir, "out.wasm")
cmd := exec.CommandContext(ctx, goBin, "build", "-o", bin, ".") //nolint:gosec
cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm", "GOWASM=satconv,signext")
cmd.Dir = "testdata"
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("couldn't compile %s: %w", string(out), err)
}
testBin, err = os.ReadFile(bin) //nolint:gosec
return err
}
func findGoBin() (string, error) {
binName := "go"
if runtime.GOOS == "windows" {
binName += ".exe"
}
goBin := filepath.Join(runtime.GOROOT(), "bin", binName)
if _, err := os.Stat(goBin); err == nil {
return goBin, nil
}
// Now, search the path
return exec.LookPath(binName)
}