wa-lang.org/wazero@v1.0.2/internal/gojs/compiler_test.go (about) 1 package gojs_test 2 3 import ( 4 "bytes" 5 "context" 6 _ "embed" 7 "fmt" 8 "io/fs" 9 "log" 10 "os" 11 "os/exec" 12 "path" 13 "path/filepath" 14 "runtime" 15 "testing" 16 "testing/fstest" 17 "time" 18 19 "wa-lang.org/wazero" 20 "wa-lang.org/wazero/experimental" 21 gojs "wa-lang.org/wazero/imports/go" 22 ) 23 24 func compileAndRun(ctx context.Context, arg string, config wazero.ModuleConfig) (stdout, stderr string, err error) { 25 var stdoutBuf, stderrBuf bytes.Buffer 26 27 r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig()) 28 defer r.Close(ctx) 29 30 compiled, compileErr := r.CompileModule(ctx, testBin) 31 if compileErr != nil { 32 err = compileErr 33 return 34 } 35 36 err = gojs.Run(ctx, r, compiled, config.WithStdout(&stdoutBuf).WithStderr(&stderrBuf). 37 WithArgs("test", arg)) 38 stdout = stdoutBuf.String() 39 stderr = stderrBuf.String() 40 return 41 } 42 43 // testBin is not checked in as it is >7.5MB 44 var testBin []byte 45 46 // testCtx is configured in TestMain to re-use wazero's compilation cache. 47 var ( 48 testCtx context.Context 49 testFS = fstest.MapFS{ 50 "empty.txt": {}, 51 "test.txt": {Data: []byte("animals")}, 52 "sub": {Mode: fs.ModeDir}, 53 "sub/test.txt": {Data: []byte("greet sub dir\n")}, 54 } 55 ) 56 57 func TestMain(m *testing.M) { 58 // For some reason, windows and freebsd fail to compile with exit status 1. 59 if o := runtime.GOOS; o != "darwin" && o != "linux" { 60 log.Println("gojs: skipping due to not yet supported OS:", o) 61 os.Exit(0) 62 } 63 64 // Find the go binary (if present), and compile the Wasm binary. 65 goBin, err := findGoBin() 66 if err != nil { 67 log.Println("gojs: skipping due missing Go binary:", err) 68 os.Exit(0) 69 } 70 if err = compileJsWasm(goBin); err != nil { 71 log.Panicln(err) 72 } 73 74 // Define a compilation cache so that tests run faster. This works because 75 // all tests use the same binary. 76 compilationCacheDir, err := os.MkdirTemp("", "gojs") 77 if err != nil { 78 log.Panicln(err) 79 } 80 defer os.RemoveAll(compilationCacheDir) 81 testCtx, err = experimental.WithCompilationCacheDirName(context.Background(), compilationCacheDir) 82 if err != nil { 83 log.Panicln(err) 84 } 85 86 // Seed wazero's compilation cache to see any error up-front and to prevent 87 // one test from a cache-miss performance penalty. 88 rt := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig()) 89 defer rt.Close(testCtx) 90 _, err = rt.CompileModule(testCtx, testBin) 91 if err != nil { 92 log.Panicln(err) 93 } 94 rt.Close(testCtx) 95 96 // Configure fs test data 97 if d, err := fs.Sub(testFS, "sub"); err != nil { 98 log.Panicln(err) 99 } else if err = fstest.TestFS(d, "test.txt"); err != nil { 100 log.Panicln(err) 101 } 102 os.Exit(m.Run()) 103 } 104 105 // compileJsWasm allows us to generate a binary with runtime.GOOS=js and 106 // runtime.GOARCH=wasm. This intentionally does so on-demand, as it allows us 107 // to test the user's current version of Go, as opposed to a specific one. 108 // For example, this allows testing both Go 1.18 and 1.19 in CI. 109 func compileJsWasm(goBin string) error { 110 // Prepare the working directory. 111 workDir, err := os.MkdirTemp("", "example") 112 if err != nil { 113 return err 114 } 115 defer os.RemoveAll(workDir) 116 117 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 118 defer cancel() 119 120 bin := path.Join(workDir, "out.wasm") 121 cmd := exec.CommandContext(ctx, goBin, "build", "-o", bin, ".") //nolint:gosec 122 cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm", "GOWASM=satconv,signext") 123 cmd.Dir = "testdata" 124 out, err := cmd.CombinedOutput() 125 if err != nil { 126 return fmt.Errorf("couldn't compile %s: %w", string(out), err) 127 } 128 129 testBin, err = os.ReadFile(bin) //nolint:gosec 130 return err 131 } 132 133 func findGoBin() (string, error) { 134 binName := "go" 135 if runtime.GOOS == "windows" { 136 binName += ".exe" 137 } 138 goBin := filepath.Join(runtime.GOROOT(), "bin", binName) 139 if _, err := os.Stat(goBin); err == nil { 140 return goBin, nil 141 } 142 // Now, search the path 143 return exec.LookPath(binName) 144 }