]> git.saurik.com Git - redis.git/blame - tests/unit/type/set.tcl
SDIFF is now able to select between two algorithms for speed.
[redis.git] / tests / unit / type / set.tcl
CommitLineData
ab37269c
PN
1start_server {
2 tags {"set"}
3 overrides {
4 "set-max-intset-entries" 512
5 }
6} {
d0b58d53
PN
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 }
98578b57
PN
35
36 test {SADD against non set} {
37 r lpush mylist foo
c4b0b685 38 assert_error WRONGTYPE* {r sadd mylist bar}
d0b58d53
PN
39 }
40
ab37269c
PN
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
87c74dfa
PN
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
ab37269c
PN
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
271f0878 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
273f6169
PN
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
d0b58d53
PN
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
b3a96d45 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
3738ff5f 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
d0b58d53
PN
114 foreach {type} {hashtable intset} {
115 for {set i 1} {$i <= 5} {incr i} {
116 r del [format "set%d" $i]
117 }
ab37269c 118 for {set i 0} {$i < 200} {incr i} {
98578b57 119 r sadd set1 $i
ab37269c 120 r sadd set2 [expr $i+195]
98578b57 121 }
ab37269c 122 foreach i {199 195 1000 2000} {
d0b58d53
PN
123 r sadd set3 $i
124 }
ab37269c 125 for {set i 5} {$i < 200} {incr i} {
d0b58d53
PN
126 r sadd set4 $i
127 }
128 r sadd set5 0
129
1eb13e49
PN
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
d0b58d53 135 if {$type eq "hashtable"} {
1eb13e49
PN
136 set large foo
137 }
138
139 for {set i 1} {$i <= 5} {incr i} {
140 r sadd [format "set%d" $i] $large
d0b58d53
PN
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" {
1eb13e49 150 assert_equal [list 195 196 197 198 199 $large] [lsort [r sinter set1 set2]]
d0b58d53
PN
151 }
152
153 test "SINTERSTORE with two sets - $type" {
154 r sinterstore setres set1 set2
1eb13e49
PN
155 assert_encoding $type setres
156 assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]]
d0b58d53
PN
157 }
158
159 test "SINTERSTORE with two sets, after a DEBUG RELOAD - $type" {
160 r debug reload
161 r sinterstore setres set1 set2
1eb13e49
PN
162 assert_encoding $type setres
163 assert_equal [list 195 196 197 198 199 $large] [lsort [r smembers setres]]
d0b58d53
PN
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
1eb13e49 173 assert_encoding $type setres
d0b58d53
PN
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" {
1eb13e49 179 assert_equal [list 195 199 $large] [lsort [r sinter set1 set2 set3]]
d0b58d53
PN
180 }
181
182 test "SINTERSTORE with three sets - $type" {
183 r sinterstore setres set1 set2 set3
1eb13e49 184 assert_equal [list 195 199 $large] [lsort [r smembers setres]]
d0b58d53
PN
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 }
98578b57 195
d0b58d53
PN
196 test "SDIFF with three sets - $type" {
197 assert_equal {1 2 3 4} [lsort [r sdiff set1 set4 set5]]
198 }
98578b57 199
d0b58d53
PN
200 test "SDIFFSTORE with three sets - $type" {
201 r sdiffstore setres set1 set4 set5
f50e6584 202 # When we start with intsets, we should always end with intsets.
203 if {$type eq {intset}} {
204 assert_encoding intset setres
205 }
d0b58d53
PN
206 assert_equal {1 2 3 4} [lsort [r smembers setres]]
207 }
208 }
98578b57 209
cddfd67e 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
395d663d 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
d0b58d53
PN
243 test "SINTER against non-set should throw error" {
244 r set key1 x
c4b0b685 245 assert_error "WRONGTYPE*" {r sinter key1 noset}
d0b58d53 246 }
98578b57 247
d0b58d53
PN
248 test "SUNION against non-set should throw error" {
249 r set key1 x
c4b0b685 250 assert_error "WRONGTYPE*" {r sunion key1 noset}
d0b58d53 251 }
98578b57 252
f800942f 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
42644591 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
d0b58d53 270 test "SINTERSTORE against non existing keys should delete dstkey" {
98578b57 271 r set setres xxx
d0b58d53
PN
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]
98578b57 288 }
98578b57 289
d0b58d53
PN
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
0ee3f055 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
b978abbf
PN
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" {
98578b57 474 r set x 10
c4b0b685 475 assert_error "WRONGTYPE*" {r smove x myset2 foo}
b978abbf 476 }
98578b57 477
b978abbf 478 test "SMOVE wrong dst key type" {
98578b57 479 r set x 10
c4b0b685 480 assert_error "WRONGTYPE*" {r smove myset2 x foo}
b978abbf 481 }
4610b033 482
88f77a2b 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
4610b033 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 }
98578b57 525}