wal_paths_test.mx raw

   1  // The WAL branches the round-trip tests do not reach: Open's two path errors,
   2  // the periodic fsync, the early stop of ForEach/ForEachFrom, and the error
   3  // returns a closed segment produces. Rotation needs a 4GB segment
   4  // (MaxSegSize is 1<<32) and is the one branch left to the integration store.
   5  package wal
   6  
   7  import (
   8  	"bytes"
   9  	"os"
  10  	"testing"
  11  )
  12  
  13  func TestOpenFailsOnUnopenablePaths(t *testing.T) {
  14  	dir, ok := walTmp(t)
  15  	if !ok {
  16  		return
  17  	}
  18  	defer os.RemoveAll(dir)
  19  
  20  	// A regular file where the wal directory belongs.
  21  	if err := os.WriteFile(dir|"/wal", []byte("not a directory"), 0644); err != nil {
  22  		t.Fatal(err)
  23  	}
  24  	if w, oerr := Open(dir | "/wal"); oerr == nil {
  25  		w.Close()
  26  		t.Fatal("Open must fail when the wal path is a file")
  27  	}
  28  
  29  	// A segment that cannot be opened.
  30  	dir2, ok2 := walTmp(t)
  31  	if !ok2 {
  32  		return
  33  	}
  34  	defer os.RemoveAll(dir2)
  35  	if err := os.MkdirAll(dir2|"/seg-0000000000000000.dat", 0755); err != nil {
  36  		t.Fatal(err)
  37  	}
  38  	if w, oerr := Open(dir2); oerr == nil {
  39  		w.Close()
  40  		t.Fatal("Open must fail when a segment cannot be opened")
  41  	}
  42  }
  43  
  44  func TestAppendSyncsEveryNWrites(t *testing.T) {
  45  	dir, ok := walTmp(t)
  46  	if !ok {
  47  		return
  48  	}
  49  	defer os.RemoveAll(dir)
  50  
  51  	w, err := Open(dir)
  52  	if err != nil {
  53  		t.Fatal(err)
  54  		return
  55  	}
  56  	defer w.Close()
  57  
  58  	// Open sets syncEvery to 100; lowering it makes the periodic fsync branch
  59  	// reachable without 100 appends per case.
  60  	w.syncEvery = 2
  61  	names := [][]byte{[]byte("a"), []byte("b"), []byte("c"), []byte("d")}
  62  	var serials []uint64
  63  	for _, n := range names {
  64  		ser, aerr := w.Append(n)
  65  		if aerr != nil {
  66  			t.Fatal(aerr)
  67  			return
  68  		}
  69  		serials = push(serials, ser)
  70  	}
  71  	for i, ser := range serials {
  72  		got, rerr := w.Read(ser)
  73  		if rerr != nil {
  74  			t.Fatal(rerr)
  75  			return
  76  		}
  77  		if !bytes.Equal(got, names[i]) {
  78  			t.Fatalf("entry %d = %s", i, string(got))
  79  		}
  80  	}
  81  }
  82  
  83  func TestForEachStopsOnFalse(t *testing.T) {
  84  	dir, ok := walTmp(t)
  85  	if !ok {
  86  		return
  87  	}
  88  	defer os.RemoveAll(dir)
  89  
  90  	w, err := Open(dir)
  91  	if err != nil {
  92  		t.Fatal(err)
  93  		return
  94  	}
  95  	defer w.Close()
  96  	for _, n := range [][]byte{[]byte("one"), []byte("two"), []byte("three")} {
  97  		if _, aerr := w.Append(n); aerr != nil {
  98  			t.Fatal(aerr)
  99  			return
 100  		}
 101  	}
 102  
 103  	// A callback that refuses the first entry ends the walk after one call.
 104  	calls := int32(0)
 105  	stop := func(ser uint64, data []byte) bool {
 106  		calls++
 107  		return false
 108  	}
 109  	if ferr := w.ForEach(stop); ferr != nil {
 110  		t.Fatal(ferr)
 111  		return
 112  	}
 113  	if calls != 1 {
 114  		t.Fatalf("ForEach called the callback %d times, want 1", calls)
 115  	}
 116  
 117  	// ForEachFrom stops the same way, from the entry after the start serial.
 118  	calls2 := int32(0)
 119  	stop2 := func(ser uint64, data []byte) bool {
 120  		calls2++
 121  		return false
 122  	}
 123  	fourth, aerr3 := w.Append([]byte("four"))
 124  	if aerr3 != nil {
 125  		t.Fatal(aerr3)
 126  		return
 127  	}
 128  	if _, aerr4 := w.Append([]byte("five")); aerr4 != nil {
 129  		t.Fatal(aerr4)
 130  		return
 131  	}
 132  	if ferr3 := w.ForEachFrom(fourth, stop2); ferr3 != nil {
 133  		t.Fatal(ferr3)
 134  		return
 135  	}
 136  	if calls2 != 1 {
 137  		t.Fatalf("ForEachFrom called the callback %d times, want 1", calls2)
 138  	}
 139  }
 140