| // Copyright 2011 The LevelDB-Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Taken from: https://code.google.com/p/leveldb-go/source/browse/leveldb/record/record_test.go?r=df1fa28f7f3be6c3935548169002309c12967135 |
| // License, authors and contributors informations can be found at bellow URLs respectively: |
| // https://code.google.com/p/leveldb-go/source/browse/LICENSE |
| // https://code.google.com/p/leveldb-go/source/browse/AUTHORS |
| // https://code.google.com/p/leveldb-go/source/browse/CONTRIBUTORS |
| |
| package journal |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "math/rand" |
| "strings" |
| "testing" |
| ) |
| |
| type dropper struct { |
| t *testing.T |
| } |
| |
| func (d dropper) Drop(err error) { |
| d.t.Log(err) |
| } |
| |
| func short(s string) string { |
| if len(s) < 64 { |
| return s |
| } |
| return fmt.Sprintf("%s...(skipping %d bytes)...%s", s[:20], len(s)-40, s[len(s)-20:]) |
| } |
| |
| // big returns a string of length n, composed of repetitions of partial. |
| func big(partial string, n int) string { |
| return strings.Repeat(partial, n/len(partial)+1)[:n] |
| } |
| |
| func TestEmpty(t *testing.T) { |
| buf := new(bytes.Buffer) |
| r := NewReader(buf, dropper{t}, true, true) |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("got %v, want %v", err, io.EOF) |
| } |
| } |
| |
| func testGenerator(t *testing.T, reset func(), gen func() (string, bool)) { |
| buf := new(bytes.Buffer) |
| |
| reset() |
| w := NewWriter(buf) |
| for { |
| s, ok := gen() |
| if !ok { |
| break |
| } |
| ww, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write([]byte(s)); err != nil { |
| t.Fatal(err) |
| } |
| } |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| reset() |
| r := NewReader(buf, dropper{t}, true, true) |
| for { |
| s, ok := gen() |
| if !ok { |
| break |
| } |
| rr, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| x, err := ioutil.ReadAll(rr) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if string(x) != s { |
| t.Fatalf("got %q, want %q", short(string(x)), short(s)) |
| } |
| } |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("got %v, want %v", err, io.EOF) |
| } |
| } |
| |
| func testLiterals(t *testing.T, s []string) { |
| var i int |
| reset := func() { |
| i = 0 |
| } |
| gen := func() (string, bool) { |
| if i == len(s) { |
| return "", false |
| } |
| i++ |
| return s[i-1], true |
| } |
| testGenerator(t, reset, gen) |
| } |
| |
| func TestMany(t *testing.T) { |
| const n = 1e5 |
| var i int |
| reset := func() { |
| i = 0 |
| } |
| gen := func() (string, bool) { |
| if i == n { |
| return "", false |
| } |
| i++ |
| return fmt.Sprintf("%d.", i-1), true |
| } |
| testGenerator(t, reset, gen) |
| } |
| |
| func TestRandom(t *testing.T) { |
| const n = 1e2 |
| var ( |
| i int |
| r *rand.Rand |
| ) |
| reset := func() { |
| i, r = 0, rand.New(rand.NewSource(0)) |
| } |
| gen := func() (string, bool) { |
| if i == n { |
| return "", false |
| } |
| i++ |
| return strings.Repeat(string(uint8(i)), r.Intn(2*blockSize+16)), true |
| } |
| testGenerator(t, reset, gen) |
| } |
| |
| func TestBasic(t *testing.T) { |
| testLiterals(t, []string{ |
| strings.Repeat("a", 1000), |
| strings.Repeat("b", 97270), |
| strings.Repeat("c", 8000), |
| }) |
| } |
| |
| func TestBoundary(t *testing.T) { |
| for i := blockSize - 16; i < blockSize+16; i++ { |
| s0 := big("abcd", i) |
| for j := blockSize - 16; j < blockSize+16; j++ { |
| s1 := big("ABCDE", j) |
| testLiterals(t, []string{s0, s1}) |
| testLiterals(t, []string{s0, "", s1}) |
| testLiterals(t, []string{s0, "x", s1}) |
| } |
| } |
| } |
| |
| func TestFlush(t *testing.T) { |
| buf := new(bytes.Buffer) |
| w := NewWriter(buf) |
| // Write a couple of records. Everything should still be held |
| // in the record.Writer buffer, so that buf.Len should be 0. |
| w0, _ := w.Next() |
| w0.Write([]byte("0")) |
| w1, _ := w.Next() |
| w1.Write([]byte("11")) |
| if got, want := buf.Len(), 0; got != want { |
| t.Fatalf("buffer length #0: got %d want %d", got, want) |
| } |
| // Flush the record.Writer buffer, which should yield 17 bytes. |
| // 17 = 2*7 + 1 + 2, which is two headers and 1 + 2 payload bytes. |
| if err := w.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| if got, want := buf.Len(), 17; got != want { |
| t.Fatalf("buffer length #1: got %d want %d", got, want) |
| } |
| // Do another write, one that isn't large enough to complete the block. |
| // The write should not have flowed through to buf. |
| w2, _ := w.Next() |
| w2.Write(bytes.Repeat([]byte("2"), 10000)) |
| if got, want := buf.Len(), 17; got != want { |
| t.Fatalf("buffer length #2: got %d want %d", got, want) |
| } |
| // Flushing should get us up to 10024 bytes written. |
| // 10024 = 17 + 7 + 10000. |
| if err := w.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| if got, want := buf.Len(), 10024; got != want { |
| t.Fatalf("buffer length #3: got %d want %d", got, want) |
| } |
| // Do a bigger write, one that completes the current block. |
| // We should now have 32768 bytes (a complete block), without |
| // an explicit flush. |
| w3, _ := w.Next() |
| w3.Write(bytes.Repeat([]byte("3"), 40000)) |
| if got, want := buf.Len(), 32768; got != want { |
| t.Fatalf("buffer length #4: got %d want %d", got, want) |
| } |
| // Flushing should get us up to 50038 bytes written. |
| // 50038 = 10024 + 2*7 + 40000. There are two headers because |
| // the one record was split into two chunks. |
| if err := w.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| if got, want := buf.Len(), 50038; got != want { |
| t.Fatalf("buffer length #5: got %d want %d", got, want) |
| } |
| // Check that reading those records give the right lengths. |
| r := NewReader(buf, dropper{t}, true, true) |
| wants := []int64{1, 2, 10000, 40000} |
| for i, want := range wants { |
| rr, _ := r.Next() |
| n, err := io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #%d: %v", i, err) |
| } |
| if n != want { |
| t.Fatalf("read #%d: got %d bytes want %d", i, n, want) |
| } |
| } |
| } |
| |
| func TestNonExhaustiveRead(t *testing.T) { |
| const n = 100 |
| buf := new(bytes.Buffer) |
| p := make([]byte, 10) |
| rnd := rand.New(rand.NewSource(1)) |
| |
| w := NewWriter(buf) |
| for i := 0; i < n; i++ { |
| length := len(p) + rnd.Intn(3*blockSize) |
| s := string(uint8(i)) + "123456789abcdefgh" |
| ww, _ := w.Next() |
| ww.Write([]byte(big(s, length))) |
| } |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| r := NewReader(buf, dropper{t}, true, true) |
| for i := 0; i < n; i++ { |
| rr, _ := r.Next() |
| _, err := io.ReadFull(rr, p) |
| if err != nil { |
| t.Fatal(err) |
| } |
| want := string(uint8(i)) + "123456789" |
| if got := string(p); got != want { |
| t.Fatalf("read #%d: got %q want %q", i, got, want) |
| } |
| } |
| } |
| |
| func TestStaleReader(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| w0, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| w0.Write([]byte("0")) |
| w1, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| w1.Write([]byte("11")) |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| r := NewReader(buf, dropper{t}, true, true) |
| r0, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| r1, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| p := make([]byte, 1) |
| if _, err := r0.Read(p); err == nil || !strings.Contains(err.Error(), "stale") { |
| t.Fatalf("stale read #0: unexpected error: %v", err) |
| } |
| if _, err := r1.Read(p); err != nil { |
| t.Fatalf("fresh read #1: got %v want nil error", err) |
| } |
| if p[0] != '1' { |
| t.Fatalf("fresh read #1: byte contents: got '%c' want '1'", p[0]) |
| } |
| } |
| |
| func TestStaleWriter(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| w0, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| w1, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := w0.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") { |
| t.Fatalf("stale write #0: unexpected error: %v", err) |
| } |
| if _, err := w1.Write([]byte("11")); err != nil { |
| t.Fatalf("fresh write #1: got %v want nil error", err) |
| } |
| if err := w.Flush(); err != nil { |
| t.Fatalf("flush: %v", err) |
| } |
| if _, err := w1.Write([]byte("0")); err == nil || !strings.Contains(err.Error(), "stale") { |
| t.Fatalf("stale write #1: unexpected error: %v", err) |
| } |
| } |
| |
| func TestCorrupt_MissingLastBlock(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| |
| // First record. |
| ww, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-1024)); err != nil { |
| t.Fatalf("write #0: unexpected error: %v", err) |
| } |
| |
| // Second record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil { |
| t.Fatalf("write #1: unexpected error: %v", err) |
| } |
| |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| // Cut the last block. |
| b := buf.Bytes()[:blockSize] |
| r := NewReader(bytes.NewReader(b), dropper{t}, false, true) |
| |
| // First read. |
| rr, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err := io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #0: %v", err) |
| } |
| if n != blockSize-1024 { |
| t.Fatalf("read #0: got %d bytes want %d", n, blockSize-1024) |
| } |
| |
| // Second read. |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != io.ErrUnexpectedEOF { |
| t.Fatalf("read #1: unexpected error: %v", err) |
| } |
| |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("last next: unexpected error: %v", err) |
| } |
| } |
| |
| func TestCorrupt_CorruptedFirstBlock(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| |
| // First record. |
| ww, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize/2)); err != nil { |
| t.Fatalf("write #0: unexpected error: %v", err) |
| } |
| |
| // Second record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil { |
| t.Fatalf("write #1: unexpected error: %v", err) |
| } |
| |
| // Third record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+1)); err != nil { |
| t.Fatalf("write #2: unexpected error: %v", err) |
| } |
| |
| // Fourth record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+2)); err != nil { |
| t.Fatalf("write #3: unexpected error: %v", err) |
| } |
| |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| b := buf.Bytes() |
| // Corrupting block #0. |
| for i := 0; i < 1024; i++ { |
| b[i] = '1' |
| } |
| |
| r := NewReader(bytes.NewReader(b), dropper{t}, false, true) |
| |
| // First read (third record). |
| rr, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err := io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #0: %v", err) |
| } |
| if want := int64(blockSize-headerSize) + 1; n != want { |
| t.Fatalf("read #0: got %d bytes want %d", n, want) |
| } |
| |
| // Second read (fourth record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #1: %v", err) |
| } |
| if want := int64(blockSize-headerSize) + 2; n != want { |
| t.Fatalf("read #1: got %d bytes want %d", n, want) |
| } |
| |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("last next: unexpected error: %v", err) |
| } |
| } |
| |
| func TestCorrupt_CorruptedMiddleBlock(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| |
| // First record. |
| ww, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize/2)); err != nil { |
| t.Fatalf("write #0: unexpected error: %v", err) |
| } |
| |
| // Second record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil { |
| t.Fatalf("write #1: unexpected error: %v", err) |
| } |
| |
| // Third record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+1)); err != nil { |
| t.Fatalf("write #2: unexpected error: %v", err) |
| } |
| |
| // Fourth record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+2)); err != nil { |
| t.Fatalf("write #3: unexpected error: %v", err) |
| } |
| |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| b := buf.Bytes() |
| // Corrupting block #1. |
| for i := 0; i < 1024; i++ { |
| b[blockSize+i] = '1' |
| } |
| |
| r := NewReader(bytes.NewReader(b), dropper{t}, false, true) |
| |
| // First read (first record). |
| rr, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err := io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #0: %v", err) |
| } |
| if want := int64(blockSize / 2); n != want { |
| t.Fatalf("read #0: got %d bytes want %d", n, want) |
| } |
| |
| // Second read (second record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != io.ErrUnexpectedEOF { |
| t.Fatalf("read #1: unexpected error: %v", err) |
| } |
| |
| // Third read (fourth record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #2: %v", err) |
| } |
| if want := int64(blockSize-headerSize) + 2; n != want { |
| t.Fatalf("read #2: got %d bytes want %d", n, want) |
| } |
| |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("last next: unexpected error: %v", err) |
| } |
| } |
| |
| func TestCorrupt_CorruptedLastBlock(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| |
| // First record. |
| ww, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize/2)); err != nil { |
| t.Fatalf("write #0: unexpected error: %v", err) |
| } |
| |
| // Second record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil { |
| t.Fatalf("write #1: unexpected error: %v", err) |
| } |
| |
| // Third record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+1)); err != nil { |
| t.Fatalf("write #2: unexpected error: %v", err) |
| } |
| |
| // Fourth record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+2)); err != nil { |
| t.Fatalf("write #3: unexpected error: %v", err) |
| } |
| |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| b := buf.Bytes() |
| // Corrupting block #3. |
| for i := len(b) - 1; i > len(b)-1024; i-- { |
| b[i] = '1' |
| } |
| |
| r := NewReader(bytes.NewReader(b), dropper{t}, false, true) |
| |
| // First read (first record). |
| rr, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err := io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #0: %v", err) |
| } |
| if want := int64(blockSize / 2); n != want { |
| t.Fatalf("read #0: got %d bytes want %d", n, want) |
| } |
| |
| // Second read (second record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #1: %v", err) |
| } |
| if want := int64(blockSize - headerSize); n != want { |
| t.Fatalf("read #1: got %d bytes want %d", n, want) |
| } |
| |
| // Third read (third record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #2: %v", err) |
| } |
| if want := int64(blockSize-headerSize) + 1; n != want { |
| t.Fatalf("read #2: got %d bytes want %d", n, want) |
| } |
| |
| // Fourth read (fourth record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != io.ErrUnexpectedEOF { |
| t.Fatalf("read #3: unexpected error: %v", err) |
| } |
| |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("last next: unexpected error: %v", err) |
| } |
| } |
| |
| func TestCorrupt_FirstChuckLengthOverflow(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| |
| // First record. |
| ww, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize/2)); err != nil { |
| t.Fatalf("write #0: unexpected error: %v", err) |
| } |
| |
| // Second record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil { |
| t.Fatalf("write #1: unexpected error: %v", err) |
| } |
| |
| // Third record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+1)); err != nil { |
| t.Fatalf("write #2: unexpected error: %v", err) |
| } |
| |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| b := buf.Bytes() |
| // Corrupting record #1. |
| x := blockSize |
| binary.LittleEndian.PutUint16(b[x+4:], 0xffff) |
| |
| r := NewReader(bytes.NewReader(b), dropper{t}, false, true) |
| |
| // First read (first record). |
| rr, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err := io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #0: %v", err) |
| } |
| if want := int64(blockSize / 2); n != want { |
| t.Fatalf("read #0: got %d bytes want %d", n, want) |
| } |
| |
| // Second read (second record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != io.ErrUnexpectedEOF { |
| t.Fatalf("read #1: unexpected error: %v", err) |
| } |
| |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("last next: unexpected error: %v", err) |
| } |
| } |
| |
| func TestCorrupt_MiddleChuckLengthOverflow(t *testing.T) { |
| buf := new(bytes.Buffer) |
| |
| w := NewWriter(buf) |
| |
| // First record. |
| ww, err := w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize/2)); err != nil { |
| t.Fatalf("write #0: unexpected error: %v", err) |
| } |
| |
| // Second record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), blockSize-headerSize)); err != nil { |
| t.Fatalf("write #1: unexpected error: %v", err) |
| } |
| |
| // Third record. |
| ww, err = w.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| if _, err := ww.Write(bytes.Repeat([]byte("0"), (blockSize-headerSize)+1)); err != nil { |
| t.Fatalf("write #2: unexpected error: %v", err) |
| } |
| |
| if err := w.Close(); err != nil { |
| t.Fatal(err) |
| } |
| |
| b := buf.Bytes() |
| // Corrupting record #1. |
| x := blockSize/2 + headerSize |
| binary.LittleEndian.PutUint16(b[x+4:], 0xffff) |
| |
| r := NewReader(bytes.NewReader(b), dropper{t}, false, true) |
| |
| // First read (first record). |
| rr, err := r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err := io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #0: %v", err) |
| } |
| if want := int64(blockSize / 2); n != want { |
| t.Fatalf("read #0: got %d bytes want %d", n, want) |
| } |
| |
| // Second read (third record). |
| rr, err = r.Next() |
| if err != nil { |
| t.Fatal(err) |
| } |
| n, err = io.Copy(ioutil.Discard, rr) |
| if err != nil { |
| t.Fatalf("read #1: %v", err) |
| } |
| if want := int64(blockSize-headerSize) + 1; n != want { |
| t.Fatalf("read #1: got %d bytes want %d", n, want) |
| } |
| |
| if _, err := r.Next(); err != io.EOF { |
| t.Fatalf("last next: unexpected error: %v", err) |
| } |
| } |