package wal import ( "errors" "fmt" "io/ioutil" "math/rand" "os" "strings" "sync" "sync/atomic" "testing") func dataStr(index uint64) string { if index%2 == 0 { return fmt.Sprintf("data-\"%d\"", index) } return fmt.Sprintf("data-'%d'", index)} func testLog(t *testing.T, opts *Options, N int) { logPath := "testlog/" + strings.Join(strings.Split(t.Name(), "/")[1:], "/") l, err := Open(logPath, opts) if err != nil { t.Fatal(err) } defer l.Close() // FirstIndex - should be zero or one, depending on allow empty. n, err := l.FirstIndex() if err != nil { t.Fatal(err) } if !l.opts.AllowEmpty { if n != 0 { t.Fatalf("expected %d, got %d", 0, n) } } else { if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } } // LastIndex - should be zero n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != 0 { t.Fatalf("expected %d, got %d", 0, n) } for i := 1; i <= N; i++ { // Write - try to append previous index, should fail err = l.Write(uint64(i-1), nil) if err != ErrOutOfOrder { t.Fatalf("expected %v, got %v", ErrOutOfOrder, err) } // Write - append next item err = l.Write(uint64(i), []byte(dataStr(uint64(i)))) if err != nil { t.Fatalf("expected %v, got %v", nil, err) } // Write - get next item data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("expected %v, got %v", nil, err) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- should fail, not found _, err = l.Read(0) if err != ErrNotFound { t.Fatalf("expected %v, got %v", ErrNotFound, err) } // Read -- read back all entries for i := 1; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- read back first half entries for i := 1; i <= N/2; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- read second third entries for i := N / 3; i <= N/3+N/3; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- random access for _, v := range rand.Perm(N) { index := uint64(v + 1) data, err := l.Read(index) if err != nil { t.Fatal(err) } if dataStr(index) != string(data) { t.Fatalf("expected %v, got %v", dataStr(index), string(data)) } } // FirstIndex/LastIndex -- check valid first and last indexes n, err = l.FirstIndex() if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != uint64(N) { t.Fatalf("expected %d, got %d", N, n) } // Close -- close the log if err := l.Close(); err != nil { t.Fatal(err) } // Write - try while closed err = l.Write(1, nil) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // WriteBatch - try while closed err = l.WriteBatch(nil) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // FirstIndex - try while closed _, err = l.FirstIndex() if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // LastIndex - try while closed _, err = l.LastIndex() if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // Get - try while closed _, err = l.Read(0) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // TruncateFront - try while closed err = l.TruncateFront(0) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // TruncateBack - try while closed err = l.TruncateBack(0) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // Open -- reopen log l, err = Open(logPath, opts) if err != nil { t.Fatal(err) } defer l.Close() // Read -- read back all entries for i := 1; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d, err=%s", i, err) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // FirstIndex/LastIndex -- check valid first and last indexes n, err = l.FirstIndex() if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != uint64(N) { t.Fatalf("expected %d, got %d", N, n) } // Write -- add 50 more items for i := N + 1; i <= N+50; i++ { index := uint64(i) if err := l.Write(index, []byte(dataStr(index))); err != nil { t.Fatal(err) } data, err := l.Read(index) if err != nil { t.Fatal(err) } if string(data) != dataStr(index) { t.Fatalf("expected %v, got %v", dataStr(index), string(data)) } } N += 50 // FirstIndex/LastIndex -- check valid first and last indexes n, err = l.FirstIndex() if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != uint64(N) { t.Fatalf("expected %d, got %d", N, n) } // Batch -- test batch writes b := new(Batch) b.Write(1, nil) b.Write(2, nil) b.Write(3, nil) // WriteBatch -- should fail out of order err = l.WriteBatch(b) if err != ErrOutOfOrder { t.Fatalf("expected %v, got %v", ErrOutOfOrder, nil) } // Clear -- clear the batch b.Clear() // WriteBatch -- should succeed err = l.WriteBatch(b) if err != nil { t.Fatal(err) } // Write 100 entries in batches of 10 for i := 0; i < 10; i++ { for i := N + 1; i <= N+10; i++ { index := uint64(i) b.Write(index, []byte(dataStr(index))) } err = l.WriteBatch(b) if err != nil { t.Fatal(err) } N += 10 } // Read -- read back all entries for i := 1; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Write -- one entry, so the buffer might be activated err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ // Read -- one random read, so there is an opened reader data, err := l.Read(uint64(N / 2)) if err != nil { t.Fatal(err) } if string(data) != dataStr(uint64(N/2)) { t.Fatalf("expected %v, got %v", dataStr(uint64(N/2)), string(data)) } // TruncateFront -- should fail, out of range for _, i := range []int{0, N + 2} { index := uint64(i) if err = l.TruncateFront(index); err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } testFirstLast(t, l, uint64(1), uint64(N), nil) } // TruncateBack -- should fail, out of range err = l.TruncateFront(0) if err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } testFirstLast(t, l, uint64(1), uint64(N), nil) // TruncateFront -- Remove no entries if err = l.TruncateFront(1); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(1), uint64(N), nil) // TruncateFront -- Remove first 80 entries if err = l.TruncateFront(81); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(81), uint64(N), nil) // Write -- one entry, so the buffer might be activated err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ testFirstLast(t, l, uint64(81), uint64(N), nil) // Read -- one random read, so there is an opened reader data, err = l.Read(uint64(N / 2)) if err != nil { t.Fatal(err) } if string(data) != dataStr(uint64(N/2)) { t.Fatalf("expected %v, got %v", dataStr(uint64(N/2)), string(data)) } // TruncateBack -- should fail, out of range for _, i := range []int{0, 79} { index := uint64(i) if err = l.TruncateBack(index); err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } testFirstLast(t, l, uint64(81), uint64(N), nil) } // TruncateBack -- Remove no entries if err = l.TruncateBack(uint64(N)); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(81), uint64(N), nil) // TruncateBack -- Remove last 80 entries if err = l.TruncateBack(uint64(N - 80)); err != nil { t.Fatal(err) } N -= 80 testFirstLast(t, l, uint64(81), uint64(N), nil) // Read -- read back all entries for i := 81; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Close -- close log after truncating if err = l.Close(); err != nil { t.Fatal(err) } // Open -- open log after truncating l, err = Open(logPath, opts) if err != nil { t.Fatal(err) } defer l.Close() testFirstLast(t, l, uint64(81), uint64(N), nil) // Read -- read back all entries for i := 81; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // TruncateFront -- truncate all entries but one if err = l.TruncateFront(uint64(N)); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(N), uint64(N), nil) // Write -- write on entry err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ testFirstLast(t, l, uint64(N-1), uint64(N), nil) // TruncateBack -- truncate all entries but one if err = l.TruncateBack(uint64(N - 1)); err != nil { t.Fatal(err) } N-- testFirstLast(t, l, uint64(N), uint64(N), nil) if err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))); err != nil { t.Fatal(err) } N++ l.Sync() testFirstLast(t, l, uint64(N-1), uint64(N), nil) allowEmpty := opts != nil && opts.AllowEmpty // TruncateFront -- truncate all entries err = l.TruncateFront(uint64(N + 1)) if allowEmpty { if err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(N+1), uint64(N), nil) } else { if err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } } err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ if allowEmpty { testFirstLast(t, l, uint64(N), uint64(N), nil) } else { testFirstLast(t, l, uint64(N-2), uint64(N), nil) } // TruncateBack -- truncate all entries fidx, err := l.FirstIndex() if err != nil { t.Fatal(err) } if allowEmpty { if fidx != uint64(N) { t.Fatalf("expected %v, got %v", N, fidx) } } else { if fidx != uint64(N-2) { t.Fatalf("expected %v, got %v", N-2, fidx) } } err = l.TruncateBack(uint64(fidx - 1)) if allowEmpty { if err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(N), uint64(N-1), nil) } else { if err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } }} func testFirstLast(t *testing.T, l *Log, expectFirst, expectLast uint64, data func(index uint64) []byte,) { t.Helper() fi, err := l.FirstIndex() if err != nil { t.Fatal(err) } li, err := l.LastIndex() if err != nil { t.Fatal(err) } if fi != expectFirst || li != expectLast { t.Fatalf("expected %v/%v, got %v/%v", expectFirst, expectLast, fi, li) } is, err := l.IsEmpty() if err != nil { t.Fatal(err) }Lines 1–500 of 1403. Load more
package wal import ( "errors" "fmt" "io/ioutil" "math/rand" "os" "strings" "sync" "sync/atomic" "testing" ) func dataStr(index uint64) string { if index%2 == 0 { return fmt.Sprintf("data-\"%d\"", index) } return fmt.Sprintf("data-'%d'", index) } func testLog(t *testing.T, opts *Options, N int) { logPath := "testlog/" + strings.Join(strings.Split(t.Name(), "/")[1:], "/") l, err := Open(logPath, opts) if err != nil { t.Fatal(err) } defer l.Close() // FirstIndex - should be zero or one, depending on allow empty. n, err := l.FirstIndex() if err != nil { t.Fatal(err) } if !l.opts.AllowEmpty { if n != 0 { t.Fatalf("expected %d, got %d", 0, n) } } else { if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } } // LastIndex - should be zero n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != 0 { t.Fatalf("expected %d, got %d", 0, n) } for i := 1; i <= N; i++ { // Write - try to append previous index, should fail err = l.Write(uint64(i-1), nil) if err != ErrOutOfOrder { t.Fatalf("expected %v, got %v", ErrOutOfOrder, err) } // Write - append next item err = l.Write(uint64(i), []byte(dataStr(uint64(i)))) if err != nil { t.Fatalf("expected %v, got %v", nil, err) } // Write - get next item data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("expected %v, got %v", nil, err) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- should fail, not found _, err = l.Read(0) if err != ErrNotFound { t.Fatalf("expected %v, got %v", ErrNotFound, err) } // Read -- read back all entries for i := 1; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- read back first half entries for i := 1; i <= N/2; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- read second third entries for i := N / 3; i <= N/3+N/3; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Read -- random access for _, v := range rand.Perm(N) { index := uint64(v + 1) data, err := l.Read(index) if err != nil { t.Fatal(err) } if dataStr(index) != string(data) { t.Fatalf("expected %v, got %v", dataStr(index), string(data)) } } // FirstIndex/LastIndex -- check valid first and last indexes n, err = l.FirstIndex() if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != uint64(N) { t.Fatalf("expected %d, got %d", N, n) } // Close -- close the log if err := l.Close(); err != nil { t.Fatal(err) } // Write - try while closed err = l.Write(1, nil) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // WriteBatch - try while closed err = l.WriteBatch(nil) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // FirstIndex - try while closed _, err = l.FirstIndex() if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // LastIndex - try while closed _, err = l.LastIndex() if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // Get - try while closed _, err = l.Read(0) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // TruncateFront - try while closed err = l.TruncateFront(0) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // TruncateBack - try while closed err = l.TruncateBack(0) if err != ErrClosed { t.Fatalf("expected %v, got %v", ErrClosed, err) } // Open -- reopen log l, err = Open(logPath, opts) if err != nil { t.Fatal(err) } defer l.Close() // Read -- read back all entries for i := 1; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d, err=%s", i, err) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // FirstIndex/LastIndex -- check valid first and last indexes n, err = l.FirstIndex() if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != uint64(N) { t.Fatalf("expected %d, got %d", N, n) } // Write -- add 50 more items for i := N + 1; i <= N+50; i++ { index := uint64(i) if err := l.Write(index, []byte(dataStr(index))); err != nil { t.Fatal(err) } data, err := l.Read(index) if err != nil { t.Fatal(err) } if string(data) != dataStr(index) { t.Fatalf("expected %v, got %v", dataStr(index), string(data)) } } N += 50 // FirstIndex/LastIndex -- check valid first and last indexes n, err = l.FirstIndex() if err != nil { t.Fatal(err) } if n != 1 { t.Fatalf("expected %d, got %d", 1, n) } n, err = l.LastIndex() if err != nil { t.Fatal(err) } if n != uint64(N) { t.Fatalf("expected %d, got %d", N, n) } // Batch -- test batch writes b := new(Batch) b.Write(1, nil) b.Write(2, nil) b.Write(3, nil) // WriteBatch -- should fail out of order err = l.WriteBatch(b) if err != ErrOutOfOrder { t.Fatalf("expected %v, got %v", ErrOutOfOrder, nil) } // Clear -- clear the batch b.Clear() // WriteBatch -- should succeed err = l.WriteBatch(b) if err != nil { t.Fatal(err) } // Write 100 entries in batches of 10 for i := 0; i < 10; i++ { for i := N + 1; i <= N+10; i++ { index := uint64(i) b.Write(index, []byte(dataStr(index))) } err = l.WriteBatch(b) if err != nil { t.Fatal(err) } N += 10 } // Read -- read back all entries for i := 1; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Write -- one entry, so the buffer might be activated err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ // Read -- one random read, so there is an opened reader data, err := l.Read(uint64(N / 2)) if err != nil { t.Fatal(err) } if string(data) != dataStr(uint64(N/2)) { t.Fatalf("expected %v, got %v", dataStr(uint64(N/2)), string(data)) } // TruncateFront -- should fail, out of range for _, i := range []int{0, N + 2} { index := uint64(i) if err = l.TruncateFront(index); err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } testFirstLast(t, l, uint64(1), uint64(N), nil) } // TruncateBack -- should fail, out of range err = l.TruncateFront(0) if err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } testFirstLast(t, l, uint64(1), uint64(N), nil) // TruncateFront -- Remove no entries if err = l.TruncateFront(1); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(1), uint64(N), nil) // TruncateFront -- Remove first 80 entries if err = l.TruncateFront(81); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(81), uint64(N), nil) // Write -- one entry, so the buffer might be activated err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ testFirstLast(t, l, uint64(81), uint64(N), nil) // Read -- one random read, so there is an opened reader data, err = l.Read(uint64(N / 2)) if err != nil { t.Fatal(err) } if string(data) != dataStr(uint64(N/2)) { t.Fatalf("expected %v, got %v", dataStr(uint64(N/2)), string(data)) } // TruncateBack -- should fail, out of range for _, i := range []int{0, 79} { index := uint64(i) if err = l.TruncateBack(index); err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } testFirstLast(t, l, uint64(81), uint64(N), nil) } // TruncateBack -- Remove no entries if err = l.TruncateBack(uint64(N)); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(81), uint64(N), nil) // TruncateBack -- Remove last 80 entries if err = l.TruncateBack(uint64(N - 80)); err != nil { t.Fatal(err) } N -= 80 testFirstLast(t, l, uint64(81), uint64(N), nil) // Read -- read back all entries for i := 81; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // Close -- close log after truncating if err = l.Close(); err != nil { t.Fatal(err) } // Open -- open log after truncating l, err = Open(logPath, opts) if err != nil { t.Fatal(err) } defer l.Close() testFirstLast(t, l, uint64(81), uint64(N), nil) // Read -- read back all entries for i := 81; i <= N; i++ { data, err := l.Read(uint64(i)) if err != nil { t.Fatalf("error while getting %d", i) } if string(data) != dataStr(uint64(i)) { t.Fatalf("expected %s, got %s", dataStr(uint64(i)), data) } } // TruncateFront -- truncate all entries but one if err = l.TruncateFront(uint64(N)); err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(N), uint64(N), nil) // Write -- write on entry err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ testFirstLast(t, l, uint64(N-1), uint64(N), nil) // TruncateBack -- truncate all entries but one if err = l.TruncateBack(uint64(N - 1)); err != nil { t.Fatal(err) } N-- testFirstLast(t, l, uint64(N), uint64(N), nil) if err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))); err != nil { t.Fatal(err) } N++ l.Sync() testFirstLast(t, l, uint64(N-1), uint64(N), nil) allowEmpty := opts != nil && opts.AllowEmpty // TruncateFront -- truncate all entries err = l.TruncateFront(uint64(N + 1)) if allowEmpty { if err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(N+1), uint64(N), nil) } else { if err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } } err = l.Write(uint64(N+1), []byte(dataStr(uint64(N+1)))) if err != nil { t.Fatal(err) } N++ if allowEmpty { testFirstLast(t, l, uint64(N), uint64(N), nil) } else { testFirstLast(t, l, uint64(N-2), uint64(N), nil) } // TruncateBack -- truncate all entries fidx, err := l.FirstIndex() if err != nil { t.Fatal(err) } if allowEmpty { if fidx != uint64(N) { t.Fatalf("expected %v, got %v", N, fidx) } } else { if fidx != uint64(N-2) { t.Fatalf("expected %v, got %v", N-2, fidx) } } err = l.TruncateBack(uint64(fidx - 1)) if allowEmpty { if err != nil { t.Fatal(err) } testFirstLast(t, l, uint64(N), uint64(N-1), nil) } else { if err != ErrOutOfRange { t.Fatalf("expected %v, got %v", ErrOutOfRange, err) } } } func testFirstLast(t *testing.T, l *Log, expectFirst, expectLast uint64, data func(index uint64) []byte, ) { t.Helper() fi, err := l.FirstIndex() if err != nil { t.Fatal(err) } li, err := l.LastIndex() if err != nil { t.Fatal(err) } if fi != expectFirst || li != expectLast { t.Fatalf("expected %v/%v, got %v/%v", expectFirst, expectLast, fi, li) } is, err := l.IsEmpty() if err != nil { t.Fatal(err) }