Fix tracker.Restore test, add CT workflow #5

Merged
roysc merged 4 commits from fix-restore-test into main 2024-07-08 07:19:02 +00:00
4 changed files with 43 additions and 9 deletions

23
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,23 @@
name: Test
on:
pull_request:
branches: '*'
push:
branches:
- main
jobs:
test:
name: Run unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v3
with:
go-version-file: go.mod
check-latest: true
- name: Run unit tests
run: |
go test -v -p 1 ./...
go test -v ./tracker -count 20

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/cerc-io/eth-iterator-utils module github.com/cerc-io/eth-iterator-utils
go 1.19 go 1.21
require ( require (
github.com/cerc-io/eth-testing v0.4.0 github.com/cerc-io/eth-testing v0.4.0

View File

@ -189,7 +189,7 @@ func (tr *TrackerImpl) Restore(makeIterator iter.IteratorConstructor) (
} }
} }
// force the lower bound path to an even length (required by geth API/HexToKeyBytes) // force the lower bound path to an even length (required by NodeIterator constructor)
if len(recoveredPath)&1 == 1 { if len(recoveredPath)&1 == 1 {
// to avoid skipped nodes, we must rewind by one index // to avoid skipped nodes, we must rewind by one index
recoveredPath = rewindPath(recoveredPath) recoveredPath = rewindPath(recoveredPath)
@ -246,6 +246,7 @@ func (it *Iterator) Next(descend bool) bool {
return ret return ret
} }
// Bounds returns the bounds of the underlying PrefixBoundIterator, if any
func (it *Iterator) Bounds() ([]byte, []byte) { func (it *Iterator) Bounds() ([]byte, []byte) {
if impl, ok := it.NodeIterator.(*iter.PrefixBoundIterator); ok { if impl, ok := it.NodeIterator.(*iter.PrefixBoundIterator); ok {
return impl.Bounds() return impl.Bounds()
@ -253,20 +254,21 @@ func (it *Iterator) Bounds() ([]byte, []byte) {
return nil, nil return nil, nil
} }
// Rewinds to the path of the previous (pre-order) node: // Returns the path, rewound to the previous (pre-order) node:
// If path is the root (empty) or a leaf path, it's returned.
// If the last byte of the path is zero, pops it (e.g. [1 0] => [1]). // If the last byte of the path is zero, pops it (e.g. [1 0] => [1]).
// Otherwise, decrements it and pads with 0xF to 64 bytes (e.g. [1] => [0 f f f ...]). // Otherwise, decrements it and pads with 0xF to 64 bytes (e.g. [1] => [0 f f f ...]).
// The passed slice is not modified. // The passed slice is not modified.
func rewindPath(path []byte) []byte { func rewindPath(path []byte) []byte {
if len(path) == 0 { if len(path) == 0 || path[len(path)-1] == 0x10 {
return path return path
} }
if path[len(path)-1] == 0 { if path[len(path)-1] == 0 {
return path[:len(path)-1] return path[:len(path)-1]
} }
path[len(path)-1]--
padded := make([]byte, 64) padded := make([]byte, 64)
i := copy(padded, path) i := copy(padded, path)
padded[len(path)-1]--
for ; i < len(padded); i++ { for ; i < len(padded); i++ {
padded[i] = 0xf padded[i] = 0xf
} }

View File

@ -25,7 +25,6 @@ func TestTracker(t *testing.T) {
tr := tracker.New(recoveryFile, NumIters) tr := tracker.New(recoveryFile, NumIters)
defer tr.CloseAndSave() defer tr.CloseAndSave()
var prevPath []byte
count := 0 count := 0
nodeit, err := tree.NodeIterator(nil) nodeit, err := tree.NodeIterator(nil)
if err != nil { if err != nil {
@ -33,9 +32,9 @@ func TestTracker(t *testing.T) {
} }
for it := tr.Tracked(nodeit); it.Next(true); { for it := tr.Tracked(nodeit); it.Next(true); {
if count == interrupt { if count == interrupt {
return prevPath // tracker rewinds one node to prevent gaps t.Logf("interrupting at: i=%d path=%v", count, it.Path())
return it.Path()
} }
prevPath = it.Path()
count++ count++
} }
return nil return nil
@ -58,9 +57,19 @@ func TestTracker(t *testing.T) {
if uint(len(its)) != NumIters { if uint(len(its)) != NumIters {
t.Fatalf("expected to restore %d iterators, got %d", NumIters, len(its)) t.Fatalf("expected to restore %d iterators, got %d", NumIters, len(its))
} }
if !its[0].Next(true) {
t.Fatal("iterator ends prematurely after restore")
}
if !bytes.Equal(failedAt, its[0].Path()) {
// Due to the constraint that NodeIterator can only be initialized with an even-length path,
// we sometimes rewind an extra node when restoring (e.g. [1 2 0] => [1 2]).
if !its[0].Next(true) {
t.Fatal("iterator ends prematurely after restore")
}
if !bytes.Equal(failedAt, its[0].Path()) { if !bytes.Equal(failedAt, its[0].Path()) {
t.Fatalf("iterator restored to wrong position: expected %v, got %v", failedAt, its[0].Path()) t.Fatalf("iterator restored to wrong position: expected %v, got %v", failedAt, its[0].Path())
} }
}
if fileExists(recoveryFile) { if fileExists(recoveryFile) {
t.Fatal("recovery file wasn't removed") t.Fatal("recovery file wasn't removed")