Skip to content

Commit

Permalink
sync -- add workheap test (ava-labs#1879)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Laine authored Aug 21, 2023
1 parent 7ec5bb3 commit f47e1be
Showing 1 changed file with 138 additions and 2 deletions.
140 changes: 138 additions & 2 deletions x/sync/workheap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
package sync

import (
"math/rand"
"testing"

"github.com/ava-labs/avalanchego/utils/maybe"
"time"

"github.com/stretchr/testify/require"

"golang.org/x/exp/slices"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/maybe"
)

// Tests heap.Interface methods Push, Pop, Swap, Len, Less.
Expand Down Expand Up @@ -271,3 +275,135 @@ func Test_WorkHeap_Merge_Insert(t *testing.T) {
syncHeap.MergeInsert(&workItem{start: maybe.Some([]byte{63}), end: maybe.Some([]byte{127}), priority: lowPriority})
require.Equal(t, 1, syncHeap.Len())
}

func TestWorkHeapMergeInsertRandom(t *testing.T) {
var (
require = require.New(t)
seed = time.Now().UnixNano()
rand = rand.New(rand.NewSource(seed)) // #nosec G404
numRanges = 1_000
bounds = [][]byte{}
rootID = ids.GenerateTestID()
)
t.Logf("seed: %d", seed)

// Create start and end bounds
for i := 0; i < numRanges; i++ {
bound := make([]byte, 32)
_, _ = rand.Read(bound)
bounds = append(bounds, bound)
}
utils.SortBytes(bounds)

// Note that start < end for all ranges.
// It is possible but extremely unlikely that
// two elements of [bounds] are equal.
ranges := []workItem{}
for i := 0; i < numRanges/2; i++ {
start := bounds[i*2]
end := bounds[i*2+1]
ranges = append(ranges, workItem{
start: maybe.Some(start),
end: maybe.Some(end),
priority: lowPriority,
// Note they all share the same root ID.
localRootID: rootID,
})
}
// Set beginning of first range to Nothing.
ranges[0].start = maybe.Nothing[[]byte]()
// Set end of last range to Nothing.
ranges[len(ranges)-1].end = maybe.Nothing[[]byte]()

setup := func() *workHeap {
// Insert all the ranges into the heap.
h := newWorkHeap()
for i, r := range ranges {
require.Equal(i, h.Len())
rCopy := r
h.MergeInsert(&rCopy)
}
return h
}

{
// Case 1: Merging an item with the range before and after
h := setup()
// Keep merging ranges until there's only one range left.
for i := 0; i < len(ranges)-1; i++ {
// Merge ranges[i] with ranges[i+1]
h.MergeInsert(&workItem{
start: ranges[i].end,
end: ranges[i+1].start,
priority: lowPriority,
localRootID: rootID,
})
require.Equal(len(ranges)-i-1, h.Len())
}
got := h.GetWork()
require.True(got.start.IsNothing())
require.True(got.end.IsNothing())
}

{
// Case 2: Merging an item with the range before
h := setup()
for i := 0; i < len(ranges)-1; i++ {
// Extend end of ranges[i]
newEnd := slices.Clone(ranges[i].end.Value())
newEnd = append(newEnd, 0)
h.MergeInsert(&workItem{
start: ranges[i].end,
end: maybe.Some(newEnd),
priority: lowPriority,
localRootID: rootID,
})

// Shouldn't cause number of elements to change
require.Equal(len(ranges), h.Len())

start := ranges[i].start
if i == 0 {
start = maybe.Nothing[[]byte]()
}
// Make sure end is updated
got, ok := h.sortedItems.Get(&heapItem{
workItem: &workItem{
start: start,
},
})
require.True(ok)
require.Equal(newEnd, got.workItem.end.Value())
}
}

{
// Case 3: Merging an item with the range after
h := setup()
for i := 1; i < len(ranges); i++ {
// Extend start of ranges[i]
newStartBytes := slices.Clone(ranges[i].start.Value())
newStartBytes = newStartBytes[:len(newStartBytes)-1]
newStart := maybe.Some(newStartBytes)

h.MergeInsert(&workItem{
start: newStart,
end: ranges[i].start,
priority: lowPriority,
localRootID: rootID,
})

// Shouldn't cause number of elements to change
require.Equal(len(ranges), h.Len())

// Make sure start is updated
got, ok := h.sortedItems.Get(&heapItem{
workItem: &workItem{
start: newStart,
},
})
require.True(ok)
require.Equal(newStartBytes, got.workItem.start.Value())
}
}
}

0 comments on commit f47e1be

Please sign in to comment.