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