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