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