1 package gojs_test
2
3 import (
4 "bytes"
5 "context"
6 _ "embed"
7 "fmt"
8 "log"
9 "os"
10 "os/exec"
11 "path"
12 "path/filepath"
13 "reflect"
14 "runtime"
15 "strings"
16 "testing"
17 "time"
18
19 "github.com/tetratelabs/wazero"
20 "github.com/tetratelabs/wazero/experimental"
21 "github.com/tetratelabs/wazero/experimental/gojs"
22 "github.com/tetratelabs/wazero/internal/fstest"
23 internalgojs "github.com/tetratelabs/wazero/internal/gojs"
24 "github.com/tetratelabs/wazero/internal/gojs/config"
25 "github.com/tetratelabs/wazero/internal/gojs/run"
26 )
27
28 type newConfig func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config)
29
30 func defaultConfig(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) {
31 return moduleConfig, config.NewConfig()
32 }
33
34 func compileAndRun(ctx context.Context, arg string, config newConfig) (stdout, stderr string, err error) {
35 rt := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig().
36
37
38 WithMemoryCapacityFromMax(true).
39
40 WithMemoryLimitPages(1024).
41 WithCompilationCache(cache))
42 return compileAndRunWithRuntime(ctx, rt, arg, config)
43 }
44
45 func compileAndRunWithRuntime(ctx context.Context, r wazero.Runtime, arg string, config newConfig) (stdout, stderr string, err error) {
46
47 var guest wazero.CompiledModule
48 if guest, err = r.CompileModule(testCtx, testBin); err != nil {
49 log.Panicln(err)
50 }
51
52 if _, err = gojs.Instantiate(ctx, r, guest); err != nil {
53 return
54 }
55
56 var stdoutBuf, stderrBuf bytes.Buffer
57 mc, c := config(wazero.NewModuleConfig().
58 WithStdout(&stdoutBuf).
59 WithStderr(&stderrBuf).
60 WithArgs("test", arg))
61
62 ctx = experimental.WithCloseNotifier(ctx, experimental.CloseNotifyFunc(func(ctx context.Context, exitCode uint32) {
63 s := ctx.Value(internalgojs.StateKey{})
64 if want, have := internalgojs.NewState(c), s; !reflect.DeepEqual(want, have) {
65 log.Panicf("unexpected state: want %#v, have %#v", want, have)
66 }
67 }))
68 err = run.Run(ctx, r, guest, mc, c)
69 stdout = stdoutBuf.String()
70 stderr = stderrBuf.String()
71 return
72 }
73
74
75 var testBin []byte
76
77
78 var (
79 testCtx = context.Background()
80 testFS = fstest.FS
81 cache = wazero.NewCompilationCache()
82 )
83
84 func TestMain(m *testing.M) {
85
86 if o := runtime.GOOS; o != "darwin" && o != "linux" {
87 log.Println("gojs: skipping due to not yet supported OS:", o)
88 os.Exit(0)
89 }
90
91
92 goBin, err := findGoBin()
93 if err != nil {
94 log.Println("gojs: skipping due missing Go binary:", err)
95 os.Exit(0)
96 }
97 if err = compileJsWasm(goBin); err != nil {
98 log.Panicln(err)
99 }
100
101
102
103 compilationCacheDir, err := os.MkdirTemp("", "gojs")
104 if err != nil {
105 log.Panicln(err)
106 }
107 defer os.RemoveAll(compilationCacheDir)
108 cache, err := wazero.NewCompilationCacheWithDir(compilationCacheDir)
109 if err != nil {
110 log.Panicln(err)
111 }
112
113
114
115 r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithCompilationCache(cache))
116 _, err = r.CompileModule(testCtx, testBin)
117 if err != nil {
118 log.Panicln(err)
119 }
120
121 var exit int
122 defer func() {
123 cache.Close(testCtx)
124 r.Close(testCtx)
125 os.Exit(exit)
126 }()
127 exit = m.Run()
128 }
129
130
131
132
133
134 func compileJsWasm(goBin string) error {
135
136 workdir, err := os.MkdirTemp("", "example")
137 if err != nil {
138 return err
139 }
140 defer os.RemoveAll(workdir)
141
142 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
143 defer cancel()
144
145 bin := path.Join(workdir, "out.wasm")
146 cmd := exec.CommandContext(ctx, goBin, "build", "-o", bin, ".")
147 cmd.Env = append(os.Environ(), "GOOS=js", "GOARCH=wasm", "GOWASM=satconv,signext")
148 cmd.Dir = "testdata"
149 out, err := cmd.CombinedOutput()
150 if err != nil {
151 return fmt.Errorf("couldn't compile %s: %w", string(out), err)
152 }
153
154 testBin, err = os.ReadFile(bin)
155 return err
156 }
157
158 func findGoBin() (string, error) {
159 binName := "go"
160 if runtime.GOOS == "windows" {
161 binName += ".exe"
162 }
163 goBin := filepath.Join(runtime.GOROOT(), "bin", binName)
164 if _, err := os.Stat(goBin); err == nil {
165 return goBin, nil
166 }
167
168 return exec.LookPath(binName)
169 }
170
171
172 func logString(log bytes.Buffer) string {
173 return strings.ReplaceAll(log.String(), "==> gojs", "==> go")
174 }
175
View as plain text