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