wandb

Форк
0
/
record_internal_test.go 
801 строка · 20.6 Кб
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.
4

5
package leveldb
6

7
import (
8
	"bytes"
9
	"errors"
10
	"fmt"
11
	"io"
12
	"math/rand"
13
	"strings"
14
	"testing"
15
)
16

17
func short(s string) string {
18
	if len(s) < 64 {
19
		return s
20
	}
21
	return fmt.Sprintf("%s...(skipping %d bytes)...%s", s[:20], len(s)-40, s[len(s)-20:])
22
}
23

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]
27
}
28

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)
36
		}
37
	}
38
}
39

40
func testGenerator(t *testing.T, reset func(), gen func() (string, bool)) {
41
	buf := new(bytes.Buffer)
42

43
	reset()
44
	w := NewWriter(buf)
45
	for {
46
		s, ok := gen()
47
		if !ok {
48
			break
49
		}
50
		ww, err := w.Next()
51
		if err != nil {
52
			t.Fatalf("writer.Next: %v", err)
53
		}
54
		if _, err := ww.Write([]byte(s)); err != nil {
55
			t.Fatalf("Write: %v", err)
56
		}
57
	}
58
	if err := w.Close(); err != nil {
59
		t.Fatalf("Close: %v", err)
60
	}
61

62
	reset()
63
	r := NewReader(buf)
64
	for {
65
		s, ok := gen()
66
		if !ok {
67
			break
68
		}
69
		rr, err := r.Next()
70
		if err != nil {
71
			t.Fatalf("reader.Next: %v", err)
72
		}
73
		x, err := io.ReadAll(rr)
74
		if err != nil {
75
			t.Fatalf("ReadAll: %v", err)
76
		}
77
		if string(x) != s {
78
			t.Fatalf("got %q, want %q", short(string(x)), short(s))
79
		}
80
	}
81
	if _, err := r.Next(); err != io.EOF {
82
		t.Fatalf("got %v, want %v", err, io.EOF)
83
	}
84
}
85

86
func testLiterals(t *testing.T, s []string) {
87
	var i int
88
	reset := func() {
89
		i = 0
90
	}
91
	gen := func() (string, bool) {
92
		if i == len(s) {
93
			return "", false
94
		}
95
		i++
96
		return s[i-1], true
97
	}
98
	testGenerator(t, reset, gen)
99
}
100

101
func TestMany(t *testing.T) {
102
	const n = 1e5
103
	var i int
104
	reset := func() {
105
		i = 0
106
	}
107
	gen := func() (string, bool) {
108
		if i == n {
109
			return "", false
110
		}
111
		i++
112
		return fmt.Sprintf("%d.", i-1), true
113
	}
114
	testGenerator(t, reset, gen)
115
}
116

117
func TestRandom(t *testing.T) {
118
	const n = 1e2
119
	var (
120
		i int
121
		r *rand.Rand
122
	)
123
	reset := func() {
124
		i, r = 0, rand.New(rand.NewSource(0))
125
	}
126
	gen := func() (string, bool) {
127
		if i == n {
128
			return "", false
129
		}
130
		i++
131
		return strings.Repeat(string(uint8(i)), r.Intn(2*blockSize+16)), true
132
	}
133
	testGenerator(t, reset, gen)
134
}
135

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),
141
	})
142
}
143

144
func TestBoundary(t *testing.T) {
145
	for i := blockSize - 16; i < blockSize+16; i++ {
146
		s0 := big("abcd", 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})
152
		}
153
	}
154
}
155

156
func TestFlush(t *testing.T) {
157
	buf := new(bytes.Buffer)
158
	w := NewWriter(buf)
159
	// Write a couple of records. Everything should still be held
160
	// in the record.Writer buffer, so that buf.Len should be 0.
161
	w0, _ := w.Next()
162
	_, _ = w0.Write([]byte("0"))
163
	w1, _ := w.Next()
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)
167
	}
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 {
171
		t.Fatal(err)
172
	}
173
	if got, want := buf.Len(), 17; got != want {
174
		t.Fatalf("buffer length #1: got %d want %d", got, want)
175
	}
176
	// Do another write, one that isn't large enough to complete the block.
177
	// The write should not have flowed through to buf.
178
	w2, _ := w.Next()
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)
182
	}
183
	// Flushing should get us up to 10024 bytes written.
184
	// 10024 = 17 + 7 + 10000.
185
	if err := w.Flush(); err != nil {
186
		t.Fatal(err)
187
	}
188
	if got, want := buf.Len(), 10024; got != want {
189
		t.Fatalf("buffer length #3: got %d want %d", got, want)
190
	}
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.
194
	w3, _ := w.Next()
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)
198
	}
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 {
203
		t.Fatal(err)
204
	}
205
	if got, want := buf.Len(), 50038; got != want {
206
		t.Fatalf("buffer length #5: got %d want %d", got, want)
207
	}
208
	// Check that reading those records give the right lengths.
209
	r := NewReader(buf)
210
	wants := []int64{1, 2, 10000, 40000}
211
	for i, want := range wants {
212
		rr, _ := r.Next()
213
		n, err := io.Copy(io.Discard, rr)
214
		if err != nil {
215
			t.Fatalf("read #%d: %v", i, err)
216
		}
217
		if n != want {
218
			t.Fatalf("read #%d: got %d bytes want %d", i, n, want)
219
		}
220
	}
221
}
222

223
func TestNonExhaustiveRead(t *testing.T) {
224
	const n = 100
225
	buf := new(bytes.Buffer)
226
	p := make([]byte, 10)
227
	rnd := rand.New(rand.NewSource(1))
228

229
	w := NewWriter(buf)
230
	for i := 0; i < n; i++ {
231
		length := len(p) + rnd.Intn(3*blockSize)
232
		s := string(uint8(i)) + "123456789abcdefgh"
233
		ww, _ := w.Next()
234
		_, _ = ww.Write([]byte(big(s, length)))
235
	}
236
	if err := w.Close(); err != nil {
237
		t.Fatalf("Close: %v", err)
238
	}
239

240
	r := NewReader(buf)
241
	for i := 0; i < n; i++ {
242
		rr, _ := r.Next()
243
		_, err := io.ReadFull(rr, p)
244
		if err != nil {
245
			t.Fatalf("ReadFull: %v", err)
246
		}
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)
250
		}
251
	}
252
}
253

254
func TestStaleReader(t *testing.T) {
255
	buf := new(bytes.Buffer)
256

257
	w := NewWriter(buf)
258
	w0, err := w.Next()
259
	if err != nil {
260
		t.Fatalf("writer.Next: %v", err)
261
	}
262
	_, _ = w0.Write([]byte("0"))
263
	w1, err := w.Next()
264
	if err != nil {
265
		t.Fatalf("writer.Next: %v", err)
266
	}
267
	_, _ = w1.Write([]byte("11"))
268
	if err := w.Close(); err != nil {
269
		t.Fatalf("Close: %v", err)
270
	}
271

272
	r := NewReader(buf)
273
	r0, err := r.Next()
274
	if err != nil {
275
		t.Fatalf("reader.Next: %v", err)
276
	}
277
	r1, err := r.Next()
278
	if err != nil {
279
		t.Fatalf("reader.Next: %v", err)
280
	}
281
	p := make([]byte, 1)
282
	if _, err := r0.Read(p); err == nil || !strings.Contains(err.Error(), "stale") {
283
		t.Fatalf("stale read #0: unexpected error: %v", err)
284
	}
285
	if _, err := r1.Read(p); err != nil {
286
		t.Fatalf("fresh read #1: got %v want nil error", err)
287
	}
288
	if p[0] != '1' {
289
		t.Fatalf("fresh read #1: byte contents: got '%c' want '1'", p[0])
290
	}
291
}
292

293
func TestStaleWriter(t *testing.T) {
294
	buf := new(bytes.Buffer)
295

296
	w := NewWriter(buf)
297
	w0, err := w.Next()
298
	if err != nil {
299
		t.Fatalf("writer.Next: %v", err)
300
	}
301
	w1, err := w.Next()
302
	if err != nil {
303
		t.Fatalf("writer.Next: %v", err)
304
	}
305
	if _, err := w0.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") {
306
		t.Fatalf("stale write #0: unexpected error: %v", err)
307
	}
308
	if _, err := w1.Write([]byte("11")); err != nil {
309
		t.Fatalf("fresh write #1: got %v want nil error", err)
310
	}
311
	if err := w.Flush(); err != nil {
312
		t.Fatalf("flush: %v", err)
313
	}
314
	if _, err := w1.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") {
315
		t.Fatalf("stale write #1: unexpected error: %v", err)
316
	}
317
}
318

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.
323
}
324

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)
334
	}
335

336
	buf := new(bytes.Buffer)
337
	w := NewWriter(buf)
338
	for i, rec := range ret.records {
339
		wRec, err := w.Next()
340
		if err != nil {
341
			return nil, err
342
		}
343

344
		// Alternate between one big write and many small writes.
345
		cSize := 8
346
		if i&1 == 0 {
347
			cSize = len(rec)
348
		}
349
		for ; len(rec) > cSize; rec = rec[cSize:] {
350
			if _, err = wRec.Write(rec[:cSize]); err != nil {
351
				return nil, err
352
			}
353
		}
354
		if _, err = wRec.Write(rec); err != nil {
355
			return nil, err
356
		}
357

358
		ret.offsets[i], err = w.LastRecordOffset()
359
		if err != nil {
360
			return nil, err
361
		}
362
	}
363

364
	if err := w.Close(); err != nil {
365
		return nil, err
366
	}
367

368
	ret.buf = buf.Bytes()
369
	return ret, nil
370
}
371

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
378
	} else {
379
		buf[blockSize*blockNum] = 0x00
380
	}
381

382
	buf[blockSize*blockNum+1] = 0x00
383
	buf[blockSize*blockNum+2] = 0x00
384
	buf[blockSize*blockNum+3] = 0x00
385
}
386

387
func TestRecoverNoOp(t *testing.T) {
388
	recs, err := makeTestRecords(
389
		blockSize-headerSize,
390
		blockSize-headerSize,
391
		blockSize-headerSize,
392
	)
393
	if err != nil {
394
		t.Fatalf("makeTestRecords: %v", err)
395
	}
396

397
	r := NewReader(bytes.NewReader(recs.buf))
398
	_, err = r.Next()
399
	if err != nil || r.err != nil {
400
		t.Fatalf("reader.Next: %v reader.err: %v", err, r.err)
401
	}
402

403
	seq, i, j, n := r.seq, r.i, r.j, r.n
404

405
	// Should be a no-op since r.err == nil.
406
	r.Recover()
407

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")
411
	}
412
}
413

414
func TestBasicRecover(t *testing.T) {
415
	recs, err := makeTestRecords(
416
		blockSize-headerSize,
417
		blockSize-headerSize,
418
		blockSize-headerSize,
419
	)
420
	if err != nil {
421
		t.Fatalf("makeTestRecords: %v", err)
422
	}
423

424
	// Corrupt the checksum of the second record r1 in our file.
425
	corruptBlock(recs.buf, 1)
426

427
	underlyingReader := bytes.NewReader(recs.buf)
428
	r := NewReader(underlyingReader)
429

430
	// The first record r0 should be read just fine.
431
	r0, err := r.Next()
432
	if err != nil {
433
		t.Fatalf("Next: %v", err)
434
	}
435
	r0Data, err := io.ReadAll(r0)
436
	if err != nil {
437
		t.Fatalf("ReadAll: %v", err)
438
	}
439
	if !bytes.Equal(r0Data, recs.records[0]) {
440
		t.Fatal("Unexpected output in r0's data")
441
	}
442

443
	// The next record should have a checksum mismatch.
444
	_, err = r.Next()
445
	if err == nil {
446
		t.Fatal("Expected an error while reading a corrupted record")
447
	}
448
	if !strings.Contains(err.Error(), "checksum mismatch") {
449
		t.Fatalf("Unexpected error returned: %v", err)
450
	}
451

452
	// Recover from that checksum mismatch.
453
	r.Recover()
454
	currentOffset, err := underlyingReader.Seek(0, io.SeekCurrent)
455
	if err != nil {
456
		t.Fatalf("current offset: %v", err)
457
	}
458
	if currentOffset != blockSize*2 {
459
		t.Fatalf("current offset: got %d, want %d", currentOffset, blockSize*2)
460
	}
461

462
	// The third record r2 should be read just fine.
463
	r2, err := r.Next()
464
	if err != nil {
465
		t.Fatalf("Next: %v", err)
466
	}
467
	r2Data, err := io.ReadAll(r2)
468
	if err != nil {
469
		t.Fatalf("ReadAll: %v", err)
470
	}
471
	if !bytes.Equal(r2Data, recs.records[2]) {
472
		t.Fatal("Unexpected output in r2's data")
473
	}
474
}
475

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(
480
		blockSize*3,
481
		blockSize-headerSize,
482
		blockSize/2,
483
	)
484
	if err != nil {
485
		t.Fatalf("makeTestRecords: %v", err)
486
	}
487

488
	// Corrupt the checksum for the portion of the first record that exists in
489
	// the 4th block.
490
	corruptBlock(recs.buf, 3)
491

492
	// The first record should fail, but only when we read deeper beyond the
493
	// first block.
494
	r := NewReader(bytes.NewReader(recs.buf))
495
	r0, err := r.Next()
496
	if err != nil {
497
		t.Fatalf("Next: %v", err)
498
	}
499

500
	// Reading deeper should yield a checksum mismatch.
501
	_, err = io.ReadAll(r0)
502
	if err == nil {
503
		t.Fatal("Expected a checksum mismatch error, got nil")
504
	}
505
	if !strings.Contains(err.Error(), "checksum mismatch") {
506
		t.Fatalf("Unexpected error returned: %v", err)
507
	}
508

509
	// Recover from that checksum mismatch.
510
	r.Recover()
511

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
516
	// third record.
517
	r2, err := r.Next()
518
	if err != nil {
519
		t.Fatalf("Next: %v", err)
520
	}
521
	r2Data, _ := io.ReadAll(r2)
522
	if !bytes.Equal(r2Data, recs.records[2]) {
523
		t.Fatal("Unexpected output in r2's data")
524
	}
525
}
526

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.
530
		blockSize*3,
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.
538
		blockSize/2,
539
	)
540
	if err != nil {
541
		t.Fatalf("makeTestRecords: %v", err)
542
	}
543

544
	// Corrupt the checksum for the portion of the first record that exists in the 4th block.
545
	corruptBlock(recs.buf, 3)
546

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)
550

551
	// The first record should fail, but only when we read deeper beyond the first block.
552
	r := NewReader(bytes.NewReader(recs.buf))
553
	r0, err := r.Next()
554
	if err != nil {
555
		t.Fatalf("Next: %v", err)
556
	}
557

558
	// Reading deeper should yield a checksum mismatch.
559
	_, err = io.ReadAll(r0)
560
	if err == nil {
561
		t.Fatal("Exptected a checksum mismatch error, got nil")
562
	}
563
	if !strings.Contains(err.Error(), "checksum mismatch") {
564
		t.Fatalf("Unexpected error returned: %v", err)
565
	}
566

567
	// Recover from that checksum mismatch.
568
	r.Recover()
569

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.
574
	r4, err := r.Next()
575
	if err != nil {
576
		t.Fatalf("Next: %v", err)
577
	}
578

579
	r4Data, _ := io.ReadAll(r4)
580
	if !bytes.Equal(r4Data, recs.records[4]) {
581
		t.Fatal("Unexpected output in r4's data")
582
	}
583
}
584

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
587
// is returned.
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++ {
592
		_, err := r.Next()
593
		switch i {
594
		case len(recs.records) - 1:
595
			if err == nil {
596
				return errors.New("Expected a checksum mismatch error, got nil")
597
			}
598
			r.Recover()
599
		case len(recs.records):
600
			if err != io.EOF {
601
				return fmt.Errorf("Expected io.EOF, got %v", err)
602
			}
603
		default:
604
			if err != nil {
605
				return fmt.Errorf("Next: %v", err)
606
			}
607
		}
608
	}
609
	return nil
610
}
611

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.
615
		blockSize*3,
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.
619
		blockSize/2,
620
	)
621
	if err != nil {
622
		t.Fatalf("makeTestRecords: %v", err)
623
	}
624

625
	// Corrupt the 5th block.
626
	corruptBlock(recs.buf, 4)
627

628
	// Verify Recover works when the last block is corrupted.
629
	if err := verifyLastBlockRecover(recs); err != nil {
630
		t.Fatalf("verifyLastBlockRecover: %v", err)
631
	}
632
}
633

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.
637
		blockSize*3,
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,
642
	)
643
	if err != nil {
644
		t.Fatalf("makeTestRecords: %v", err)
645
	}
646

647
	// Corrupt the 5th block.
648
	corruptBlock(recs.buf, 4)
649

650
	// Verify Recover works when the last block is corrupted.
651
	if err := verifyLastBlockRecover(recs); err != nil {
652
		t.Fatalf("verifyLastBlockRecover: %v", err)
653
	}
654
}
655

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.
659
		blockSize*3,
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.
667
		blockSize/2,
668
	)
669
	if err != nil {
670
		t.Fatalf("makeTestRecords: %v", err)
671
	}
672

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)
677
	if err != nil {
678
		t.Fatalf("SeekRecord: %v", err)
679
	}
680
	rec, err := r.Next()
681
	if err != nil {
682
		t.Fatalf("Next: %v", err)
683
	}
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])
687
	}
688

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)
692
	if err != nil {
693
		t.Fatalf("SeekRecord: %v", err)
694
	}
695
	if _, err = r.Next(); err == nil {
696
		t.Fatalf("Expected an error seeking to an invalid chunk boundary")
697
	}
698
	r.Recover()
699

700
	// Seek to the fifth block and verify all records can be read as appropriate.
701
	err = r.SeekRecord(blockSize * 4)
702
	if err != nil {
703
		t.Fatalf("SeekRecord: %v", err)
704
	}
705

706
	check := func(i int) {
707
		for ; i < len(recs.records); i++ {
708
			rec, err := r.Next()
709
			if err != nil {
710
				t.Fatalf("Next: %v", err)
711
			}
712

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])
716
			}
717
		}
718
	}
719
	check(2)
720

721
	// Seek back to the fourth block, and read all subsequent records and verify them.
722
	err = r.SeekRecord(blockSize * 3)
723
	if err != nil {
724
		t.Fatalf("SeekRecord: %v", err)
725
	}
726
	check(1)
727

728
	// Now seek past the end of the file and verify it causes an error.
729
	err = r.SeekRecord(1 << 20)
730
	if err == nil {
731
		t.Fatalf("Seek past the end of a file didn't cause an error")
732
	}
733
	if err != io.EOF {
734
		t.Fatalf("Seeking past EOF raised unexpected error: %v", err)
735
	}
736
	r.Recover() // Verify recovery works.
737

738
	// Validate the current records are returned after seeking to a valid offset.
739
	err = r.SeekRecord(blockSize * 4)
740
	if err != nil {
741
		t.Fatalf("SeekRecord: %v", err)
742
	}
743
	check(2)
744
}
745

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.
749
		blockSize*3,
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.
757
		blockSize/2,
758
	)
759
	if err != nil {
760
		t.Fatalf("makeTestRecords: %v", err)
761
	}
762

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)
767
		}
768
	}
769
}
770

771
func TestNoLastRecordOffset(t *testing.T) {
772
	buf := new(bytes.Buffer)
773
	w := NewWriter(buf)
774

775
	if _, err := w.LastRecordOffset(); err != ErrNoLastRecord {
776
		t.Fatalf("Expected ErrNoLastRecord, got: %v", err)
777
	}
778

779
	if err := w.Flush(); err != nil {
780
		t.Fatal(err)
781
	}
782

783
	if _, err := w.LastRecordOffset(); err != ErrNoLastRecord {
784
		t.Fatalf("LastRecordOffset: got: %v, want ErrNoLastRecord", err)
785
	}
786

787
	writer, err := w.Next()
788
	if err != nil {
789
		t.Fatal(err)
790
	}
791

792
	if _, err := writer.Write([]byte("testrecord")); err != nil {
793
		t.Fatal(err)
794
	}
795

796
	if off, err := w.LastRecordOffset(); err != nil {
797
		t.Fatalf("LastRecordOffset: %v", err)
798
	} else if off != 0 {
799
		t.Fatalf("LastRecordOffset: got %d, want 0", off)
800
	}
801
}
802

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.