wa-lang.org/wazero@v1.0.2/imports/emscripten/emscripten_test.go (about) 1 package emscripten 2 3 import ( 4 "bytes" 5 "context" 6 _ "embed" 7 "testing" 8 9 "wa-lang.org/wazero" 10 . "wa-lang.org/wazero/experimental" 11 "wa-lang.org/wazero/experimental/logging" 12 "wa-lang.org/wazero/imports/wasi_snapshot_preview1" 13 "wa-lang.org/wazero/internal/testing/require" 14 "wa-lang.org/wazero/sys" 15 ) 16 17 // growWasm was compiled from testdata/grow.cc 18 // 19 //go:embed testdata/grow.wasm 20 var growWasm []byte 21 22 // invokeWasm was generated by the following: 23 // 24 // cd testdata; wat2wasm --debug-names invoke.wat 25 // 26 //go:embed testdata/invoke.wasm 27 var invokeWasm []byte 28 29 // testCtx is an arbitrary, non-default context. Non-nil also prevents linter errors. 30 var testCtx = context.WithValue(context.Background(), struct{}{}, "arbitrary") 31 32 // TestGrow is an integration test until we have an Emscripten example. 33 func TestGrow(t *testing.T) { 34 var log bytes.Buffer 35 36 // Set context to one that has an experimental listener 37 ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&log)) 38 39 r := wazero.NewRuntime(ctx) 40 defer r.Close(ctx) 41 42 wasi_snapshot_preview1.MustInstantiate(ctx, r) 43 44 _, err := Instantiate(ctx, r) 45 require.NoError(t, err) 46 47 // Emscripten exits main with zero by default 48 _, err = r.InstantiateModuleFromBinary(ctx, growWasm) 49 require.Error(t, err) 50 require.Zero(t, err.(*sys.ExitError).ExitCode()) 51 52 // We expect the memory no-op memory growth hook to be invoked as wasm. 53 require.Contains(t, log.String(), "--> env.emscripten_notify_memory_growth(memory_index=0)") 54 } 55 56 func TestInvoke(t *testing.T) { 57 var log bytes.Buffer 58 59 // Set context to one that has an experimental listener 60 ctx := context.WithValue(testCtx, FunctionListenerFactoryKey{}, logging.NewLoggingListenerFactory(&log)) 61 62 r := wazero.NewRuntime(ctx) 63 defer r.Close(ctx) 64 65 _, err := Instantiate(ctx, r) 66 require.NoError(t, err) 67 68 mod, err := r.InstantiateModuleFromBinary(ctx, invokeWasm) 69 require.NoError(t, err) 70 71 tests := []struct { 72 name, funcName string 73 tableOffset int 74 params, expectedResults []uint64 75 expectedLog string 76 }{ 77 { 78 name: "invoke_i", 79 funcName: "call_v_i32", 80 expectedResults: []uint64{42}, 81 expectedLog: `--> .call_v_i32(0) 82 ==> env.invoke_i(index=0) 83 --> .v_i32() 84 <-- (42) 85 <== (42) 86 <-- (42) 87 `, 88 }, 89 { 90 name: "invoke_ii", 91 funcName: "call_i32_i32", 92 tableOffset: 2, 93 params: []uint64{42}, 94 expectedResults: []uint64{42}, 95 expectedLog: `--> .call_i32_i32(2,42) 96 ==> env.invoke_ii(index=2,a1=42) 97 --> .i32_i32(42) 98 <-- (42) 99 <== (42) 100 <-- (42) 101 `, 102 }, 103 { 104 name: "invoke_iii", 105 funcName: "call_i32i32_i32", 106 tableOffset: 4, 107 params: []uint64{1, 2}, 108 expectedResults: []uint64{3}, 109 expectedLog: `--> .call_i32i32_i32(4,1,2) 110 ==> env.invoke_iii(index=4,a1=1,a2=2) 111 --> .i32i32_i32(1,2) 112 <-- (3) 113 <== (3) 114 <-- (3) 115 `, 116 }, 117 { 118 name: "invoke_iiii", 119 funcName: "call_i32i32i32_i32", 120 tableOffset: 6, 121 params: []uint64{1, 2, 4}, 122 expectedResults: []uint64{7}, 123 expectedLog: `--> .call_i32i32i32_i32(6,1,2,4) 124 ==> env.invoke_iiii(index=6,a1=1,a2=2,a3=4) 125 --> .i32i32i32_i32(1,2,4) 126 <-- (7) 127 <== (7) 128 <-- (7) 129 `, 130 }, 131 { 132 name: "invoke_iiiii", 133 funcName: "calli32_i32i32i32i32_i32", 134 tableOffset: 8, 135 params: []uint64{1, 2, 4, 8}, 136 expectedResults: []uint64{15}, 137 expectedLog: `--> .calli32_i32i32i32i32_i32(8,1,2,4,8) 138 ==> env.invoke_iiiii(index=8,a1=1,a2=2,a3=4,a4=8) 139 --> .i32i32i32i32_i32(1,2,4,8) 140 <-- (15) 141 <== (15) 142 <-- (15) 143 `, 144 }, 145 { 146 name: "invoke_v", 147 funcName: "call_v_v", 148 tableOffset: 10, 149 expectedLog: `--> .call_v_v(10) 150 ==> env.invoke_v(index=10) 151 --> .v_v() 152 <-- () 153 <== () 154 <-- () 155 `, 156 }, 157 { 158 name: "invoke_vi", 159 funcName: "call_i32_v", 160 tableOffset: 12, 161 params: []uint64{42}, 162 expectedLog: `--> .call_i32_v(12,42) 163 ==> env.invoke_vi(index=12,a1=42) 164 --> .i32_v(42) 165 <-- () 166 <== () 167 <-- () 168 `, 169 }, 170 { 171 name: "invoke_vii", 172 funcName: "call_i32i32_v", 173 tableOffset: 14, 174 params: []uint64{1, 2}, 175 expectedLog: `--> .call_i32i32_v(14,1,2) 176 ==> env.invoke_vii(index=14,a1=1,a2=2) 177 --> .i32i32_v(1,2) 178 <-- () 179 <== () 180 <-- () 181 `, 182 }, 183 { 184 name: "invoke_viii", 185 funcName: "call_i32i32i32_v", 186 tableOffset: 16, 187 params: []uint64{1, 2, 4}, 188 expectedLog: `--> .call_i32i32i32_v(16,1,2,4) 189 ==> env.invoke_viii(index=16,a1=1,a2=2,a3=4) 190 --> .i32i32i32_v(1,2,4) 191 <-- () 192 <== () 193 <-- () 194 `, 195 }, 196 { 197 name: "invoke_viiii", 198 funcName: "calli32_i32i32i32i32_v", 199 tableOffset: 18, 200 params: []uint64{1, 2, 4, 8}, 201 expectedLog: `--> .calli32_i32i32i32i32_v(18,1,2,4,8) 202 ==> env.invoke_viiii(index=18,a1=1,a2=2,a3=4,a4=8) 203 --> .i32i32i32i32_v(1,2,4,8) 204 <-- () 205 <== () 206 <-- () 207 `, 208 }, 209 } 210 211 for _, tt := range tests { 212 tc := tt 213 214 t.Run(tc.name, func(t *testing.T) { 215 defer log.Reset() 216 217 params := tc.params 218 params = append([]uint64{uint64(tc.tableOffset)}, params...) 219 220 results, err := mod.ExportedFunction(tc.funcName).Call(testCtx, params...) 221 require.NoError(t, err) 222 require.Equal(t, tc.expectedResults, results) 223 224 // We expect to see the dynamic function call target 225 require.Equal(t, log.String(), tc.expectedLog) 226 227 // We expect an unreachable function to err 228 params[0]++ 229 _, err = mod.ExportedFunction(tc.funcName).Call(testCtx, params...) 230 require.Error(t, err) 231 }) 232 } 233 }