11package statedb
22
33import (
4+ "bytes"
45 "cmp"
6+ "fmt"
57 "iter"
8+ "math/rand"
9+ "reflect"
610 "strings"
711 "testing"
812 "testing/quick"
@@ -13,6 +17,18 @@ import (
1317
1418var quickConfig = & quick.Config {
1519 MaxCount : 5000 ,
20+
21+ // Make 1-8 byte long strings as input data. Keep the strings shorter
22+ // than the default quick value generation to hit the more interesting cases
23+ // often.
24+ Values : func (args []reflect.Value , rand * rand.Rand ) {
25+ for i := range args {
26+ numBytes := 1 + rand .Intn (8 )
27+ bs := make ([]byte , numBytes )
28+ rand .Read (bs )
29+ args [i ] = reflect .ValueOf (string (bs ))
30+ }
31+ },
1632}
1733
1834// Use an object with strings for both primary and secondary
@@ -23,11 +39,8 @@ type quickObj struct {
2339 A , B string
2440}
2541
26- func (q quickObj ) getA () string {
27- return q .A
28- }
29- func (q quickObj ) getB () string {
30- return q .B
42+ func (q quickObj ) String () string {
43+ return fmt .Sprintf ("%x %x" , []byte (q .A ), []byte (q .B ))
3144}
3245
3346var (
@@ -52,13 +65,27 @@ var (
5265 }
5366)
5467
55- func isOrdered [ A cmp. Ordered , B any ] (t * testing.T , it iter.Seq2 [A , B ]) bool {
56- var prev A
68+ func isOrdered (t * testing.T , aFirst bool , it iter.Seq2 [quickObj , Revision ]) bool {
69+ var prev quickObj
5770 for a := range it {
58- if cmp .Compare (a , prev ) < 0 {
59- t .Logf ("isOrdered: %#v < %#v!" , a , prev )
60- return false
71+ if aFirst {
72+ if ret := cmp .Compare (a .A , prev .A ); ret < 0 {
73+ t .Logf ("isOrdered(A): %s < %s!" , a , prev )
74+ return false
75+ } else if ret == 0 && cmp .Compare (a .B , prev .B ) < 0 {
76+ t .Logf ("isOrdered(B): %s < %s!" , a , prev )
77+ return false
78+ }
79+ } else {
80+ if ret := cmp .Compare (a .B , prev .B ); ret < 0 {
81+ t .Logf ("isOrdered(B): %s < %s!" , a , prev )
82+ return false
83+ } else if ret == 0 && cmp .Compare (a .A , prev .A ) < 0 {
84+ t .Logf ("isOrdered(A): %s < %s!" , a , prev )
85+ return false
86+ }
6187 }
88+
6289 prev = a
6390 }
6491 return true
@@ -106,15 +133,15 @@ func TestDB_Quick(t *testing.T) {
106133 // Check queries against the primary index
107134 //
108135
109- if numExpected != seqLen (Map ( table .All (rtxn ), quickObj . getA )) {
136+ if numExpected != seqLen (table .All (rtxn )) {
110137 t .Logf ("All() via aIndex wrong length" )
111138 return false
112139 }
113- if numExpected != seqLen (Map ( table .Prefix (rtxn , aIndex .Query ("" )), quickObj . getA )) {
140+ if numExpected != seqLen (table .Prefix (rtxn , aIndex .Query ("" ))) {
114141 t .Logf ("Prefix() via aIndex wrong length" )
115142 return false
116143 }
117- if numExpected != seqLen (Map ( table .LowerBound (rtxn , aIndex .Query ("" )), quickObj . getA )) {
144+ if numExpected != seqLen (table .LowerBound (rtxn , aIndex .Query ("" ))) {
118145 t .Logf ("LowerBound() via aIndex wrong length" )
119146 return false
120147 }
@@ -151,20 +178,20 @@ func TestDB_Quick(t *testing.T) {
151178 for anyObj := range anyObjs {
152179 obj := anyObj .(quickObj )
153180 if cmp .Compare (obj .A , a ) < 0 {
154- t .Logf ("AnyTable.LowerBound() order wrong" )
181+ t .Logf ("AnyTable.LowerBound(%x ) order wrong: %x < %x" , [] byte ( a ), [] byte ( obj . A ), [] byte ( a ) )
155182 return false
156183 }
157184 }
158185
159- if ! isOrdered (t , Map ( table .All (rtxn ), quickObj . getA )) {
186+ if ! isOrdered (t , true , table .All (rtxn )) {
160187 t .Logf ("All() wrong order" )
161188 return false
162189 }
163- if ! isOrdered (t , Map ( table .Prefix (rtxn , aIndex .Query ("" )), quickObj . getA )) {
190+ if ! isOrdered (t , true , table .Prefix (rtxn , aIndex .Query ("" ))) {
164191 t .Logf ("Prefix() via aIndex wrong order" )
165192 return false
166193 }
167- if ! isOrdered (t , Map ( table .LowerBound (rtxn , aIndex .Query ("" )), quickObj . getA )) {
194+ if ! isOrdered (t , true , table .LowerBound (rtxn , aIndex .Query ("" ))) {
168195 t .Logf ("LowerBound() via aIndex wrong order" )
169196 return false
170197 }
@@ -178,6 +205,7 @@ func TestDB_Quick(t *testing.T) {
178205 t .Logf ("Prefix() via bIndex wrong length" )
179206 return false
180207 }
208+
181209 if numExpected != seqLen (table .LowerBound (rtxn , bIndex .Query ("" ))) {
182210 t .Logf ("LowerBound() via bIndex wrong length" )
183211 return false
@@ -187,7 +215,7 @@ func TestDB_Quick(t *testing.T) {
187215 // not be the one that we just inserted.
188216 obj , _ , found = table .Get (rtxn , bIndex .Query (b ))
189217 if ! found || obj .B != b {
190- t .Logf ("Get() via bIndex not found or wrong B" )
218+ t .Logf ("Get(%q ) via bIndex not found (%v) or wrong B (%q vs %q)" , b , found , obj . B , b )
191219 return false
192220 }
193221
@@ -224,7 +252,7 @@ func TestDB_Quick(t *testing.T) {
224252 for anyObj := range anyObjs {
225253 obj := anyObj .(quickObj )
226254 if ! strings .HasPrefix (obj .B , b ) {
227- t .Logf ("AnyTable.Prefix() via bIndex has wrong prefix" )
255+ t .Logf ("AnyTable.Prefix() via bIndex has wrong prefix: %q vs %q" , obj . B , b )
228256 return false
229257 }
230258 }
@@ -253,17 +281,75 @@ func TestDB_Quick(t *testing.T) {
253281 }
254282
255283 // Iterating over the secondary index returns the objects in order
256- // defined by the "B" key.
257- if ! isOrdered (t , Map (table .Prefix (rtxn , bIndex .Query ("" )), quickObj .getB )) {
258- t .Logf ("Prefix() via bIndex has wrong order" )
284+ // defined by the "B" key first and then by the "A" key.
285+ if ! isOrdered (t , false , table .Prefix (rtxn , bIndex .Query ("" ))) {
286+ t .Logf ("Prefix() via bIndex wrong order" )
287+ rtxn .getTxn ().mustIndexReadTxn (table , table .indexPos ("b" )).PrintTree ()
259288 return false
260289 }
261- if ! isOrdered (t , Map ( table .LowerBound (rtxn , bIndex .Query ("" )), quickObj . getB )) {
262- t .Logf ("LowerBound () via bIndex has wrong order" )
290+ if ! isOrdered (t , false , table .LowerBound (rtxn , bIndex .Query ("" ))) {
291+ t .Logf ("Prefix () via bIndex wrong order" )
263292 return false
264293 }
265294 return true
266295 }
267296
268297 require .NoError (t , quick .Check (check , quickConfig ))
269298}
299+
300+ func Test_Quick_nonUniqueKey (t * testing.T ) {
301+ check := func (p1 , s1 , p2 , s2 []byte ) bool {
302+ key1 := encodeNonUniqueKey (p1 , s1 )
303+ expectedLen := encodedLength (p1 ) + 1 + encodedLength (s1 ) + 2
304+ minLen := len (p1 ) + 1 + len (s1 ) + 2
305+ if expectedLen < minLen {
306+ t .Logf ("expected length too short (%d), must be >= %d" , expectedLen , minLen )
307+ return false
308+ }
309+ if len (key1 ) != expectedLen {
310+ t .Logf ("length mismatch, expected %d, got %d" , expectedLen , len (key1 ))
311+ return false
312+ }
313+
314+ nuk1 := nonUniqueKey (key1 )
315+ if len (nuk1 .encodedPrimary ()) < len (p1 ) {
316+ t .Logf ("encodedPrimary() length (%d) shorter than original (%d)" , len (nuk1 .encodedPrimary ()), len (p1 ))
317+ return false
318+ }
319+ if len (nuk1 .encodedPrimary ()) != encodedLength (p1 ) {
320+ t .Logf ("encodedPrimary() length (%d) does not match encodedLength() (%d)" , len (nuk1 .encodedPrimary ()), len (p1 ))
321+ return false
322+ }
323+ if len (nuk1 .encodedSecondary ()) < len (s1 ) {
324+ t .Logf ("encodedSecondary() length (%d) shorter than original (%d)" , len (nuk1 .encodedSecondary ()), len (s1 ))
325+ }
326+ if len (nuk1 .encodedSecondary ()) != encodedLength (s1 ) {
327+ t .Logf ("encodedSecondary() length (%d) does not match encodedLength() (%d)" , len (nuk1 .encodedSecondary ()), len (s1 ))
328+ return false
329+ }
330+
331+ // Do another key and check that ordering is preserved.
332+ key2 := encodeNonUniqueKey (p2 , s2 )
333+ scmp := bytes .Compare (s1 , s2 )
334+ pcmp := bytes .Compare (p1 , p2 )
335+ kcmp := bytes .Compare (key1 , key2 )
336+
337+ if (scmp == 0 && pcmp != kcmp ) /* secondary key matches, primary key determines order */ ||
338+ (scmp != 0 && scmp != kcmp ) /* secondary key determines order */ {
339+ t .Logf ("ordering not preserved: key1=%v key2=%v, p1=%v, s1=%v, p2=%v, s2=%v" , key1 , key2 , p1 , s1 , p2 , s2 )
340+ return false
341+ }
342+ return true
343+ }
344+ require .NoError (t , quick .Check (check , & quick.Config {
345+ MaxCount : 50000 ,
346+ Values : func (args []reflect.Value , rand * rand.Rand ) {
347+ for i := range args {
348+ numBytes := 1 + rand .Intn (8 )
349+ bs := make ([]byte , numBytes )
350+ rand .Read (bs )
351+ args [i ] = reflect .ValueOf (bs )
352+ }
353+ },
354+ }))
355+ }
0 commit comments