github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vfs/tests/mptest/mptest_test.go (about) 1 package mptest 2 3 import ( 4 "bytes" 5 "compress/bzip2" 6 "context" 7 "crypto/rand" 8 "embed" 9 "io" 10 "io/fs" 11 "os" 12 "path/filepath" 13 "runtime" 14 "strconv" 15 "strings" 16 "sync" 17 "sync/atomic" 18 "testing" 19 20 "github.com/ncruces/go-sqlite3/internal/util" 21 "github.com/ncruces/go-sqlite3/vfs" 22 _ "github.com/ncruces/go-sqlite3/vfs/adiantum" 23 "github.com/ncruces/go-sqlite3/vfs/memdb" 24 "github.com/tetratelabs/wazero" 25 "github.com/tetratelabs/wazero/api" 26 "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" 27 ) 28 29 //go:embed testdata/mptest.wasm.bz2 30 var compressed string 31 32 //go:embed testdata/*.*test 33 var scripts embed.FS 34 35 var ( 36 rt wazero.Runtime 37 module wazero.CompiledModule 38 instances atomic.Uint64 39 ) 40 41 func TestMain(m *testing.M) { 42 ctx := context.Background() 43 cfg := wazero.NewRuntimeConfig().WithMemoryLimitPages(1024) 44 rt = wazero.NewRuntimeWithConfig(ctx, cfg) 45 wasi_snapshot_preview1.MustInstantiate(ctx, rt) 46 47 env := vfs.ExportHostFunctions(rt.NewHostModuleBuilder("env")) 48 env.NewFunctionBuilder().WithFunc(system).Export("system") 49 _, err := env.Instantiate(ctx) 50 if err != nil { 51 panic(err) 52 } 53 54 if !strings.HasPrefix(compressed, "BZh") { 55 panic("Please use Git LFS to clone this repo: https://git-lfs.com/") 56 } 57 binary, err := io.ReadAll(bzip2.NewReader(strings.NewReader(compressed))) 58 if err != nil { 59 panic(err) 60 } 61 62 module, err = rt.CompileModule(ctx, binary) 63 if err != nil { 64 panic(err) 65 } 66 67 os.Exit(m.Run()) 68 } 69 70 func config(ctx context.Context) wazero.ModuleConfig { 71 name := strconv.FormatUint(instances.Add(1), 10) 72 log := ctx.Value(logger{}).(io.Writer) 73 fs, err := fs.Sub(scripts, "testdata") 74 if err != nil { 75 panic(err) 76 } 77 78 return wazero.NewModuleConfig(). 79 WithName(name).WithStdout(log).WithStderr(log).WithFS(fs). 80 WithSysWalltime().WithSysNanotime().WithSysNanosleep(). 81 WithOsyield(runtime.Gosched). 82 WithRandSource(rand.Reader) 83 } 84 85 func system(ctx context.Context, mod api.Module, ptr uint32) uint32 { 86 buf, _ := mod.Memory().Read(ptr, mod.Memory().Size()-ptr) 87 buf = buf[:bytes.IndexByte(buf, 0)] 88 89 args := strings.Split(string(buf), " ") 90 for i := range args { 91 args[i] = strings.Trim(args[i], `"`) 92 } 93 args = args[:len(args)-1] 94 95 cfg := config(ctx).WithArgs(args...) 96 go func() { 97 ctx := util.NewContext(ctx) 98 mod, _ := rt.InstantiateModule(ctx, module, cfg) 99 mod.Close(ctx) 100 }() 101 return 0 102 } 103 104 func Test_config01(t *testing.T) { 105 if !vfs.SupportsFileLocking { 106 t.Skip("skipping without locks") 107 } 108 109 ctx := util.NewContext(newContext(t)) 110 name := filepath.Join(t.TempDir(), "test.db") 111 cfg := config(ctx).WithArgs("mptest", name, "config01.test") 112 mod, err := rt.InstantiateModule(ctx, module, cfg) 113 if err != nil { 114 t.Fatal(err) 115 } 116 mod.Close(ctx) 117 } 118 119 func Test_config02(t *testing.T) { 120 if testing.Short() { 121 t.Skip("skipping in short mode") 122 } 123 if os.Getenv("CI") != "" { 124 t.Skip("skipping in CI") 125 } 126 if !vfs.SupportsFileLocking { 127 t.Skip("skipping without locks") 128 } 129 130 ctx := util.NewContext(newContext(t)) 131 name := filepath.Join(t.TempDir(), "test.db") 132 cfg := config(ctx).WithArgs("mptest", name, "config02.test") 133 mod, err := rt.InstantiateModule(ctx, module, cfg) 134 if err != nil { 135 t.Fatal(err) 136 } 137 mod.Close(ctx) 138 } 139 140 func Test_crash01(t *testing.T) { 141 if testing.Short() { 142 t.Skip("skipping in short mode") 143 } 144 if os.Getenv("CI") != "" { 145 t.Skip("skipping in CI") 146 } 147 if !vfs.SupportsFileLocking { 148 t.Skip("skipping without locks") 149 } 150 151 ctx := util.NewContext(newContext(t)) 152 name := filepath.Join(t.TempDir(), "test.db") 153 cfg := config(ctx).WithArgs("mptest", name, "crash01.test") 154 mod, err := rt.InstantiateModule(ctx, module, cfg) 155 if err != nil { 156 t.Fatal(err) 157 } 158 mod.Close(ctx) 159 } 160 161 func Test_multiwrite01(t *testing.T) { 162 if testing.Short() { 163 t.Skip("skipping in short mode") 164 } 165 if !vfs.SupportsFileLocking { 166 t.Skip("skipping without locks") 167 } 168 169 ctx := util.NewContext(newContext(t)) 170 name := filepath.Join(t.TempDir(), "test.db") 171 cfg := config(ctx).WithArgs("mptest", name, "multiwrite01.test") 172 mod, err := rt.InstantiateModule(ctx, module, cfg) 173 if err != nil { 174 t.Fatal(err) 175 } 176 mod.Close(ctx) 177 } 178 179 func Test_config01_memory(t *testing.T) { 180 memdb.Delete("test.db") 181 ctx := util.NewContext(newContext(t)) 182 cfg := config(ctx).WithArgs("mptest", "/test.db", "config01.test", 183 "--vfs", "memdb") 184 mod, err := rt.InstantiateModule(ctx, module, cfg) 185 if err != nil { 186 t.Fatal(err) 187 } 188 mod.Close(ctx) 189 } 190 191 func Test_multiwrite01_memory(t *testing.T) { 192 if testing.Short() { 193 t.Skip("skipping in short mode") 194 } 195 196 memdb.Delete("test.db") 197 ctx := util.NewContext(newContext(t)) 198 cfg := config(ctx).WithArgs("mptest", "/test.db", "multiwrite01.test", 199 "--vfs", "memdb") 200 mod, err := rt.InstantiateModule(ctx, module, cfg) 201 if err != nil { 202 t.Fatal(err) 203 } 204 mod.Close(ctx) 205 } 206 207 func Test_crash01_wal(t *testing.T) { 208 if testing.Short() { 209 t.Skip("skipping in short mode") 210 } 211 if os.Getenv("CI") != "" { 212 t.Skip("skipping in CI") 213 } 214 if !vfs.SupportsSharedMemory { 215 t.Skip("skipping without shared memory") 216 } 217 218 ctx := util.NewContext(newContext(t)) 219 name := filepath.Join(t.TempDir(), "test.db") 220 cfg := config(ctx).WithArgs("mptest", name, "crash01.test", 221 "--journalmode", "wal") 222 mod, err := rt.InstantiateModule(ctx, module, cfg) 223 if err != nil { 224 t.Fatal(err) 225 } 226 mod.Close(ctx) 227 } 228 229 func Test_multiwrite01_wal(t *testing.T) { 230 if testing.Short() { 231 t.Skip("skipping in short mode") 232 } 233 if !vfs.SupportsSharedMemory { 234 t.Skip("skipping without shared memory") 235 } 236 237 ctx := util.NewContext(newContext(t)) 238 name := filepath.Join(t.TempDir(), "test.db") 239 cfg := config(ctx).WithArgs("mptest", name, "multiwrite01.test", 240 "--journalmode", "wal") 241 mod, err := rt.InstantiateModule(ctx, module, cfg) 242 if err != nil { 243 t.Fatal(err) 244 } 245 mod.Close(ctx) 246 } 247 248 func Test_crash01_adiantum(t *testing.T) { 249 if testing.Short() { 250 t.Skip("skipping in short mode") 251 } 252 if os.Getenv("CI") != "" { 253 t.Skip("skipping in CI") 254 } 255 if !vfs.SupportsFileLocking { 256 t.Skip("skipping without locks") 257 } 258 259 ctx := util.NewContext(newContext(t)) 260 name := "file:" + filepath.Join(t.TempDir(), "test.db") + 261 "?hexkey=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" 262 cfg := config(ctx).WithArgs("mptest", name, "crash01.test", 263 "--vfs", "adiantum") 264 mod, err := rt.InstantiateModule(ctx, module, cfg) 265 if err != nil { 266 t.Fatal(err) 267 } 268 mod.Close(ctx) 269 } 270 271 func Test_crash01_adiantum_wal(t *testing.T) { 272 if testing.Short() { 273 t.Skip("skipping in short mode") 274 } 275 if os.Getenv("CI") != "" { 276 t.Skip("skipping in CI") 277 } 278 if !vfs.SupportsSharedMemory { 279 t.Skip("skipping without shared memory") 280 } 281 282 ctx := util.NewContext(newContext(t)) 283 name := "file:" + filepath.Join(t.TempDir(), "test.db") + 284 "?hexkey=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" 285 cfg := config(ctx).WithArgs("mptest", name, "crash01.test", 286 "--vfs", "adiantum", "--journalmode", "wal") 287 mod, err := rt.InstantiateModule(ctx, module, cfg) 288 if err != nil { 289 t.Fatal(err) 290 } 291 mod.Close(ctx) 292 } 293 294 func newContext(t *testing.T) context.Context { 295 return context.WithValue(context.Background(), logger{}, &testWriter{T: t}) 296 } 297 298 type logger struct{} 299 300 type testWriter struct { 301 // +checklocks:mtx 302 *testing.T 303 // +checklocks:mtx 304 buf []byte 305 mtx sync.Mutex 306 } 307 308 func (l *testWriter) Write(p []byte) (n int, err error) { 309 l.mtx.Lock() 310 defer l.mtx.Unlock() 311 312 l.buf = append(l.buf, p...) 313 for { 314 before, after, found := bytes.Cut(l.buf, []byte("\n")) 315 if !found { 316 return len(p), nil 317 } 318 l.Logf("%s", before) 319 l.buf = after 320 } 321 }