github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/engine/wazevo/engine_cache_test.go (about) 1 package wazevo 2 3 import ( 4 "bytes" 5 "crypto/sha256" 6 "io" 7 "testing" 8 9 "github.com/bananabytelabs/wazero/internal/testing/require" 10 "github.com/bananabytelabs/wazero/internal/u32" 11 "github.com/bananabytelabs/wazero/internal/u64" 12 "github.com/bananabytelabs/wazero/internal/wasm" 13 ) 14 15 var testVersion = "0.0.1" 16 17 func TestSerializeCompiledModule(t *testing.T) { 18 tests := []struct { 19 in *compiledModule 20 exp []byte 21 }{ 22 { 23 in: &compiledModule{ 24 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 25 functionOffsets: []int{0}, 26 }, 27 exp: concat( 28 magic, 29 []byte{byte(len(testVersion))}, 30 []byte(testVersion), 31 u32.LeBytes(1), // number of functions. 32 u64.LeBytes(0), // offset. 33 u64.LeBytes(5), // length of code. 34 []byte{1, 2, 3, 4, 5}, // code. 35 []byte{0}, // no source map. 36 ), 37 }, 38 { 39 in: &compiledModule{ 40 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 41 functionOffsets: []int{0}, 42 }, 43 exp: concat( 44 magic, 45 []byte{byte(len(testVersion))}, 46 []byte(testVersion), 47 u32.LeBytes(1), // number of functions. 48 u64.LeBytes(0), // offset. 49 u64.LeBytes(5), // length of code. 50 []byte{1, 2, 3, 4, 5}, // code. 51 []byte{0}, // no source map. 52 ), 53 }, 54 { 55 in: &compiledModule{ 56 executables: &executables{executable: []byte{1, 2, 3, 4, 5, 1, 2, 3}}, 57 functionOffsets: []int{0, 5}, 58 }, 59 exp: concat( 60 magic, 61 []byte{byte(len(testVersion))}, 62 []byte(testVersion), 63 u32.LeBytes(2), // number of functions. 64 // Function index = 0. 65 u64.LeBytes(0), // offset. 66 // Function index = 1. 67 u64.LeBytes(5), // offset. 68 // Executable. 69 u64.LeBytes(8), // length of code. 70 []byte{1, 2, 3, 4, 5, 1, 2, 3}, // code. 71 []byte{0}, // no source map. 72 ), 73 }, 74 } 75 76 for i, tc := range tests { 77 actual, err := io.ReadAll(serializeCompiledModule(testVersion, tc.in)) 78 require.NoError(t, err, i) 79 require.Equal(t, tc.exp, actual, i) 80 } 81 } 82 83 func concat(ins ...[]byte) (ret []byte) { 84 for _, in := range ins { 85 ret = append(ret, in...) 86 } 87 return 88 } 89 90 func TestDeserializeCompiledModule(t *testing.T) { 91 tests := []struct { 92 name string 93 in []byte 94 importedFunctionCount uint32 95 expCompiledModule *compiledModule 96 expStaleCache bool 97 expErr string 98 }{ 99 { 100 name: "invalid header", 101 in: []byte{1}, 102 expErr: "compilationcache: invalid header length: 1", 103 }, 104 { 105 name: "invalid magic", 106 in: concat( 107 []byte{'a', 'b', 'c', 'd', 'e', 'f'}, 108 []byte{byte(len(testVersion))}, 109 []byte(testVersion), 110 u32.LeBytes(1), // number of functions. 111 ), 112 expErr: "compilationcache: invalid magic number: got WAZEVO but want abcdef", 113 }, 114 { 115 name: "version mismatch", 116 in: concat( 117 magic, 118 []byte{byte(len("1233123.1.1"))}, 119 []byte("1233123.1.1"), 120 u32.LeBytes(1), // number of functions. 121 ), 122 expStaleCache: true, 123 }, 124 { 125 name: "version mismatch", 126 in: concat( 127 magic, 128 []byte{byte(len("0.0.0"))}, 129 []byte("0.0.0"), 130 u32.LeBytes(1), // number of functions. 131 ), 132 expStaleCache: true, 133 }, 134 { 135 name: "one function", 136 in: concat( 137 magic, 138 []byte{byte(len(testVersion))}, 139 []byte(testVersion), 140 u32.LeBytes(1), // number of functions. 141 u64.LeBytes(0), // offset. 142 // Executable. 143 u64.LeBytes(5), // size. 144 []byte{1, 2, 3, 4, 5}, // machine code. 145 []byte{0}, // no source map. 146 ), 147 expCompiledModule: &compiledModule{ 148 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 149 functionOffsets: []int{0}, 150 }, 151 expStaleCache: false, 152 expErr: "", 153 }, 154 { 155 name: "two functions", 156 in: concat( 157 magic, 158 []byte{byte(len(testVersion))}, 159 []byte(testVersion), 160 u32.LeBytes(2), // number of functions. 161 // Function index = 0. 162 u64.LeBytes(0), // offset. 163 // Function index = 1. 164 u64.LeBytes(7), // offset. 165 // Executable. 166 u64.LeBytes(10), // size. 167 []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, // machine code. 168 []byte{0}, // no source map. 169 ), 170 importedFunctionCount: 1, 171 expCompiledModule: &compiledModule{ 172 executables: &executables{executable: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, 173 functionOffsets: []int{0, 7}, 174 }, 175 expStaleCache: false, 176 expErr: "", 177 }, 178 { 179 name: "reading executable offset", 180 in: concat( 181 []byte(magic), 182 []byte{byte(len(testVersion))}, 183 []byte(testVersion), 184 u32.LeBytes(2), // number of functions. 185 // Function index = 0. 186 u64.LeBytes(5), // offset. 187 // Function index = 1. 188 ), 189 expErr: "compilationcache: error reading func[1] executable offset: EOF", 190 }, 191 { 192 name: "mmapping", 193 in: concat( 194 []byte(magic), 195 []byte{byte(len(testVersion))}, 196 []byte(testVersion), 197 u32.LeBytes(2), // number of functions. 198 // Function index = 0. 199 u64.LeBytes(0), // offset. 200 // Function index = 1. 201 u64.LeBytes(5), // offset. 202 // Executable. 203 u64.LeBytes(5), // size of the executable. 204 // Lack of machine code here. 205 ), 206 expErr: "compilationcache: error reading executable (len=5): EOF", 207 }, 208 { 209 name: "no source map presence", 210 in: concat( 211 magic, 212 []byte{byte(len(testVersion))}, 213 []byte(testVersion), 214 u32.LeBytes(1), // number of functions. 215 u64.LeBytes(0), // offset. 216 // Executable. 217 u64.LeBytes(5), // size. 218 []byte{1, 2, 3, 4, 5}, // machine code. 219 ), 220 expCompiledModule: &compiledModule{ 221 executables: &executables{executable: []byte{1, 2, 3, 4, 5}}, 222 functionOffsets: []int{0}, 223 }, 224 expStaleCache: false, 225 expErr: "compilationcache: error reading source map presence: EOF", 226 }, 227 } 228 229 for _, tc := range tests { 230 tc := tc 231 t.Run(tc.name, func(t *testing.T) { 232 cm, staleCache, err := deserializeCompiledModule(testVersion, io.NopCloser(bytes.NewReader(tc.in))) 233 234 if tc.expErr != "" { 235 require.EqualError(t, err, tc.expErr) 236 } else { 237 require.NoError(t, err) 238 require.Equal(t, tc.expCompiledModule, cm) 239 } 240 241 require.Equal(t, tc.expStaleCache, staleCache) 242 }) 243 } 244 } 245 246 func Test_fileCacheKey(t *testing.T) { 247 s := sha256.New() 248 s.Write([]byte("hello world")) 249 m := &wasm.Module{} 250 s.Sum(m.ID[:0]) 251 original := m.ID 252 result := fileCacheKey(m) 253 require.Equal(t, original, m.ID) 254 require.NotEqual(t, original, result) 255 }