xref: /f-stack/app/redis-5.0.5/tests/unit/type/set.tcl (revision 572c4311)
1start_server {
2    tags {"set"}
3    overrides {
4        "set-max-intset-entries" 512
5    }
6} {
7    proc create_set {key entries} {
8        r del $key
9        foreach entry $entries { r sadd $key $entry }
10    }
11
12    test {SADD, SCARD, SISMEMBER, SMEMBERS basics - regular set} {
13        create_set myset {foo}
14        assert_encoding hashtable myset
15        assert_equal 1 [r sadd myset bar]
16        assert_equal 0 [r sadd myset bar]
17        assert_equal 2 [r scard myset]
18        assert_equal 1 [r sismember myset foo]
19        assert_equal 1 [r sismember myset bar]
20        assert_equal 0 [r sismember myset bla]
21        assert_equal {bar foo} [lsort [r smembers myset]]
22    }
23
24    test {SADD, SCARD, SISMEMBER, SMEMBERS basics - intset} {
25        create_set myset {17}
26        assert_encoding intset myset
27        assert_equal 1 [r sadd myset 16]
28        assert_equal 0 [r sadd myset 16]
29        assert_equal 2 [r scard myset]
30        assert_equal 1 [r sismember myset 16]
31        assert_equal 1 [r sismember myset 17]
32        assert_equal 0 [r sismember myset 18]
33        assert_equal {16 17} [lsort [r smembers myset]]
34    }
35
36    test {SADD against non set} {
37        r lpush mylist foo
38        assert_error WRONGTYPE* {r sadd mylist bar}
39    }
40
41    test "SADD a non-integer against an intset" {
42        create_set myset {1 2 3}
43        assert_encoding intset myset
44        assert_equal 1 [r sadd myset a]
45        assert_encoding hashtable myset
46    }
47
48    test "SADD an integer larger than 64 bits" {
49        create_set myset {213244124402402314402033402}
50        assert_encoding hashtable myset
51        assert_equal 1 [r sismember myset 213244124402402314402033402]
52    }
53
54    test "SADD overflows the maximum allowed integers in an intset" {
55        r del myset
56        for {set i 0} {$i < 512} {incr i} { r sadd myset $i }
57        assert_encoding intset myset
58        assert_equal 1 [r sadd myset 512]
59        assert_encoding hashtable myset
60    }
61
62    test {Variadic SADD} {
63        r del myset
64        assert_equal 3 [r sadd myset a b c]
65        assert_equal 2 [r sadd myset A a b c B]
66        assert_equal [lsort {A a b c B}] [lsort [r smembers myset]]
67    }
68
69    test "Set encoding after DEBUG RELOAD" {
70        r del myintset myhashset mylargeintset
71        for {set i 0} {$i <  100} {incr i} { r sadd myintset $i }
72        for {set i 0} {$i < 1280} {incr i} { r sadd mylargeintset $i }
73        for {set i 0} {$i <  256} {incr i} { r sadd myhashset [format "i%03d" $i] }
74        assert_encoding intset myintset
75        assert_encoding hashtable mylargeintset
76        assert_encoding hashtable myhashset
77
78        r debug reload
79        assert_encoding intset myintset
80        assert_encoding hashtable mylargeintset
81        assert_encoding hashtable myhashset
82    }
83
84    test {SREM basics - regular set} {
85        create_set myset {foo bar ciao}
86        assert_encoding hashtable myset
87        assert_equal 0 [r srem myset qux]
88        assert_equal 1 [r srem myset foo]
89        assert_equal {bar ciao} [lsort [r smembers myset]]
90    }
91
92    test {SREM basics - intset} {
93        create_set myset {3 4 5}
94        assert_encoding intset myset
95        assert_equal 0 [r srem myset 6]
96        assert_equal 1 [r srem myset 4]
97        assert_equal {3 5} [lsort [r smembers myset]]
98    }
99
100    test {SREM with multiple arguments} {
101        r del myset
102        r sadd myset a b c d
103        assert_equal 0 [r srem myset k k k]
104        assert_equal 2 [r srem myset b d x y]
105        lsort [r smembers myset]
106    } {a c}
107
108    test {SREM variadic version with more args needed to destroy the key} {
109        r del myset
110        r sadd myset 1 2 3
111        r srem myset 1 2 3 4 5 6 7 8
112    } {3}
113
114    foreach {type} {hashtable intset} {
115        for {set i 1} {$i <= 5} {incr i} {
116            r del [format "set%d" $i]
117        }
118        for {set i 0} {$i < 200} {incr i} {
119            r sadd set1 $i
120            r sadd set2 [expr $i+195]
121        }
122        foreach i {199 195 1000 2000} {
123            r sadd set3 $i
124        }
125        for {set i 5} {$i < 200} {incr i} {
126            r sadd set4 $i
127        }
128        r sadd set5 0
129
130        # To make sure the sets are encoded as the type we are testing -- also
131        # when the VM is enabled and the values may be swapped in and out
132        # while the tests are running -- an extra element is added to every
133        # set that determines its encoding.
134        set large 200
135        if {$type eq "hashtable"} {
136            set large foo
137        }
138
139        for {set i 1} {$i <= 5} {incr i} {
140            r sadd [format "set%d" $i] $large
141        }
142
143        test "Generated sets must be encoded as $type" {
144            for {set i 1} {$i <= 5} {incr i} {
145                assert_encoding $type [format "set%d" $i]
146            }
147        }
148
149        test "SINTER with two sets - $type" {
150            assert_equal [list 195 196 197 198 199 $large] [lsort [r sinter set1 set2]]
151        }
152
153        test "SINTERSTORE with two sets - $type" {
154            r sinterstore setres set1 set2
155            assert_encoding $type setres
156            assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]]
157        }
158
159        test "SINTERSTORE with two sets, after a DEBUG RELOAD - $type" {
160            r debug reload
161            r sinterstore setres set1 set2
162            assert_encoding $type setres
163            assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]]
164        }
165
166        test "SUNION with two sets - $type" {
167            set expected [lsort -uniq "[r smembers set1] [r smembers set2]"]
168            assert_equal $expected [lsort [r sunion set1 set2]]
169        }
170
171        test "SUNIONSTORE with two sets - $type" {
172            r sunionstore setres set1 set2
173            assert_encoding $type setres
174            set expected [lsort -uniq "[r smembers set1] [r smembers set2]"]
175            assert_equal $expected [lsort [r smembers setres]]
176        }
177
178        test "SINTER against three sets - $type" {
179            assert_equal [list 195 199 $large] [lsort [r sinter set1 set2 set3]]
180        }
181
182        test "SINTERSTORE with three sets - $type" {
183            r sinterstore setres set1 set2 set3
184            assert_equal [list 195 199 $large] [lsort [r smembers setres]]
185        }
186
187        test "SUNION with non existing keys - $type" {
188            set expected [lsort -uniq "[r smembers set1] [r smembers set2]"]
189            assert_equal $expected [lsort [r sunion nokey1 set1 set2 nokey2]]
190        }
191
192        test "SDIFF with two sets - $type" {
193            assert_equal {0 1 2 3 4} [lsort [r sdiff set1 set4]]
194        }
195
196        test "SDIFF with three sets - $type" {
197            assert_equal {1 2 3 4} [lsort [r sdiff set1 set4 set5]]
198        }
199
200        test "SDIFFSTORE with three sets - $type" {
201            r sdiffstore setres set1 set4 set5
202            # When we start with intsets, we should always end with intsets.
203            if {$type eq {intset}} {
204                assert_encoding intset setres
205            }
206            assert_equal {1 2 3 4} [lsort [r smembers setres]]
207        }
208    }
209
210    test "SDIFF with first set empty" {
211        r del set1 set2 set3
212        r sadd set2 1 2 3 4
213        r sadd set3 a b c d
214        r sdiff set1 set2 set3
215    } {}
216
217    test "SDIFF with same set two times" {
218        r del set1
219        r sadd set1 a b c 1 2 3 4 5 6
220        r sdiff set1 set1
221    } {}
222
223    test "SDIFF fuzzing" {
224        for {set j 0} {$j < 100} {incr j} {
225            unset -nocomplain s
226            array set s {}
227            set args {}
228            set num_sets [expr {[randomInt 10]+1}]
229            for {set i 0} {$i < $num_sets} {incr i} {
230                set num_elements [randomInt 100]
231                r del set_$i
232                lappend args set_$i
233                while {$num_elements} {
234                    set ele [randomValue]
235                    r sadd set_$i $ele
236                    if {$i == 0} {
237                        set s($ele) x
238                    } else {
239                        unset -nocomplain s($ele)
240                    }
241                    incr num_elements -1
242                }
243            }
244            set result [lsort [r sdiff {*}$args]]
245            assert_equal $result [lsort [array names s]]
246        }
247    }
248
249    test "SINTER against non-set should throw error" {
250        r set key1 x
251        assert_error "WRONGTYPE*" {r sinter key1 noset}
252    }
253
254    test "SUNION against non-set should throw error" {
255        r set key1 x
256        assert_error "WRONGTYPE*" {r sunion key1 noset}
257    }
258
259    test "SINTER should handle non existing key as empty" {
260        r del set1 set2 set3
261        r sadd set1 a b c
262        r sadd set2 b c d
263        r sinter set1 set2 set3
264    } {}
265
266    test "SINTER with same integer elements but different encoding" {
267        r del set1 set2
268        r sadd set1 1 2 3
269        r sadd set2 1 2 3 a
270        r srem set2 a
271        assert_encoding intset set1
272        assert_encoding hashtable set2
273        lsort [r sinter set1 set2]
274    } {1 2 3}
275
276    test "SINTERSTORE against non existing keys should delete dstkey" {
277        r set setres xxx
278        assert_equal 0 [r sinterstore setres foo111 bar222]
279        assert_equal 0 [r exists setres]
280    }
281
282    test "SUNIONSTORE against non existing keys should delete dstkey" {
283        r set setres xxx
284        assert_equal 0 [r sunionstore setres foo111 bar222]
285        assert_equal 0 [r exists setres]
286    }
287
288    foreach {type contents} {hashtable {a b c} intset {1 2 3}} {
289        test "SPOP basics - $type" {
290            create_set myset $contents
291            assert_encoding $type myset
292            assert_equal $contents [lsort [list [r spop myset] [r spop myset] [r spop myset]]]
293            assert_equal 0 [r scard myset]
294        }
295
296        test "SPOP with <count>=1 - $type" {
297            create_set myset $contents
298            assert_encoding $type myset
299            assert_equal $contents [lsort [list [r spop myset 1] [r spop myset 1] [r spop myset 1]]]
300            assert_equal 0 [r scard myset]
301        }
302
303        test "SRANDMEMBER - $type" {
304            create_set myset $contents
305            unset -nocomplain myset
306            array set myset {}
307            for {set i 0} {$i < 100} {incr i} {
308                set myset([r srandmember myset]) 1
309            }
310            assert_equal $contents [lsort [array names myset]]
311        }
312    }
313
314    foreach {type contents} {
315        hashtable {a b c d e f g h i j k l m n o p q r s t u v w x y z}
316        intset {1 10 11 12 13 14 15 16 17 18 19 2 20 21 22 23 24 25 26 3 4 5 6 7 8 9}
317    } {
318        test "SPOP with <count>" {
319            create_set myset $contents
320            assert_encoding $type myset
321            assert_equal $contents [lsort [concat [r spop myset 11] [r spop myset 9] [r spop myset 0] [r spop myset 4] [r spop myset 1] [r spop myset 0] [r spop myset 1] [r spop myset 0]]]
322            assert_equal 0 [r scard myset]
323        }
324    }
325
326    # As seen in intsetRandomMembers
327    test "SPOP using integers, testing Knuth's and Floyd's algorithm" {
328        create_set myset {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
329        assert_encoding intset myset
330        assert_equal 20 [r scard myset]
331        r spop myset 1
332        assert_equal 19 [r scard myset]
333        r spop myset 2
334        assert_equal 17 [r scard myset]
335        r spop myset 3
336        assert_equal 14 [r scard myset]
337        r spop myset 10
338        assert_equal 4 [r scard myset]
339        r spop myset 10
340        assert_equal 0 [r scard myset]
341        r spop myset 1
342        assert_equal 0 [r scard myset]
343    } {}
344
345    test "SPOP using integers with Knuth's algorithm" {
346        r spop nonexisting_key 100
347    } {}
348
349    test "SPOP new implementation: code path #1" {
350        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
351        create_set myset $content
352        set res [r spop myset 30]
353        assert {[lsort $content] eq [lsort $res]}
354    }
355
356    test "SPOP new implementation: code path #2" {
357        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
358        create_set myset $content
359        set res [r spop myset 2]
360        assert {[llength $res] == 2}
361        assert {[r scard myset] == 18}
362        set union [concat [r smembers myset] $res]
363        assert {[lsort $union] eq [lsort $content]}
364    }
365
366    test "SPOP new implementation: code path #3" {
367        set content {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
368        create_set myset $content
369        set res [r spop myset 18]
370        assert {[llength $res] == 18}
371        assert {[r scard myset] == 2}
372        set union [concat [r smembers myset] $res]
373        assert {[lsort $union] eq [lsort $content]}
374    }
375
376    test "SRANDMEMBER with <count> against non existing key" {
377        r srandmember nonexisting_key 100
378    } {}
379
380    foreach {type contents} {
381        hashtable {
382            1 5 10 50 125 50000 33959417 4775547 65434162
383            12098459 427716 483706 2726473884 72615637475
384            MARY PATRICIA LINDA BARBARA ELIZABETH JENNIFER MARIA
385            SUSAN MARGARET DOROTHY LISA NANCY KAREN BETTY HELEN
386            SANDRA DONNA CAROL RUTH SHARON MICHELLE LAURA SARAH
387            KIMBERLY DEBORAH JESSICA SHIRLEY CYNTHIA ANGELA MELISSA
388            BRENDA AMY ANNA REBECCA VIRGINIA KATHLEEN
389        }
390        intset {
391            0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
392            20 21 22 23 24 25 26 27 28 29
393            30 31 32 33 34 35 36 37 38 39
394            40 41 42 43 44 45 46 47 48 49
395        }
396    } {
397        test "SRANDMEMBER with <count> - $type" {
398            create_set myset $contents
399            unset -nocomplain myset
400            array set myset {}
401            foreach ele [r smembers myset] {
402                set myset($ele) 1
403            }
404            assert_equal [lsort $contents] [lsort [array names myset]]
405
406            # Make sure that a count of 0 is handled correctly.
407            assert_equal [r srandmember myset 0] {}
408
409            # We'll stress different parts of the code, see the implementation
410            # of SRANDMEMBER for more information, but basically there are
411            # four different code paths.
412            #
413            # PATH 1: Use negative count.
414            #
415            # 1) Check that it returns repeated elements.
416            set res [r srandmember myset -100]
417            assert_equal [llength $res] 100
418
419            # 2) Check that all the elements actually belong to the
420            # original set.
421            foreach ele $res {
422                assert {[info exists myset($ele)]}
423            }
424
425            # 3) Check that eventually all the elements are returned.
426            unset -nocomplain auxset
427            set iterations 1000
428            while {$iterations != 0} {
429                incr iterations -1
430                set res [r srandmember myset -10]
431                foreach ele $res {
432                    set auxset($ele) 1
433                }
434                if {[lsort [array names myset]] eq
435                    [lsort [array names auxset]]} {
436                    break;
437                }
438            }
439            assert {$iterations != 0}
440
441            # PATH 2: positive count (unique behavior) with requested size
442            # equal or greater than set size.
443            foreach size {50 100} {
444                set res [r srandmember myset $size]
445                assert_equal [llength $res] 50
446                assert_equal [lsort $res] [lsort [array names myset]]
447            }
448
449            # PATH 3: Ask almost as elements as there are in the set.
450            # In this case the implementation will duplicate the original
451            # set and will remove random elements up to the requested size.
452            #
453            # PATH 4: Ask a number of elements definitely smaller than
454            # the set size.
455            #
456            # We can test both the code paths just changing the size but
457            # using the same code.
458
459            foreach size {45 5} {
460                set res [r srandmember myset $size]
461                assert_equal [llength $res] $size
462
463                # 1) Check that all the elements actually belong to the
464                # original set.
465                foreach ele $res {
466                    assert {[info exists myset($ele)]}
467                }
468
469                # 2) Check that eventually all the elements are returned.
470                unset -nocomplain auxset
471                set iterations 1000
472                while {$iterations != 0} {
473                    incr iterations -1
474                    set res [r srandmember myset -10]
475                    foreach ele $res {
476                        set auxset($ele) 1
477                    }
478                    if {[lsort [array names myset]] eq
479                        [lsort [array names auxset]]} {
480                        break;
481                    }
482                }
483                assert {$iterations != 0}
484            }
485        }
486    }
487
488    proc setup_move {} {
489        r del myset3 myset4
490        create_set myset1 {1 a b}
491        create_set myset2 {2 3 4}
492        assert_encoding hashtable myset1
493        assert_encoding intset myset2
494    }
495
496    test "SMOVE basics - from regular set to intset" {
497        # move a non-integer element to an intset should convert encoding
498        setup_move
499        assert_equal 1 [r smove myset1 myset2 a]
500        assert_equal {1 b} [lsort [r smembers myset1]]
501        assert_equal {2 3 4 a} [lsort [r smembers myset2]]
502        assert_encoding hashtable myset2
503
504        # move an integer element should not convert the encoding
505        setup_move
506        assert_equal 1 [r smove myset1 myset2 1]
507        assert_equal {a b} [lsort [r smembers myset1]]
508        assert_equal {1 2 3 4} [lsort [r smembers myset2]]
509        assert_encoding intset myset2
510    }
511
512    test "SMOVE basics - from intset to regular set" {
513        setup_move
514        assert_equal 1 [r smove myset2 myset1 2]
515        assert_equal {1 2 a b} [lsort [r smembers myset1]]
516        assert_equal {3 4} [lsort [r smembers myset2]]
517    }
518
519    test "SMOVE non existing key" {
520        setup_move
521        assert_equal 0 [r smove myset1 myset2 foo]
522        assert_equal 0 [r smove myset1 myset1 foo]
523        assert_equal {1 a b} [lsort [r smembers myset1]]
524        assert_equal {2 3 4} [lsort [r smembers myset2]]
525    }
526
527    test "SMOVE non existing src set" {
528        setup_move
529        assert_equal 0 [r smove noset myset2 foo]
530        assert_equal {2 3 4} [lsort [r smembers myset2]]
531    }
532
533    test "SMOVE from regular set to non existing destination set" {
534        setup_move
535        assert_equal 1 [r smove myset1 myset3 a]
536        assert_equal {1 b} [lsort [r smembers myset1]]
537        assert_equal {a} [lsort [r smembers myset3]]
538        assert_encoding hashtable myset3
539    }
540
541    test "SMOVE from intset to non existing destination set" {
542        setup_move
543        assert_equal 1 [r smove myset2 myset3 2]
544        assert_equal {3 4} [lsort [r smembers myset2]]
545        assert_equal {2} [lsort [r smembers myset3]]
546        assert_encoding intset myset3
547    }
548
549    test "SMOVE wrong src key type" {
550        r set x 10
551        assert_error "WRONGTYPE*" {r smove x myset2 foo}
552    }
553
554    test "SMOVE wrong dst key type" {
555        r set x 10
556        assert_error "WRONGTYPE*" {r smove myset2 x foo}
557    }
558
559    test "SMOVE with identical source and destination" {
560        r del set
561        r sadd set a b c
562        r smove set set b
563        lsort [r smembers set]
564    } {a b c}
565
566    tags {slow} {
567        test {intsets implementation stress testing} {
568            for {set j 0} {$j < 20} {incr j} {
569                unset -nocomplain s
570                array set s {}
571                r del s
572                set len [randomInt 1024]
573                for {set i 0} {$i < $len} {incr i} {
574                    randpath {
575                        set data [randomInt 65536]
576                    } {
577                        set data [randomInt 4294967296]
578                    } {
579                        set data [randomInt 18446744073709551616]
580                    }
581                    set s($data) {}
582                    r sadd s $data
583                }
584                assert_equal [lsort [r smembers s]] [lsort [array names s]]
585                set len [array size s]
586                for {set i 0} {$i < $len} {incr i} {
587                    set e [r spop s]
588                    if {![info exists s($e)]} {
589                        puts "Can't find '$e' on local array"
590                        puts "Local array: [lsort [r smembers s]]"
591                        puts "Remote array: [lsort [array names s]]"
592                        error "exception"
593                    }
594                    array unset s $e
595                }
596                assert_equal [r scard s] 0
597                assert_equal [array size s] 0
598            }
599        }
600    }
601}
602