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