Skip to content

Commit d6004f2

Browse files
Add test to ensure that database packing produces sorted values (ava-labs#1560)
1 parent e17a6ca commit d6004f2

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

database/helpers_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (C) 2019-2023, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package database
5+
6+
import (
7+
"math/rand"
8+
"testing"
9+
"time"
10+
11+
"github.com/stretchr/testify/require"
12+
13+
"golang.org/x/exp/slices"
14+
15+
"github.com/ava-labs/avalanchego/utils"
16+
)
17+
18+
func TestSortednessUint64(t *testing.T) {
19+
seed := time.Now().UnixNano()
20+
t.Log("Seed: ", seed)
21+
rand := rand.New(rand.NewSource(seed)) //#nosec G404
22+
23+
ints := make([]uint64, 1024)
24+
for i := range ints {
25+
ints[i] = rand.Uint64()
26+
}
27+
slices.Sort(ints)
28+
29+
intBytes := make([][]byte, 1024)
30+
for i, val := range ints {
31+
intBytes[i] = PackUInt64(val)
32+
}
33+
require.True(t, utils.IsSortedBytes(intBytes))
34+
}
35+
36+
func TestSortednessUint32(t *testing.T) {
37+
seed := time.Now().UnixNano()
38+
t.Log("Seed: ", seed)
39+
rand := rand.New(rand.NewSource(seed)) //#nosec G404
40+
41+
ints := make([]uint32, 1024)
42+
for i := range ints {
43+
ints[i] = rand.Uint32()
44+
}
45+
slices.Sort(ints)
46+
47+
intBytes := make([][]byte, 1024)
48+
for i, val := range ints {
49+
intBytes[i] = PackUInt32(val)
50+
}
51+
require.True(t, utils.IsSortedBytes(intBytes))
52+
}

utils/sorting.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,22 @@ func SortByHash[T ~[]byte](s []T) {
3838
// Sorts a 2D byte slice.
3939
// Each byte slice is not sorted internally; the byte slices are sorted relative
4040
// to one another.
41-
func SortBytes[T ~[]byte](arr []T) {
42-
slices.SortFunc(arr, func(i, j T) bool {
41+
func SortBytes[T ~[]byte](s []T) {
42+
slices.SortFunc(s, func(i, j T) bool {
4343
return bytes.Compare(i, j) == -1
4444
})
4545
}
4646

47+
// Returns true iff the elements in [s] are sorted.
48+
func IsSortedBytes[T ~[]byte](s []T) bool {
49+
for i := 0; i < len(s)-1; i++ {
50+
if bytes.Compare(s[i], s[i+1]) == 1 {
51+
return false
52+
}
53+
}
54+
return true
55+
}
56+
4757
// Returns true iff the elements in [s] are unique and sorted.
4858
func IsSortedAndUniqueSortable[T Sortable[T]](s []T) bool {
4959
for i := 0; i < len(s)-1; i++ {
@@ -82,10 +92,10 @@ func IsSortedAndUniqueByHash[T ~[]byte](s []T) bool {
8292
}
8393

8494
// Returns true iff the elements in [s] are unique.
85-
func IsUnique[T comparable](elts []T) bool {
95+
func IsUnique[T comparable](s []T) bool {
8696
// Can't use set.Set because it'd be a circular import.
87-
asMap := make(map[T]struct{}, len(elts))
88-
for _, elt := range elts {
97+
asMap := make(map[T]struct{}, len(s))
98+
for _, elt := range s {
8999
if _, ok := asMap[elt]; ok {
90100
return false
91101
}

utils/sorting_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
package utils
55

66
import (
7+
"math/rand"
78
"testing"
9+
"time"
810

911
"github.com/stretchr/testify/require"
1012
)
@@ -57,6 +59,23 @@ func TestSortSliceSortable(t *testing.T) {
5759
require.Equal([]sortable{1, 2, 3}, s)
5860
}
5961

62+
func TestSortBytesIsSortedBytes(t *testing.T) {
63+
require := require.New(t)
64+
65+
seed := time.Now().UnixNano()
66+
t.Log("Seed: ", seed)
67+
rand := rand.New(rand.NewSource(seed)) //#nosec G404
68+
69+
slices := make([][]byte, 1024)
70+
for j := 0; j < len(slices); j++ {
71+
slices[j] = make([]byte, 32)
72+
_, _ = rand.Read(slices[j])
73+
}
74+
require.False(IsSortedBytes(slices))
75+
SortBytes(slices)
76+
require.True(IsSortedBytes(slices))
77+
}
78+
6079
func TestIsSortedAndUniqueSortable(t *testing.T) {
6180
require := require.New(t)
6281

0 commit comments

Comments
 (0)