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