1
// Copyright 2011 The LevelDB-Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
17
func short(s string) string {
21
return fmt.Sprintf("%s...(skipping %d bytes)...%s", s[:20], len(s)-40, s[len(s)-20:])
24
// big returns a string of length n, composed of repetitions of partial.
25
func big(partial string, n int) string {
26
return strings.Repeat(partial, n/len(partial)+1)[:n]
29
// TestZeroBlocks tests that reading nothing but all-zero blocks gives io.EOF.
30
// This includes decoding an empty stream.
31
func TestZeroBlocks(t *testing.T) {
32
for i := 0; i < 3; i++ {
33
r := NewReader(bytes.NewReader(make([]byte, i*blockSize)))
34
if _, err := r.Next(); err != io.EOF {
35
t.Fatalf("%d blocks: got %v, want %v", i, err, io.EOF)
40
func testGenerator(t *testing.T, reset func(), gen func() (string, bool)) {
41
buf := new(bytes.Buffer)
52
t.Fatalf("writer.Next: %v", err)
54
if _, err := ww.Write([]byte(s)); err != nil {
55
t.Fatalf("Write: %v", err)
58
if err := w.Close(); err != nil {
59
t.Fatalf("Close: %v", err)
71
t.Fatalf("reader.Next: %v", err)
73
x, err := io.ReadAll(rr)
75
t.Fatalf("ReadAll: %v", err)
78
t.Fatalf("got %q, want %q", short(string(x)), short(s))
81
if _, err := r.Next(); err != io.EOF {
82
t.Fatalf("got %v, want %v", err, io.EOF)
86
func testLiterals(t *testing.T, s []string) {
91
gen := func() (string, bool) {
98
testGenerator(t, reset, gen)
101
func TestMany(t *testing.T) {
107
gen := func() (string, bool) {
112
return fmt.Sprintf("%d.", i-1), true
114
testGenerator(t, reset, gen)
117
func TestRandom(t *testing.T) {
124
i, r = 0, rand.New(rand.NewSource(0))
126
gen := func() (string, bool) {
131
return strings.Repeat(string(uint8(i)), r.Intn(2*blockSize+16)), true
133
testGenerator(t, reset, gen)
136
func TestBasic(t *testing.T) {
137
testLiterals(t, []string{
138
strings.Repeat("a", 1000),
139
strings.Repeat("b", 97270),
140
strings.Repeat("c", 8000),
144
func TestBoundary(t *testing.T) {
145
for i := blockSize - 16; i < blockSize+16; i++ {
147
for j := blockSize - 16; j < blockSize+16; j++ {
148
s1 := big("ABCDE", j)
149
testLiterals(t, []string{s0, s1})
150
testLiterals(t, []string{s0, "", s1})
151
testLiterals(t, []string{s0, "x", s1})
156
func TestFlush(t *testing.T) {
157
buf := new(bytes.Buffer)
159
// Write a couple of records. Everything should still be held
160
// in the record.Writer buffer, so that buf.Len should be 0.
162
_, _ = w0.Write([]byte("0"))
164
_, _ = w1.Write([]byte("11"))
165
if got, want := buf.Len(), 0; got != want {
166
t.Fatalf("buffer length #0: got %d want %d", got, want)
168
// Flush the record.Writer buffer, which should yield 17 bytes.
169
// 17 = 2*7 + 1 + 2, which is two headers and 1 + 2 payload bytes.
170
if err := w.Flush(); err != nil {
173
if got, want := buf.Len(), 17; got != want {
174
t.Fatalf("buffer length #1: got %d want %d", got, want)
176
// Do another write, one that isn't large enough to complete the block.
177
// The write should not have flowed through to buf.
179
_, _ = w2.Write(bytes.Repeat([]byte("2"), 10000))
180
if got, want := buf.Len(), 17; got != want {
181
t.Fatalf("buffer length #2: got %d want %d", got, want)
183
// Flushing should get us up to 10024 bytes written.
184
// 10024 = 17 + 7 + 10000.
185
if err := w.Flush(); err != nil {
188
if got, want := buf.Len(), 10024; got != want {
189
t.Fatalf("buffer length #3: got %d want %d", got, want)
191
// Do a bigger write, one that completes the current block.
192
// We should now have 32768 bytes (a complete block), without
193
// an explicit flush.
195
_, _ = w3.Write(bytes.Repeat([]byte("3"), 40000))
196
if got, want := buf.Len(), 32768; got != want {
197
t.Fatalf("buffer length #4: got %d want %d", got, want)
199
// Flushing should get us up to 50038 bytes written.
200
// 50038 = 10024 + 2*7 + 40000. There are two headers because
201
// the one record was split into two chunks.
202
if err := w.Flush(); err != nil {
205
if got, want := buf.Len(), 50038; got != want {
206
t.Fatalf("buffer length #5: got %d want %d", got, want)
208
// Check that reading those records give the right lengths.
210
wants := []int64{1, 2, 10000, 40000}
211
for i, want := range wants {
213
n, err := io.Copy(io.Discard, rr)
215
t.Fatalf("read #%d: %v", i, err)
218
t.Fatalf("read #%d: got %d bytes want %d", i, n, want)
223
func TestNonExhaustiveRead(t *testing.T) {
225
buf := new(bytes.Buffer)
226
p := make([]byte, 10)
227
rnd := rand.New(rand.NewSource(1))
230
for i := 0; i < n; i++ {
231
length := len(p) + rnd.Intn(3*blockSize)
232
s := string(uint8(i)) + "123456789abcdefgh"
234
_, _ = ww.Write([]byte(big(s, length)))
236
if err := w.Close(); err != nil {
237
t.Fatalf("Close: %v", err)
241
for i := 0; i < n; i++ {
243
_, err := io.ReadFull(rr, p)
245
t.Fatalf("ReadFull: %v", err)
247
want := string(uint8(i)) + "123456789"
248
if got := string(p); got != want {
249
t.Fatalf("read #%d: got %q want %q", i, got, want)
254
func TestStaleReader(t *testing.T) {
255
buf := new(bytes.Buffer)
260
t.Fatalf("writer.Next: %v", err)
262
_, _ = w0.Write([]byte("0"))
265
t.Fatalf("writer.Next: %v", err)
267
_, _ = w1.Write([]byte("11"))
268
if err := w.Close(); err != nil {
269
t.Fatalf("Close: %v", err)
275
t.Fatalf("reader.Next: %v", err)
279
t.Fatalf("reader.Next: %v", err)
282
if _, err := r0.Read(p); err == nil || !strings.Contains(err.Error(), "stale") {
283
t.Fatalf("stale read #0: unexpected error: %v", err)
285
if _, err := r1.Read(p); err != nil {
286
t.Fatalf("fresh read #1: got %v want nil error", err)
289
t.Fatalf("fresh read #1: byte contents: got '%c' want '1'", p[0])
293
func TestStaleWriter(t *testing.T) {
294
buf := new(bytes.Buffer)
299
t.Fatalf("writer.Next: %v", err)
303
t.Fatalf("writer.Next: %v", err)
305
if _, err := w0.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") {
306
t.Fatalf("stale write #0: unexpected error: %v", err)
308
if _, err := w1.Write([]byte("11")); err != nil {
309
t.Fatalf("fresh write #1: got %v want nil error", err)
311
if err := w.Flush(); err != nil {
312
t.Fatalf("flush: %v", err)
314
if _, err := w1.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") {
315
t.Fatalf("stale write #1: unexpected error: %v", err)
319
type testRecords struct {
320
records [][]byte // The raw value of each record.
321
offsets []int64 // The offset of each record within buf, derived from writer.LastRecordOffset.
322
buf []byte // The serialized records form of all records.
325
// makeTestRecords generates test records of specified lengths.
326
// The first record will consist of repeating 0x00 bytes, the next record of
327
// 0x01 bytes, and so forth. The values will loop back to 0x00 after 0xff.
328
func makeTestRecords(recordLengths ...int) (*testRecords, error) {
329
ret := &testRecords{}
330
ret.records = make([][]byte, len(recordLengths))
331
ret.offsets = make([]int64, len(recordLengths))
332
for i, n := range recordLengths {
333
ret.records[i] = bytes.Repeat([]byte{byte(i)}, n)
336
buf := new(bytes.Buffer)
338
for i, rec := range ret.records {
339
wRec, err := w.Next()
344
// Alternate between one big write and many small writes.
349
for ; len(rec) > cSize; rec = rec[cSize:] {
350
if _, err = wRec.Write(rec[:cSize]); err != nil {
354
if _, err = wRec.Write(rec); err != nil {
358
ret.offsets[i], err = w.LastRecordOffset()
364
if err := w.Close(); err != nil {
368
ret.buf = buf.Bytes()
372
// corruptBlock corrupts the checksum of the record that starts at the
373
// specified block offset. The number of the block offset is 0 based.
374
func corruptBlock(buf []byte, blockNum int) {
375
// Ensure we always permute at least 1 byte of the checksum.
376
if buf[blockSize*blockNum] == 0x00 {
377
buf[blockSize*blockNum] = 0xff
379
buf[blockSize*blockNum] = 0x00
382
buf[blockSize*blockNum+1] = 0x00
383
buf[blockSize*blockNum+2] = 0x00
384
buf[blockSize*blockNum+3] = 0x00
387
func TestRecoverNoOp(t *testing.T) {
388
recs, err := makeTestRecords(
389
blockSize-headerSize,
390
blockSize-headerSize,
391
blockSize-headerSize,
394
t.Fatalf("makeTestRecords: %v", err)
397
r := NewReader(bytes.NewReader(recs.buf))
399
if err != nil || r.err != nil {
400
t.Fatalf("reader.Next: %v reader.err: %v", err, r.err)
403
seq, i, j, n := r.seq, r.i, r.j, r.n
405
// Should be a no-op since r.err == nil.
408
// r.err was nil, nothing should have changed.
409
if seq != r.seq || i != r.i || j != r.j || n != r.n {
410
t.Fatal("reader.Recover when no error existed, was not a no-op")
414
func TestBasicRecover(t *testing.T) {
415
recs, err := makeTestRecords(
416
blockSize-headerSize,
417
blockSize-headerSize,
418
blockSize-headerSize,
421
t.Fatalf("makeTestRecords: %v", err)
424
// Corrupt the checksum of the second record r1 in our file.
425
corruptBlock(recs.buf, 1)
427
underlyingReader := bytes.NewReader(recs.buf)
428
r := NewReader(underlyingReader)
430
// The first record r0 should be read just fine.
433
t.Fatalf("Next: %v", err)
435
r0Data, err := io.ReadAll(r0)
437
t.Fatalf("ReadAll: %v", err)
439
if !bytes.Equal(r0Data, recs.records[0]) {
440
t.Fatal("Unexpected output in r0's data")
443
// The next record should have a checksum mismatch.
446
t.Fatal("Expected an error while reading a corrupted record")
448
if !strings.Contains(err.Error(), "checksum mismatch") {
449
t.Fatalf("Unexpected error returned: %v", err)
452
// Recover from that checksum mismatch.
454
currentOffset, err := underlyingReader.Seek(0, io.SeekCurrent)
456
t.Fatalf("current offset: %v", err)
458
if currentOffset != blockSize*2 {
459
t.Fatalf("current offset: got %d, want %d", currentOffset, blockSize*2)
462
// The third record r2 should be read just fine.
465
t.Fatalf("Next: %v", err)
467
r2Data, err := io.ReadAll(r2)
469
t.Fatalf("ReadAll: %v", err)
471
if !bytes.Equal(r2Data, recs.records[2]) {
472
t.Fatal("Unexpected output in r2's data")
476
func TestRecoverSingleBlock(t *testing.T) {
477
// The first record will be blockSize * 3 bytes long. Since each block has
478
// a 7 byte header, the first record will roll over into 4 blocks.
479
recs, err := makeTestRecords(
481
blockSize-headerSize,
485
t.Fatalf("makeTestRecords: %v", err)
488
// Corrupt the checksum for the portion of the first record that exists in
490
corruptBlock(recs.buf, 3)
492
// The first record should fail, but only when we read deeper beyond the
494
r := NewReader(bytes.NewReader(recs.buf))
497
t.Fatalf("Next: %v", err)
500
// Reading deeper should yield a checksum mismatch.
501
_, err = io.ReadAll(r0)
503
t.Fatal("Expected a checksum mismatch error, got nil")
505
if !strings.Contains(err.Error(), "checksum mismatch") {
506
t.Fatalf("Unexpected error returned: %v", err)
509
// Recover from that checksum mismatch.
512
// All of the data in the second record r1 is lost because the first record
513
// r0 shared a partial block with it. The second record also overlapped
514
// into the block with the third record r2. Recovery should jump to that
515
// block, skipping over the end of the second record and start parsing the
519
t.Fatalf("Next: %v", err)
521
r2Data, _ := io.ReadAll(r2)
522
if !bytes.Equal(r2Data, recs.records[2]) {
523
t.Fatal("Unexpected output in r2's data")
527
func TestRecoverMultipleBlocks(t *testing.T) {
528
recs, err := makeTestRecords(
529
// The first record will consume 3 entire blocks but a fraction of the 4th.
531
// The second record will completely fill the remainder of the 4th block.
532
3*(blockSize-headerSize)-2*blockSize-2*headerSize,
533
// Consume the entirety of the 5th block.
534
blockSize-headerSize,
535
// Consume the entirety of the 6th block.
536
blockSize-headerSize,
537
// Consume roughly half of the 7th block.
541
t.Fatalf("makeTestRecords: %v", err)
544
// Corrupt the checksum for the portion of the first record that exists in the 4th block.
545
corruptBlock(recs.buf, 3)
547
// Now corrupt the two blocks in a row that correspond to recs.records[2:4].
548
corruptBlock(recs.buf, 4)
549
corruptBlock(recs.buf, 5)
551
// The first record should fail, but only when we read deeper beyond the first block.
552
r := NewReader(bytes.NewReader(recs.buf))
555
t.Fatalf("Next: %v", err)
558
// Reading deeper should yield a checksum mismatch.
559
_, err = io.ReadAll(r0)
561
t.Fatal("Exptected a checksum mismatch error, got nil")
563
if !strings.Contains(err.Error(), "checksum mismatch") {
564
t.Fatalf("Unexpected error returned: %v", err)
567
// Recover from that checksum mismatch.
570
// All of the data in the second record is lost because the first
571
// record shared a partial block with it. The following two records
572
// have corrupted checksums as well, so the call above to r.Recover
573
// should result in r.Next() being a reader to the 5th record.
576
t.Fatalf("Next: %v", err)
579
r4Data, _ := io.ReadAll(r4)
580
if !bytes.Equal(r4Data, recs.records[4]) {
581
t.Fatal("Unexpected output in r4's data")
585
// verifyLastBlockRecover reads each record from recs expecting that the
586
// last record will be corrupted. It will then try Recover and verify that EOF
588
func verifyLastBlockRecover(recs *testRecords) error {
589
r := NewReader(bytes.NewReader(recs.buf))
590
// Loop to one element larger than the number of records to verify EOF.
591
for i := 0; i < len(recs.records)+1; i++ {
594
case len(recs.records) - 1:
596
return errors.New("Expected a checksum mismatch error, got nil")
599
case len(recs.records):
601
return fmt.Errorf("Expected io.EOF, got %v", err)
605
return fmt.Errorf("Next: %v", err)
612
func TestRecoverLastPartialBlock(t *testing.T) {
613
recs, err := makeTestRecords(
614
// The first record will consume 3 entire blocks but a fraction of the 4th.
616
// The second record will completely fill the remainder of the 4th block.
617
3*(blockSize-headerSize)-2*blockSize-2*headerSize,
618
// Consume roughly half of the 5th block.
622
t.Fatalf("makeTestRecords: %v", err)
625
// Corrupt the 5th block.
626
corruptBlock(recs.buf, 4)
628
// Verify Recover works when the last block is corrupted.
629
if err := verifyLastBlockRecover(recs); err != nil {
630
t.Fatalf("verifyLastBlockRecover: %v", err)
634
func TestRecoverLastCompleteBlock(t *testing.T) {
635
recs, err := makeTestRecords(
636
// The first record will consume 3 entire blocks but a fraction of the 4th.
638
// The second record will completely fill the remainder of the 4th block.
639
3*(blockSize-headerSize)-2*blockSize-2*headerSize,
640
// Consume the entire 5th block.
641
blockSize-headerSize,
644
t.Fatalf("makeTestRecords: %v", err)
647
// Corrupt the 5th block.
648
corruptBlock(recs.buf, 4)
650
// Verify Recover works when the last block is corrupted.
651
if err := verifyLastBlockRecover(recs); err != nil {
652
t.Fatalf("verifyLastBlockRecover: %v", err)
656
func TestSeekRecord(t *testing.T) {
657
recs, err := makeTestRecords(
658
// The first record will consume 3 entire blocks but a fraction of the 4th.
660
// The second record will completely fill the remainder of the 4th block.
661
3*(blockSize-headerSize)-2*blockSize-2*headerSize,
662
// Consume the entirety of the 5th block.
663
blockSize-headerSize,
664
// Consume the entirety of the 6th block.
665
blockSize-headerSize,
666
// Consume roughly half of the 7th block.
670
t.Fatalf("makeTestRecords: %v", err)
673
r := NewReader(bytes.NewReader(recs.buf))
674
// Seek to a valid block offset, but within a multiblock record. This should cause the next call to
675
// Next after SeekRecord to return the next valid FIRST/FULL chunk of the subsequent record.
676
err = r.SeekRecord(blockSize)
678
t.Fatalf("SeekRecord: %v", err)
682
t.Fatalf("Next: %v", err)
684
rData, _ := io.ReadAll(rec)
685
if !bytes.Equal(rData, recs.records[1]) {
686
t.Fatalf("Unexpected output in record 1's data, got %v want %v", rData, recs.records[1])
689
// Seek 3 bytes into the second block, which is still in the middle of the first record, but not
690
// at a valid chunk boundary. Should result in an error upon calling r.Next.
691
err = r.SeekRecord(blockSize + 3)
693
t.Fatalf("SeekRecord: %v", err)
695
if _, err = r.Next(); err == nil {
696
t.Fatalf("Expected an error seeking to an invalid chunk boundary")
700
// Seek to the fifth block and verify all records can be read as appropriate.
701
err = r.SeekRecord(blockSize * 4)
703
t.Fatalf("SeekRecord: %v", err)
706
check := func(i int) {
707
for ; i < len(recs.records); i++ {
710
t.Fatalf("Next: %v", err)
713
rData, _ := io.ReadAll(rec)
714
if !bytes.Equal(rData, recs.records[i]) {
715
t.Fatalf("Unexpected output in record #%d's data, got %v want %v", i, rData, recs.records[i])
721
// Seek back to the fourth block, and read all subsequent records and verify them.
722
err = r.SeekRecord(blockSize * 3)
724
t.Fatalf("SeekRecord: %v", err)
728
// Now seek past the end of the file and verify it causes an error.
729
err = r.SeekRecord(1 << 20)
731
t.Fatalf("Seek past the end of a file didn't cause an error")
734
t.Fatalf("Seeking past EOF raised unexpected error: %v", err)
736
r.Recover() // Verify recovery works.
738
// Validate the current records are returned after seeking to a valid offset.
739
err = r.SeekRecord(blockSize * 4)
741
t.Fatalf("SeekRecord: %v", err)
746
func TestLastRecordOffset(t *testing.T) {
747
recs, err := makeTestRecords(
748
// The first record will consume 3 entire blocks but a fraction of the 4th.
750
// The second record will completely fill the remainder of the 4th block.
751
3*(blockSize-headerSize)-2*blockSize-2*headerSize,
752
// Consume the entirety of the 5th block.
753
blockSize-headerSize,
754
// Consume the entirety of the 6th block.
755
blockSize-headerSize,
756
// Consume roughly half of the 7th block.
760
t.Fatalf("makeTestRecords: %v", err)
763
wants := []int64{0, 98332, 131072, 163840, 196608}
764
for i, got := range recs.offsets {
765
if want := wants[i]; got != want {
766
t.Errorf("record #%d: got %d, want %d", i, got, want)
771
func TestNoLastRecordOffset(t *testing.T) {
772
buf := new(bytes.Buffer)
775
if _, err := w.LastRecordOffset(); err != ErrNoLastRecord {
776
t.Fatalf("Expected ErrNoLastRecord, got: %v", err)
779
if err := w.Flush(); err != nil {
783
if _, err := w.LastRecordOffset(); err != ErrNoLastRecord {
784
t.Fatalf("LastRecordOffset: got: %v, want ErrNoLastRecord", err)
787
writer, err := w.Next()
792
if _, err := writer.Write([]byte("testrecord")); err != nil {
796
if off, err := w.LastRecordOffset(); err != nil {
797
t.Fatalf("LastRecordOffset: %v", err)
799
t.Fatalf("LastRecordOffset: got %d, want 0", off)