sorted_paths_test.mx raw
1 // The sorted-file paths the mixed-source tests do not reach: the .buf sidecar
2 // branch of GetPrefix, Last across all three sources, Open's two file errors,
3 // refreshSidecar on a handle without a sidecar, and Flush when there is nothing
4 // to merge.
5 package sorted
6
7 import (
8 "os"
9 "testing"
10 )
11
12 func TestGetPrefixFromSidecar(t *testing.T) {
13 dir, ok := srTmp(t)
14 if !ok {
15 return
16 }
17 defer os.RemoveAll(dir)
18
19 f, ok2 := srOpen(t, dir|"/idx.dat")
20 if !ok2 {
21 return
22 }
23 defer f.Close()
24
25 // Only the sidecar carries the record: QuickFlush moves the in-memory
26 // buffer into .buf and leaves the main file empty.
27 f.Put(srKey(1, 1, 0, 0, 0x0A))
28 if _, qerr := f.QuickFlush(1, false); qerr != nil {
29 t.Fatal(qerr)
30 return
31 }
32 rec, found := f.GetPrefix([]byte{1, 1})
33 if !found {
34 t.Fatal("GetPrefix must find a record in the .buf sidecar")
35 }
36 if rec[0] != 1 || rec[1] != 1 {
37 t.Fatal("GetPrefix returned a non-matching sidecar record")
38 }
39 if _, found2 := f.GetPrefix([]byte{2}); found2 {
40 t.Fatal("GetPrefix of an absent prefix")
41 }
42 }
43
44 func TestGetPrefixEmptyFile(t *testing.T) {
45 dir, ok := srTmp(t)
46 if !ok {
47 return
48 }
49 defer os.RemoveAll(dir)
50
51 f, ok2 := srOpen(t, dir|"/idx.dat")
52 if !ok2 {
53 return
54 }
55 defer f.Close()
56
57 if rec, found := f.GetPrefix([]byte{1}); found || rec != nil {
58 t.Fatal("GetPrefix on an empty file must report no match")
59 }
60 if rec, found := f.Last(); found || rec != nil {
61 t.Fatal("Last on an empty file must report no record")
62 }
63 }
64
65 func TestLastAcrossSources(t *testing.T) {
66 dir, ok := srTmp(t)
67 if !ok {
68 return
69 }
70 defer os.RemoveAll(dir)
71
72 f, ok2 := srOpen(t, dir|"/idx.dat")
73 if !ok2 {
74 return
75 }
76 defer f.Close()
77
78 // Sidecar only.
79 f.Put(srKey(5, 0, 0, 0, 0x05))
80 if _, qerr := f.QuickFlush(1, false); qerr != nil {
81 t.Fatal(qerr)
82 return
83 }
84 rec, found := f.Last()
85 if !found || rec[0] != 5 {
86 t.Fatalf("Last from the sidecar = %v", rec)
87 }
88
89 // Main file and sidecar: the file holds the larger key.
90 f.Put(srKey(9, 0, 0, 0, 0x09))
91 if err := f.Flush(); err != nil {
92 t.Fatal(err)
93 return
94 }
95 f.Put(srKey(3, 0, 0, 0, 0x03))
96 if _, qerr := f.QuickFlush(2, false); qerr != nil {
97 t.Fatal(qerr)
98 return
99 }
100 rec2, found2 := f.Last()
101 if !found2 || rec2[0] != 9 {
102 t.Fatalf("Last with the largest key in the main file = %v", rec2)
103 }
104
105 // The memory buffer holds the largest key.
106 f.Put(srKey(11, 0, 0, 0, 0x0B))
107 rec3, found3 := f.Last()
108 if !found3 || rec3[0] != 11 {
109 t.Fatalf("Last with the largest key in the memory buffer = %v", rec3)
110 }
111 }
112
113 func TestOpenRejectsUnopenablePaths(t *testing.T) {
114 dir, ok := srTmp(t)
115 if !ok {
116 return
117 }
118 defer os.RemoveAll(dir)
119
120 // The index path is a directory.
121 if err := os.MkdirAll(dir|"/idx.dat", 0755); err != nil {
122 t.Fatal(err)
123 }
124 if f, oerr := Open(dir|"/idx.dat", 4, 4); oerr == nil {
125 f.Close()
126 t.Fatal("Open must fail when the index path is a directory")
127 }
128
129 // The sidecar path is a directory.
130 if err := os.MkdirAll(dir|"/side.dat.buf", 0755); err != nil {
131 t.Fatal(err)
132 }
133 if f, oerr := Open(dir|"/side.dat", 4, 4); oerr == nil {
134 f.Close()
135 t.Fatal("Open must fail when the sidecar path is a directory")
136 }
137 }
138
139 func TestRefreshSidecarWithoutOne(t *testing.T) {
140 // A File built without Open has no sidecar: refreshSidecar and readBufFile
141 // must be no-ops rather than dereferencing nil.
142 f := &File{recLen: 4, cmpLen: 4}
143 f.refreshSidecar()
144 if f.bufCount != 0 {
145 t.Fatal("a handle without a sidecar must report no sidecar records")
146 }
147 if buf := f.readBufFile(); buf != nil {
148 t.Fatal("readBufFile without a sidecar must return nil")
149 }
150 }
151
152 func TestFlushWithNothingToMerge(t *testing.T) {
153 dir, ok := srTmp(t)
154 if !ok {
155 return
156 }
157 defer os.RemoveAll(dir)
158
159 f, ok2 := srOpen(t, dir|"/idx.dat")
160 if !ok2 {
161 return
162 }
163 defer f.Close()
164
165 // An untouched file flushes cleanly.
166 if err := f.Flush(); err != nil {
167 t.Fatal(err)
168 return
169 }
170
171 // Deleting every record and flushing must leave an empty file that still
172 // answers Count and Get.
173 f.Put(srRec(1, 0x01))
174 f.Put(srRec(2, 0x02))
175 if err := f.Flush(); err != nil {
176 t.Fatal(err)
177 return
178 }
179 f.Delete(srRec(1, 0))
180 f.Delete(srRec(2, 0))
181 if err := f.Flush(); err != nil {
182 t.Fatal(err)
183 return
184 }
185 if n := f.Count(); n != 0 {
186 t.Fatalf("Count after deleting everything = %d", n)
187 }
188 if _, found := f.Get(srRec(1, 0)); found {
189 t.Fatal("a deleted record must not be found")
190 }
191 }
192