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  }