xref: /redis-3.2.3/tests/unit/type/set.tcl (revision 40f822da)
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 fuzzing" {
218        for {set j 0} {$j < 100} {incr j} {
219            unset -nocomplain s
220            array set s {}
221            set args {}
222            set num_sets [expr {[randomInt 10]+1}]
223            for {set i 0} {$i < $num_sets} {incr i} {
224                set num_elements [randomInt 100]
225                r del set_$i
226                lappend args set_$i
227                while {$num_elements} {
228                    set ele [randomValue]
229                    r sadd set_$i $ele
230                    if {$i == 0} {
231                        set s($ele) x
232                    } else {
233                        unset -nocomplain s($ele)
234                    }
235                    incr num_elements -1
236                }
237            }
238            set result [lsort [r sdiff {*}$args]]
239            assert_equal $result [lsort [array names s]]
240        }
241    }
242
243    test "SINTER against non-set should throw error" {
244        r set key1 x
245        assert_error "WRONGTYPE*" {r sinter key1 noset}
246    }
247
248    test "SUNION against non-set should throw error" {
249        r set key1 x
250        assert_error "WRONGTYPE*" {r sunion key1 noset}
251    }
252
253    test "SINTER should handle non existing key as empty" {
254        r del set1 set2 set3
255        r sadd set1 a b c
256        r sadd set2 b c d
257        r sinter set1 set2 set3
258    } {}
259
260    test "SINTER with same integer elements but different encoding" {
261        r del set1 set2
262        r sadd set1 1 2 3
263        r sadd set2 1 2 3 a
264        r srem set2 a
265        assert_encoding intset set1
266        assert_encoding hashtable set2
267        lsort [r sinter set1 set2]
268    } {1 2 3}
269
270    test "SINTERSTORE against non existing keys should delete dstkey" {
271        r set setres xxx
272        assert_equal 0 [r sinterstore setres foo111 bar222]
273        assert_equal 0 [r exists setres]
274    }
275
276    test "SUNIONSTORE against non existing keys should delete dstkey" {
277        r set setres xxx
278        assert_equal 0 [r sunionstore setres foo111 bar222]
279        assert_equal 0 [r exists setres]
280    }
281
282    foreach {type contents} {hashtable {a b c} intset {1 2 3}} {
283        test "SPOP basics - $type" {
284            create_set myset $contents
285            assert_encoding $type myset
286            assert_equal $contents [lsort [list [r spop myset] [r spop myset] [r spop myset]]]
287            assert_equal 0 [r scard myset]
288        }
289
290        test "SRANDMEMBER - $type" {
291            create_set myset $contents
292            unset -nocomplain myset
293            array set myset {}
294            for {set i 0} {$i < 100} {incr i} {
295                set myset([r srandmember myset]) 1
296            }
297            assert_equal $contents [lsort [array names myset]]
298        }
299    }
300
301    test "SRANDMEMBER with <count> against non existing key" {
302        r srandmember nonexisting_key 100
303    } {}
304
305    foreach {type contents} {
306        hashtable {
307            1 5 10 50 125 50000 33959417 4775547 65434162
308            12098459 427716 483706 2726473884 72615637475
309            MARY PATRICIA LINDA BARBARA ELIZABETH JENNIFER MARIA
310            SUSAN MARGARET DOROTHY LISA NANCY KAREN BETTY HELEN
311            SANDRA DONNA CAROL RUTH SHARON MICHELLE LAURA SARAH
312            KIMBERLY DEBORAH JESSICA SHIRLEY CYNTHIA ANGELA MELISSA
313            BRENDA AMY ANNA REBECCA VIRGINIA KATHLEEN
314        }
315        intset {
316            0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
317            20 21 22 23 24 25 26 27 28 29
318            30 31 32 33 34 35 36 37 38 39
319            40 41 42 43 44 45 46 47 48 49
320        }
321    } {
322        test "SRANDMEMBER with <count> - $type" {
323            create_set myset $contents
324            unset -nocomplain myset
325            array set myset {}
326            foreach ele [r smembers myset] {
327                set myset($ele) 1
328            }
329            assert_equal [lsort $contents] [lsort [array names myset]]
330
331            # Make sure that a count of 0 is handled correctly.
332            assert_equal [r srandmember myset 0] {}
333
334            # We'll stress different parts of the code, see the implementation
335            # of SRANDMEMBER for more information, but basically there are
336            # four different code paths.
337            #
338            # PATH 1: Use negative count.
339            #
340            # 1) Check that it returns repeated elements.
341            set res [r srandmember myset -100]
342            assert_equal [llength $res] 100
343
344            # 2) Check that all the elements actually belong to the
345            # original set.
346            foreach ele $res {
347                assert {[info exists myset($ele)]}
348            }
349
350            # 3) Check that eventually all the elements are returned.
351            unset -nocomplain auxset
352            set iterations 1000
353            while {$iterations != 0} {
354                incr iterations -1
355                set res [r srandmember myset -10]
356                foreach ele $res {
357                    set auxset($ele) 1
358                }
359                if {[lsort [array names myset]] eq
360                    [lsort [array names auxset]]} {
361                    break;
362                }
363            }
364            assert {$iterations != 0}
365
366            # PATH 2: positive count (unique behavior) with requested size
367            # equal or greater than set size.
368            foreach size {50 100} {
369                set res [r srandmember myset $size]
370                assert_equal [llength $res] 50
371                assert_equal [lsort $res] [lsort [array names myset]]
372            }
373
374            # PATH 3: Ask almost as elements as there are in the set.
375            # In this case the implementation will duplicate the original
376            # set and will remove random elements up to the requested size.
377            #
378            # PATH 4: Ask a number of elements definitely smaller than
379            # the set size.
380            #
381            # We can test both the code paths just changing the size but
382            # using the same code.
383
384            foreach size {45 5} {
385                set res [r srandmember myset $size]
386                assert_equal [llength $res] $size
387
388                # 1) Check that all the elements actually belong to the
389                # original set.
390                foreach ele $res {
391                    assert {[info exists myset($ele)]}
392                }
393
394                # 2) Check that eventually all the elements are returned.
395                unset -nocomplain auxset
396                set iterations 1000
397                while {$iterations != 0} {
398                    incr iterations -1
399                    set res [r srandmember myset -10]
400                    foreach ele $res {
401                        set auxset($ele) 1
402                    }
403                    if {[lsort [array names myset]] eq
404                        [lsort [array names auxset]]} {
405                        break;
406                    }
407                }
408                assert {$iterations != 0}
409            }
410        }
411    }
412
413    proc setup_move {} {
414        r del myset3 myset4
415        create_set myset1 {1 a b}
416        create_set myset2 {2 3 4}
417        assert_encoding hashtable myset1
418        assert_encoding intset myset2
419    }
420
421    test "SMOVE basics - from regular set to intset" {
422        # move a non-integer element to an intset should convert encoding
423        setup_move
424        assert_equal 1 [r smove myset1 myset2 a]
425        assert_equal {1 b} [lsort [r smembers myset1]]
426        assert_equal {2 3 4 a} [lsort [r smembers myset2]]
427        assert_encoding hashtable myset2
428
429        # move an integer element should not convert the encoding
430        setup_move
431        assert_equal 1 [r smove myset1 myset2 1]
432        assert_equal {a b} [lsort [r smembers myset1]]
433        assert_equal {1 2 3 4} [lsort [r smembers myset2]]
434        assert_encoding intset myset2
435    }
436
437    test "SMOVE basics - from intset to regular set" {
438        setup_move
439        assert_equal 1 [r smove myset2 myset1 2]
440        assert_equal {1 2 a b} [lsort [r smembers myset1]]
441        assert_equal {3 4} [lsort [r smembers myset2]]
442    }
443
444    test "SMOVE non existing key" {
445        setup_move
446        assert_equal 0 [r smove myset1 myset2 foo]
447        assert_equal {1 a b} [lsort [r smembers myset1]]
448        assert_equal {2 3 4} [lsort [r smembers myset2]]
449    }
450
451    test "SMOVE non existing src set" {
452        setup_move
453        assert_equal 0 [r smove noset myset2 foo]
454        assert_equal {2 3 4} [lsort [r smembers myset2]]
455    }
456
457    test "SMOVE from regular set to non existing destination set" {
458        setup_move
459        assert_equal 1 [r smove myset1 myset3 a]
460        assert_equal {1 b} [lsort [r smembers myset1]]
461        assert_equal {a} [lsort [r smembers myset3]]
462        assert_encoding hashtable myset3
463    }
464
465    test "SMOVE from intset to non existing destination set" {
466        setup_move
467        assert_equal 1 [r smove myset2 myset3 2]
468        assert_equal {3 4} [lsort [r smembers myset2]]
469        assert_equal {2} [lsort [r smembers myset3]]
470        assert_encoding intset myset3
471    }
472
473    test "SMOVE wrong src key type" {
474        r set x 10
475        assert_error "WRONGTYPE*" {r smove x myset2 foo}
476    }
477
478    test "SMOVE wrong dst key type" {
479        r set x 10
480        assert_error "WRONGTYPE*" {r smove myset2 x foo}
481    }
482
483    test "SMOVE with identical source and destination" {
484        r del set
485        r sadd set a b c
486        r smove set set b
487        lsort [r smembers set]
488    } {a b c}
489
490    tags {slow} {
491        test {intsets implementation stress testing} {
492            for {set j 0} {$j < 20} {incr j} {
493                unset -nocomplain s
494                array set s {}
495                r del s
496                set len [randomInt 1024]
497                for {set i 0} {$i < $len} {incr i} {
498                    randpath {
499                        set data [randomInt 65536]
500                    } {
501                        set data [randomInt 4294967296]
502                    } {
503                        set data [randomInt 18446744073709551616]
504                    }
505                    set s($data) {}
506                    r sadd s $data
507                }
508                assert_equal [lsort [r smembers s]] [lsort [array names s]]
509                set len [array size s]
510                for {set i 0} {$i < $len} {incr i} {
511                    set e [r spop s]
512                    if {![info exists s($e)]} {
513                        puts "Can't find '$e' on local array"
514                        puts "Local array: [lsort [r smembers s]]"
515                        puts "Remote array: [lsort [array names s]]"
516                        error "exception"
517                    }
518                    array unset s $e
519                }
520                assert_equal [r scard s] 0
521                assert_equal [array size s] 0
522            }
523        }
524    }
525}
526