github.com/cilium/statedb@v0.3.2/part/quick_test.go (about) 1 package part_test 2 3 import ( 4 "slices" 5 "testing" 6 "testing/quick" 7 8 "github.com/cilium/statedb/part" 9 "github.com/stretchr/testify/require" 10 ) 11 12 var quickConfig = &quick.Config{ 13 // Use a higher count in order to hit the Node256 cases. 14 MaxCount: 2000, 15 } 16 17 func TestQuick_InsertGetPrefix(t *testing.T) { 18 tree := part.New[string]() 19 insert := func(key, value string) any { 20 _, _, tree = tree.Insert([]byte(key), value) 21 return value 22 } 23 24 get := func(key, value string) any { 25 val, _, _ := tree.Get([]byte(key)) 26 if val != value { 27 return val 28 } 29 30 iter, _ := tree.Prefix([]byte(key)) 31 _, v, _ := iter.Next() 32 return v 33 } 34 35 require.NoError(t, 36 quick.CheckEqual(insert, get, quickConfig), 37 ) 38 } 39 40 func TestQuick_IteratorReuse(t *testing.T) { 41 tree := part.New[string]() 42 43 iterate := func(key, value string, cloneFirst bool) bool { 44 _, _, tree = tree.Insert([]byte(key), value) 45 v, _, ok := tree.Get([]byte(key)) 46 if !ok || value != v { 47 return false 48 } 49 50 prefixIter, _ := tree.Prefix([]byte(key)) 51 iterators := []*part.Iterator[string]{ 52 tree.LowerBound([]byte(key)), 53 prefixIter, 54 } 55 56 for _, iter := range iterators { 57 iter2 := iter.Clone() 58 59 collect := func(it *part.Iterator[string]) (out []string) { 60 for k, v, ok := it.Next(); ok; k, v, ok = it.Next() { 61 out = append(out, string(k)+"="+v) 62 } 63 return 64 } 65 66 var fst, snd []string 67 if cloneFirst { 68 snd = collect(iter2) 69 fst = collect(iter) 70 } else { 71 fst = collect(iter) 72 snd = collect(iter2) 73 } 74 75 if !slices.Equal(fst, snd) { 76 return false 77 } 78 } 79 return true 80 } 81 82 require.NoError(t, 83 quick.Check(iterate, quickConfig), 84 ) 85 } 86 87 func TestQuick_Delete(t *testing.T) { 88 tree := part.New[string]() 89 90 do := func(key, value string, delete bool) bool { 91 _, _, tree = tree.Insert([]byte(key), value) 92 treeAfterInsert := tree 93 v, watch, ok := tree.Get([]byte(key)) 94 if !ok || v != value { 95 t.Logf("value not in tree after insert") 96 return false 97 } 98 99 // delete some of the time to construct different variations of trees. 100 if delete { 101 _, _, tree = tree.Delete([]byte(key)) 102 _, _, ok := tree.Get([]byte(key)) 103 if ok { 104 t.Logf("value exists after delete") 105 return false 106 } 107 108 _, _, ok = treeAfterInsert.Get([]byte(key)) 109 if !ok { 110 t.Logf("value deleted from original") 111 } 112 113 // Check that watch channel closed. 114 select { 115 case <-watch: 116 default: 117 t.Logf("watch channel not closed") 118 return false 119 } 120 } 121 return true 122 } 123 124 require.NoError(t, quick.Check(do, quickConfig)) 125 } 126 127 func TestQuick_ClosedWatch(t *testing.T) { 128 tree := part.New[string]() 129 insert := func(key, value string) bool { 130 _, _, tree = tree.Insert([]byte(key), value) 131 treeAfterInsert := tree 132 133 val, watch, ok := tree.Get([]byte(key)) 134 if !ok { 135 return false 136 } 137 if val != value { 138 return false 139 } 140 141 select { 142 case <-watch: 143 return false 144 default: 145 } 146 147 // Changing the key makes the channel close. 148 _, _, tree = tree.Insert([]byte(key), "x") 149 select { 150 case <-watch: 151 default: 152 t.Logf("channel not closed!") 153 return false 154 } 155 156 // Original tree unaffected. 157 val, _, ok = treeAfterInsert.Get([]byte(key)) 158 if !ok || val != value { 159 t.Logf("original changed!") 160 return false 161 } 162 163 val, _, ok = tree.Get([]byte(key)) 164 if !ok || val != "x" { 165 t.Logf("new tree does not have x!") 166 return false 167 } 168 169 return true 170 } 171 172 require.NoError(t, quick.Check(insert, quickConfig)) 173 }