1 package filecache
2
3 import (
4 "bytes"
5 "context"
6 "fmt"
7 "os"
8 "os/exec"
9 "runtime"
10 "strings"
11 "testing"
12 "time"
13
14 "github.com/tetratelabs/wazero"
15 "github.com/tetratelabs/wazero/api"
16 "github.com/tetratelabs/wazero/experimental"
17 "github.com/tetratelabs/wazero/experimental/logging"
18 "github.com/tetratelabs/wazero/experimental/opt"
19 "github.com/tetratelabs/wazero/internal/integration_test/spectest"
20 v1 "github.com/tetratelabs/wazero/internal/integration_test/spectest/v1"
21 "github.com/tetratelabs/wazero/internal/platform"
22 "github.com/tetratelabs/wazero/internal/testing/binaryencoding"
23 "github.com/tetratelabs/wazero/internal/testing/require"
24 "github.com/tetratelabs/wazero/internal/wasm"
25 )
26
27 func TestFileCacheSpecTest_compiler(t *testing.T) {
28 if !platform.CompilerSupported() {
29 return
30 }
31 runAllFileCacheTests(t, wazero.NewRuntimeConfigCompiler())
32 }
33
34 func TestFileCacheSpecTest_wazevo(t *testing.T) {
35 if runtime.GOARCH != "arm64" {
36 return
37 }
38 config := opt.NewRuntimeConfigOptimizingCompiler()
39 runAllFileCacheTests(t, config)
40 }
41
42 func runAllFileCacheTests(t *testing.T, config wazero.RuntimeConfig) {
43 t.Run("spectest", func(t *testing.T) {
44 testSpecTestCompilerCache(t, config)
45 })
46 t.Run("listeners", func(t *testing.T) {
47 testListeners(t, config)
48 })
49 t.Run("close on context done", func(t *testing.T) {
50 testWithCloseOnContextDone(t, config)
51 })
52 }
53
54 func testSpecTestCompilerCache(t *testing.T, config wazero.RuntimeConfig) {
55 const cachePathKey = "FILE_CACHE_DIR"
56 cacheDir := os.Getenv(cachePathKey)
57 if len(cacheDir) == 0 {
58
59 cacheDir = t.TempDir()
60
61
62 files, err := os.ReadDir(cacheDir)
63 require.NoError(t, err)
64 require.True(t, len(files) == 0)
65
66
67 testExecutable, err := os.Executable()
68 require.NoError(t, err)
69
70
71
72 var exp []string
73 buf := bytes.NewBuffer(nil)
74 for i := 0; i < 2; i++ {
75 cmd := exec.Command(testExecutable)
76 cmd.Args = append(cmd.Args, fmt.Sprintf("-test.run=%s", t.Name()))
77 cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", cachePathKey, cacheDir))
78 cmd.Stdout = buf
79 cmd.Stderr = buf
80 err = cmd.Run()
81 require.NoError(t, err, buf.String())
82 exp = append(exp, "PASS\n")
83 }
84
85
86 require.Equal(t, strings.Join(exp, ""), buf.String())
87
88
89 files, err = os.ReadDir(cacheDir)
90 require.NoError(t, err)
91 require.True(t, len(files) > 0)
92 } else {
93
94 cc, err := wazero.NewCompilationCacheWithDir(cacheDir)
95 require.NoError(t, err)
96 spectest.Run(t, v1.Testcases, context.Background(),
97 config.WithCompilationCache(cc).WithCoreFeatures(api.CoreFeaturesV1))
98 }
99 }
100
101
102 func testListeners(t *testing.T, config wazero.RuntimeConfig) {
103 if !platform.CompilerSupported() {
104 t.Skip()
105 }
106
107 var (
108 zero uint32 = 0
109 wasmBin = binaryencoding.EncodeModule(&wasm.Module{
110 TypeSection: []wasm.FunctionType{{}},
111 FunctionSection: []wasm.Index{0, 0, 0, 0},
112 CodeSection: []wasm.Code{
113 {Body: []byte{wasm.OpcodeCall, 1, wasm.OpcodeEnd}},
114 {Body: []byte{wasm.OpcodeCall, 2, wasm.OpcodeEnd}},
115 {Body: []byte{wasm.OpcodeCall, 3, wasm.OpcodeEnd}},
116 {Body: []byte{wasm.OpcodeEnd}},
117 },
118 StartSection: &zero,
119 NameSection: &wasm.NameSection{
120 FunctionNames: wasm.NameMap{{Index: 0, Name: "1"}, {Index: 1, Name: "2"}, {Index: 2, Name: "3"}, {Index: 3, Name: "4"}},
121 ModuleName: "test",
122 },
123 })
124 )
125
126 t.Run("always on", func(t *testing.T) {
127 dir := t.TempDir()
128
129 out := bytes.NewBuffer(nil)
130 ctxWithListener := context.WithValue(context.Background(),
131 experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(out))
132
133 {
134 cc, err := wazero.NewCompilationCacheWithDir(dir)
135 require.NoError(t, err)
136 rc := config.WithCompilationCache(cc)
137
138 r := wazero.NewRuntimeWithConfig(ctxWithListener, rc)
139 _, err = r.CompileModule(ctxWithListener, wasmBin)
140 require.NoError(t, err)
141 err = r.Close(ctxWithListener)
142 require.NoError(t, err)
143 }
144
145 cc, err := wazero.NewCompilationCacheWithDir(dir)
146 require.NoError(t, err)
147 rc := config.WithCompilationCache(cc)
148 r := wazero.NewRuntimeWithConfig(ctxWithListener, rc)
149 _, err = r.Instantiate(ctxWithListener, wasmBin)
150 require.NoError(t, err)
151 err = r.Close(ctxWithListener)
152 require.NoError(t, err)
153
154
155 require.Equal(t, `--> test.1()
156 --> test.2()
157 --> test.3()
158 --> test.4()
159 <--
160 <--
161 <--
162 <--
163 `, out.String())
164 })
165
166 t.Run("with->without", func(t *testing.T) {
167 dir := t.TempDir()
168
169
170 {
171 cc, err := wazero.NewCompilationCacheWithDir(dir)
172 require.NoError(t, err)
173 rc := config.WithCompilationCache(cc)
174
175 out := bytes.NewBuffer(nil)
176 ctxWithListener := context.WithValue(context.Background(),
177 experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(out))
178 r := wazero.NewRuntimeWithConfig(ctxWithListener, rc)
179 _, err = r.CompileModule(ctxWithListener, wasmBin)
180 require.NoError(t, err)
181 err = r.Close(ctxWithListener)
182 require.NoError(t, err)
183 }
184
185
186 cc, err := wazero.NewCompilationCacheWithDir(dir)
187 require.NoError(t, err)
188 rc := config.WithCompilationCache(cc)
189 r := wazero.NewRuntimeWithConfig(context.Background(), rc)
190 _, err = r.Instantiate(context.Background(), wasmBin)
191 require.NoError(t, err)
192 err = r.Close(context.Background())
193 require.NoError(t, err)
194 })
195
196 t.Run("without->with", func(t *testing.T) {
197 dir := t.TempDir()
198
199
200 {
201 cc, err := wazero.NewCompilationCacheWithDir(dir)
202 require.NoError(t, err)
203 rc := config.WithCompilationCache(cc)
204 r := wazero.NewRuntimeWithConfig(context.Background(), rc)
205 _, err = r.CompileModule(context.Background(), wasmBin)
206 require.NoError(t, err)
207 err = r.Close(context.Background())
208 require.NoError(t, err)
209 }
210
211
212 out := bytes.NewBuffer(nil)
213 ctxWithListener := context.WithValue(context.Background(),
214 experimental.FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(out))
215
216 cc, err := wazero.NewCompilationCacheWithDir(dir)
217 require.NoError(t, err)
218 rc := config.WithCompilationCache(cc)
219 r := wazero.NewRuntimeWithConfig(ctxWithListener, rc)
220 _, err = r.Instantiate(ctxWithListener, wasmBin)
221 require.NoError(t, err)
222 err = r.Close(ctxWithListener)
223 require.NoError(t, err)
224
225
226 require.Equal(t, `--> test.1()
227 --> test.2()
228 --> test.3()
229 --> test.4()
230 <--
231 <--
232 <--
233 <--
234 `, out.String())
235 })
236 }
237
238
239 func testWithCloseOnContextDone(t *testing.T, config wazero.RuntimeConfig) {
240 var (
241 zero uint32 = 0
242 wasmBin = binaryencoding.EncodeModule(&wasm.Module{
243 TypeSection: []wasm.FunctionType{{}},
244 FunctionSection: []wasm.Index{0},
245 CodeSection: []wasm.Code{
246 {Body: []byte{
247 wasm.OpcodeLoop, 0,
248 wasm.OpcodeBr, 0,
249 wasm.OpcodeEnd,
250 wasm.OpcodeEnd,
251 }},
252 },
253 StartSection: &zero,
254 })
255 )
256
257 t.Run("always on", func(t *testing.T) {
258 dir := t.TempDir()
259 ctx := context.Background()
260 {
261 cc, err := wazero.NewCompilationCacheWithDir(dir)
262 require.NoError(t, err)
263 rc := config.WithCompilationCache(cc).WithCloseOnContextDone(true)
264
265 r := wazero.NewRuntimeWithConfig(ctx, rc)
266 _, err = r.CompileModule(ctx, wasmBin)
267 require.NoError(t, err)
268 err = r.Close(ctx)
269 require.NoError(t, err)
270 }
271
272 cc, err := wazero.NewCompilationCacheWithDir(dir)
273 require.NoError(t, err)
274 rc := config.WithCompilationCache(cc).WithCloseOnContextDone(true)
275 r := wazero.NewRuntimeWithConfig(ctx, rc)
276
277 timeoutCtx, done := context.WithTimeout(ctx, time.Second)
278 defer done()
279 _, err = r.Instantiate(timeoutCtx, wasmBin)
280 require.EqualError(t, err, "module closed with context deadline exceeded")
281 err = r.Close(ctx)
282 require.NoError(t, err)
283 })
284
285 t.Run("off->on", func(t *testing.T) {
286 dir := t.TempDir()
287 ctx := context.Background()
288 {
289 cc, err := wazero.NewCompilationCacheWithDir(dir)
290 require.NoError(t, err)
291 rc := config.WithCompilationCache(cc).WithCloseOnContextDone(false)
292
293 r := wazero.NewRuntimeWithConfig(ctx, rc)
294 _, err = r.CompileModule(ctx, wasmBin)
295 require.NoError(t, err)
296 err = r.Close(ctx)
297 require.NoError(t, err)
298 }
299
300 cc, err := wazero.NewCompilationCacheWithDir(dir)
301 require.NoError(t, err)
302 rc := config.WithCompilationCache(cc).WithCloseOnContextDone(true)
303 r := wazero.NewRuntimeWithConfig(ctx, rc)
304
305 timeoutCtx, done := context.WithTimeout(ctx, time.Second)
306 defer done()
307 _, err = r.Instantiate(timeoutCtx, wasmBin)
308 require.EqualError(t, err, "module closed with context deadline exceeded")
309 err = r.Close(ctx)
310 require.NoError(t, err)
311 })
312 }
313
View as plain text