]> git.saurik.com Git - redis.git/blob - tests/unit/type/list.tcl
d3ed90ecc0fc174534983fe60a66bd3260aded9c
[redis.git] / tests / unit / type / list.tcl
1 start_server {
2 tags {"list"}
3 overrides {
4 "list-max-ziplist-value" 16
5 "list-max-ziplist-entries" 256
6 }
7 } {
8 # We need a value larger than list-max-ziplist-value to make sure
9 # the list has the right encoding when it is swapped in again.
10 array set largevalue {}
11 set largevalue(ziplist) "hello"
12 set largevalue(linkedlist) [string repeat "hello" 4]
13
14 test {LPUSH, RPUSH, LLENGTH, LINDEX - ziplist} {
15 # first lpush then rpush
16 assert_equal 1 [r lpush myziplist1 a]
17 assert_equal 2 [r rpush myziplist1 b]
18 assert_equal 3 [r rpush myziplist1 c]
19 assert_equal 3 [r llen myziplist1]
20 assert_equal a [r lindex myziplist1 0]
21 assert_equal b [r lindex myziplist1 1]
22 assert_equal c [r lindex myziplist1 2]
23 assert_encoding ziplist myziplist1
24
25 # first rpush then lpush
26 assert_equal 1 [r rpush myziplist2 a]
27 assert_equal 2 [r lpush myziplist2 b]
28 assert_equal 3 [r lpush myziplist2 c]
29 assert_equal 3 [r llen myziplist2]
30 assert_equal c [r lindex myziplist2 0]
31 assert_equal b [r lindex myziplist2 1]
32 assert_equal a [r lindex myziplist2 2]
33 assert_encoding ziplist myziplist2
34 }
35
36 test {LPUSH, RPUSH, LLENGTH, LINDEX - regular list} {
37 # first lpush then rpush
38 assert_equal 1 [r lpush mylist1 $largevalue(linkedlist)]
39 assert_encoding linkedlist mylist1
40 assert_equal 2 [r rpush mylist1 b]
41 assert_equal 3 [r rpush mylist1 c]
42 assert_equal 3 [r llen mylist1]
43 assert_equal $largevalue(linkedlist) [r lindex mylist1 0]
44 assert_equal b [r lindex mylist1 1]
45 assert_equal c [r lindex mylist1 2]
46
47 # first rpush then lpush
48 assert_equal 1 [r rpush mylist2 $largevalue(linkedlist)]
49 assert_encoding linkedlist mylist2
50 assert_equal 2 [r lpush mylist2 b]
51 assert_equal 3 [r lpush mylist2 c]
52 assert_equal 3 [r llen mylist2]
53 assert_equal c [r lindex mylist2 0]
54 assert_equal b [r lindex mylist2 1]
55 assert_equal $largevalue(linkedlist) [r lindex mylist2 2]
56 }
57
58 test {DEL a list - ziplist} {
59 assert_equal 1 [r del myziplist2]
60 assert_equal 0 [r exists myziplist2]
61 assert_equal 0 [r llen myziplist2]
62 }
63
64 test {DEL a list - regular list} {
65 assert_equal 1 [r del mylist2]
66 assert_equal 0 [r exists mylist2]
67 assert_equal 0 [r llen mylist2]
68 }
69
70 proc create_ziplist {key entries} {
71 r del $key
72 foreach entry $entries { r rpush $key $entry }
73 assert_encoding ziplist $key
74 }
75
76 proc create_linkedlist {key entries} {
77 r del $key
78 foreach entry $entries { r rpush $key $entry }
79 assert_encoding linkedlist $key
80 }
81
82 foreach {type large} [array get largevalue] {
83 test "BLPOP, BRPOP: single existing list - $type" {
84 set rd [redis_deferring_client]
85 create_$type blist "a b $large c d"
86
87 $rd blpop blist 1
88 assert_equal {blist a} [$rd read]
89 $rd brpop blist 1
90 assert_equal {blist d} [$rd read]
91
92 $rd blpop blist 1
93 assert_equal {blist b} [$rd read]
94 $rd brpop blist 1
95 assert_equal {blist c} [$rd read]
96 }
97
98 test "BLPOP, BRPOP: multiple existing lists - $type" {
99 set rd [redis_deferring_client]
100 create_$type blist1 "a $large c"
101 create_$type blist2 "d $large f"
102
103 $rd blpop blist1 blist2 1
104 assert_equal {blist1 a} [$rd read]
105 $rd brpop blist1 blist2 1
106 assert_equal {blist1 c} [$rd read]
107 assert_equal 1 [r llen blist1]
108 assert_equal 3 [r llen blist2]
109
110 $rd blpop blist2 blist1 1
111 assert_equal {blist2 d} [$rd read]
112 $rd brpop blist2 blist1 1
113 assert_equal {blist2 f} [$rd read]
114 assert_equal 1 [r llen blist1]
115 assert_equal 1 [r llen blist2]
116 }
117
118 test "BLPOP, BRPOP: second list has an entry - $type" {
119 set rd [redis_deferring_client]
120 r del blist1
121 create_$type blist2 "d $large f"
122
123 $rd blpop blist1 blist2 1
124 assert_equal {blist2 d} [$rd read]
125 $rd brpop blist1 blist2 1
126 assert_equal {blist2 f} [$rd read]
127 assert_equal 0 [r llen blist1]
128 assert_equal 1 [r llen blist2]
129 }
130 }
131
132 foreach {pop} {BLPOP BRPOP} {
133 test "$pop: with single empty list argument" {
134 set rd [redis_deferring_client]
135 r del blist1
136 $rd $pop blist1 1
137 r rpush blist1 foo
138 assert_equal {blist1 foo} [$rd read]
139 assert_equal 0 [r exists blist1]
140 }
141
142 test "$pop: second argument is not a list" {
143 set rd [redis_deferring_client]
144 r del blist1 blist2
145 r set blist2 nolist
146 $rd $pop blist1 blist2 1
147 assert_error "ERR*wrong kind*" {$rd read}
148 }
149
150 test "$pop: timeout" {
151 set rd [redis_deferring_client]
152 r del blist1 blist2
153 $rd $pop blist1 blist2 1
154 assert_equal {} [$rd read]
155 }
156
157 test "$pop: arguments are empty" {
158 set rd [redis_deferring_client]
159 r del blist1 blist2
160
161 $rd $pop blist1 blist2 1
162 r rpush blist1 foo
163 assert_equal {blist1 foo} [$rd read]
164 assert_equal 0 [r exists blist1]
165 assert_equal 0 [r exists blist2]
166
167 $rd $pop blist1 blist2 1
168 r rpush blist2 foo
169 assert_equal {blist2 foo} [$rd read]
170 assert_equal 0 [r exists blist1]
171 assert_equal 0 [r exists blist2]
172 }
173 }
174
175 test {LPUSHX, RPUSHX - generic} {
176 r del xlist
177 assert_equal 0 [r lpushx xlist a]
178 assert_equal 0 [r llen xlist]
179 assert_equal 0 [r rpushx xlist a]
180 assert_equal 0 [r llen xlist]
181 }
182
183 foreach {type large} [array get largevalue] {
184 test "LPUSHX, RPUSHX - $type" {
185 create_$type xlist "$large c"
186 assert_equal 3 [r rpushx xlist d]
187 assert_equal 4 [r lpushx xlist a]
188 assert_equal "a $large c d" [r lrange xlist 0 -1]
189 }
190
191 test "LINSERT - $type" {
192 create_$type xlist "a $large c d"
193 assert_equal 5 [r linsert xlist before c zz]
194 assert_equal "a $large zz c d" [r lrange xlist 0 10]
195 assert_equal 6 [r linsert xlist after c yy]
196 assert_equal "a $large zz c yy d" [r lrange xlist 0 10]
197 assert_equal 7 [r linsert xlist after d dd]
198 assert_equal -1 [r linsert xlist after bad ddd]
199 assert_equal "a $large zz c yy d dd" [r lrange xlist 0 10]
200 assert_equal 8 [r linsert xlist before a aa]
201 assert_equal -1 [r linsert xlist before bad aaa]
202 assert_equal "aa a $large zz c yy d dd" [r lrange xlist 0 10]
203
204 # check inserting integer encoded value
205 assert_equal 9 [r linsert xlist before aa 42]
206 assert_equal 42 [r lrange xlist 0 0]
207 }
208 }
209
210 test {LPUSHX, RPUSHX convert from ziplist to list} {
211 set large $largevalue(linkedlist)
212
213 # convert when a large value is pushed
214 create_ziplist xlist a
215 assert_equal 2 [r rpushx xlist $large]
216 assert_encoding linkedlist xlist
217 create_ziplist xlist a
218 assert_equal 2 [r lpushx xlist $large]
219 assert_encoding linkedlist xlist
220
221 # convert when the length threshold is exceeded
222 create_ziplist xlist [lrepeat 256 a]
223 assert_equal 257 [r rpushx xlist b]
224 assert_encoding linkedlist xlist
225 create_ziplist xlist [lrepeat 256 a]
226 assert_equal 257 [r lpushx xlist b]
227 assert_encoding linkedlist xlist
228 }
229
230 test {LINSERT convert from ziplist to list} {
231 set large $largevalue(linkedlist)
232
233 # convert when a large value is inserted
234 create_ziplist xlist a
235 assert_equal 2 [r linsert xlist before a $large]
236 assert_encoding linkedlist xlist
237 create_ziplist xlist a
238 assert_equal 2 [r linsert xlist after a $large]
239 assert_encoding linkedlist xlist
240
241 # convert when the length threshold is exceeded
242 create_ziplist xlist [lrepeat 256 a]
243 assert_equal 257 [r linsert xlist before a a]
244 assert_encoding linkedlist xlist
245 create_ziplist xlist [lrepeat 256 a]
246 assert_equal 257 [r linsert xlist after a a]
247 assert_encoding linkedlist xlist
248
249 # don't convert when the value could not be inserted
250 create_ziplist xlist [lrepeat 256 a]
251 assert_equal -1 [r linsert xlist before foo a]
252 assert_encoding ziplist xlist
253 create_ziplist xlist [lrepeat 256 a]
254 assert_equal -1 [r linsert xlist after foo a]
255 assert_encoding ziplist xlist
256 }
257
258 foreach {type num} {ziplist 250 linkedlist 500} {
259 proc check_numbered_list_consistency {key} {
260 set len [r llen $key]
261 for {set i 0} {$i < $len} {incr i} {
262 assert_equal $i [r lindex $key $i]
263 assert_equal [expr $len-1-$i] [r lindex $key [expr (-$i)-1]]
264 }
265 }
266
267 proc check_random_access_consistency {key} {
268 set len [r llen $key]
269 for {set i 0} {$i < $len} {incr i} {
270 set rint [expr int(rand()*$len)]
271 assert_equal $rint [r lindex $key $rint]
272 assert_equal [expr $len-1-$rint] [r lindex $key [expr (-$rint)-1]]
273 }
274 }
275
276 test "LINDEX consistency test - $type" {
277 r del mylist
278 for {set i 0} {$i < $num} {incr i} {
279 r rpush mylist $i
280 }
281 assert_encoding $type mylist
282 check_numbered_list_consistency mylist
283 }
284
285 test "LINDEX random access - $type" {
286 assert_encoding $type mylist
287 check_random_access_consistency mylist
288 }
289
290 test "Check if list is still ok after a DEBUG RELOAD - $type" {
291 r debug reload
292 assert_encoding $type mylist
293 check_numbered_list_consistency mylist
294 check_random_access_consistency mylist
295 }
296 }
297
298 test {LLEN against non-list value error} {
299 r del mylist
300 r set mylist foobar
301 assert_error ERR* {r llen mylist}
302 }
303
304 test {LLEN against non existing key} {
305 assert_equal 0 [r llen not-a-key]
306 }
307
308 test {LINDEX against non-list value error} {
309 assert_error ERR* {r lindex mylist 0}
310 }
311
312 test {LINDEX against non existing key} {
313 assert_equal "" [r lindex not-a-key 10]
314 }
315
316 test {LPUSH against non-list value error} {
317 assert_error ERR* {r lpush mylist 0}
318 }
319
320 test {RPUSH against non-list value error} {
321 assert_error ERR* {r rpush mylist 0}
322 }
323
324 foreach {type large} [array get largevalue] {
325 test "RPOPLPUSH base case - $type" {
326 r del mylist1 mylist2
327 create_$type mylist1 "a $large c d"
328 assert_equal d [r rpoplpush mylist1 mylist2]
329 assert_equal c [r rpoplpush mylist1 mylist2]
330 assert_equal "a $large" [r lrange mylist1 0 -1]
331 assert_equal "c d" [r lrange mylist2 0 -1]
332 assert_encoding ziplist mylist2
333 }
334
335 test "RPOPLPUSH with the same list as src and dst - $type" {
336 create_$type mylist "a $large c"
337 assert_equal "a $large c" [r lrange mylist 0 -1]
338 assert_equal c [r rpoplpush mylist mylist]
339 assert_equal "c a $large" [r lrange mylist 0 -1]
340 }
341
342 foreach {othertype otherlarge} [array get largevalue] {
343 test "RPOPLPUSH with $type source and existing target $othertype" {
344 create_$type srclist "a b c $large"
345 create_$othertype dstlist "$otherlarge"
346 assert_equal $large [r rpoplpush srclist dstlist]
347 assert_equal c [r rpoplpush srclist dstlist]
348 assert_equal "a b" [r lrange srclist 0 -1]
349 assert_equal "c $large $otherlarge" [r lrange dstlist 0 -1]
350
351 # When we rpoplpush'ed a large value, dstlist should be
352 # converted to the same encoding as srclist.
353 if {$type eq "linkedlist"} {
354 assert_encoding linkedlist dstlist
355 }
356 }
357 }
358 }
359
360 test {RPOPLPUSH against non existing key} {
361 r del srclist dstlist
362 assert_equal {} [r rpoplpush srclist dstlist]
363 assert_equal 0 [r exists srclist]
364 assert_equal 0 [r exists dstlist]
365 }
366
367 test {RPOPLPUSH against non list src key} {
368 r del srclist dstlist
369 r set srclist x
370 assert_error ERR* {r rpoplpush srclist dstlist}
371 assert_type string srclist
372 assert_equal 0 [r exists newlist]
373 }
374
375 test {RPOPLPUSH against non list dst key} {
376 create_ziplist srclist {a b c d}
377 r set dstlist x
378 assert_error ERR* {r rpoplpush srclist dstlist}
379 assert_type string dstlist
380 assert_equal {a b c d} [r lrange srclist 0 -1]
381 }
382
383 test {RPOPLPUSH against non existing src key} {
384 r del srclist dstlist
385 assert_equal {} [r rpoplpush srclist dstlist]
386 } {}
387
388 foreach {type large} [array get largevalue] {
389 test "Basic LPOP/RPOP - $type" {
390 create_$type mylist "$large 1 2"
391 assert_equal $large [r lpop mylist]
392 assert_equal 2 [r rpop mylist]
393 assert_equal 1 [r lpop mylist]
394 assert_equal 0 [r llen mylist]
395
396 # pop on empty list
397 assert_equal {} [r lpop mylist]
398 assert_equal {} [r rpop mylist]
399 }
400 }
401
402 test {LPOP/RPOP against non list value} {
403 r set notalist foo
404 assert_error ERR*kind* {r lpop notalist}
405 assert_error ERR*kind* {r rpop notalist}
406 }
407
408 foreach {type num} {ziplist 250 linkedlist 500} {
409 test "Mass RPOP/LPOP - $type" {
410 r del mylist
411 set sum1 0
412 for {set i 0} {$i < $num} {incr i} {
413 r lpush mylist $i
414 incr sum1 $i
415 }
416 assert_encoding $type mylist
417 set sum2 0
418 for {set i 0} {$i < [expr $num/2]} {incr i} {
419 incr sum2 [r lpop mylist]
420 incr sum2 [r rpop mylist]
421 }
422 assert_equal $sum1 $sum2
423 }
424 }
425
426 foreach {type large} [array get largevalue] {
427 test "LRANGE basics - $type" {
428 create_$type mylist "$large 1 2 3 4 5 6 7 8 9"
429 assert_equal {1 2 3 4 5 6 7 8} [r lrange mylist 1 -2]
430 assert_equal {7 8 9} [r lrange mylist -3 -1]
431 assert_equal {4} [r lrange mylist 4 4]
432 }
433
434 test "LRANGE inverted indexes - $type" {
435 create_$type mylist "$large 1 2 3 4 5 6 7 8 9"
436 assert_equal {} [r lrange mylist 6 2]
437 }
438
439 test "LRANGE out of range indexes including the full list - $type" {
440 create_$type mylist "$large 1 2 3"
441 assert_equal "$large 1 2 3" [r lrange mylist -1000 1000]
442 }
443
444 test "LRANGE out of range negative end index - $type" {
445 create_$type mylist "$large 1 2 3"
446 assert_equal $large [r lrange mylist 0 -4]
447 assert_equal {} [r lrange mylist 0 -5]
448 }
449 }
450
451 test {LRANGE against non existing key} {
452 assert_equal {} [r lrange nosuchkey 0 1]
453 }
454
455 foreach {type large} [array get largevalue] {
456 proc trim_list {type min max} {
457 upvar 1 large large
458 r del mylist
459 create_$type mylist "1 2 3 4 $large"
460 r ltrim mylist $min $max
461 r lrange mylist 0 -1
462 }
463
464 test "LTRIM basics - $type" {
465 assert_equal "1" [trim_list $type 0 0]
466 assert_equal "1 2" [trim_list $type 0 1]
467 assert_equal "1 2 3" [trim_list $type 0 2]
468 assert_equal "2 3" [trim_list $type 1 2]
469 assert_equal "2 3 4 $large" [trim_list $type 1 -1]
470 assert_equal "2 3 4" [trim_list $type 1 -2]
471 assert_equal "4 $large" [trim_list $type -2 -1]
472 assert_equal "$large" [trim_list $type -1 -1]
473 assert_equal "1 2 3 4 $large" [trim_list $type -5 -1]
474 assert_equal "1 2 3 4 $large" [trim_list $type -10 10]
475 assert_equal "1 2 3 4 $large" [trim_list $type 0 5]
476 assert_equal "1 2 3 4 $large" [trim_list $type 0 10]
477 }
478
479 test "LTRIM out of range negative end index - $type" {
480 assert_equal {1} [trim_list $type 0 -5]
481 assert_equal {} [trim_list $type 0 -6]
482 }
483
484 tags {"slow"} {
485 test "LTRIM stress testing - $type" {
486 set mylist {}
487 set startlen 32
488 r del mylist
489
490 # Start with the large value to ensure the
491 # right encoding is used.
492 r rpush mylist $large
493 lappend mylist $large
494
495 for {set i 0} {$i < $startlen} {incr i} {
496 set str [randomInt 9223372036854775807]
497 r rpush mylist $str
498 lappend mylist $str
499 }
500
501 for {set i 0} {$i < 1000} {incr i} {
502 set min [expr {int(rand()*$startlen)}]
503 set max [expr {$min+int(rand()*$startlen)}]
504 set mylist [lrange $mylist $min $max]
505 r ltrim mylist $min $max
506 assert_equal $mylist [r lrange mylist 0 -1]
507
508 for {set j [r llen mylist]} {$j < $startlen} {incr j} {
509 set str [randomInt 9223372036854775807]
510 r rpush mylist $str
511 lappend mylist $str
512 }
513 }
514 }
515 }
516 }
517
518 foreach {type large} [array get largevalue] {
519 test "LSET - $type" {
520 create_$type mylist "99 98 $large 96 95"
521 r lset mylist 1 foo
522 r lset mylist -1 bar
523 assert_equal "99 foo $large 96 bar" [r lrange mylist 0 -1]
524 }
525
526 test "LSET out of range index - $type" {
527 assert_error ERR*range* {r lset mylist 10 foo}
528 }
529 }
530
531 test {LSET against non existing key} {
532 assert_error ERR*key* {r lset nosuchkey 10 foo}
533 }
534
535 test {LSET against non list value} {
536 r set nolist foobar
537 assert_error ERR*value* {r lset nolist 0 foo}
538 }
539
540 foreach {type e} [array get largevalue] {
541 test "LREM remove all the occurrences - $type" {
542 create_$type mylist "$e foo bar foobar foobared zap bar test foo"
543 assert_equal 2 [r lrem mylist 0 bar]
544 assert_equal "$e foo foobar foobared zap test foo" [r lrange mylist 0 -1]
545 }
546
547 test "LREM remove the first occurrence - $type" {
548 assert_equal 1 [r lrem mylist 1 foo]
549 assert_equal "$e foobar foobared zap test foo" [r lrange mylist 0 -1]
550 }
551
552 test "LREM remove non existing element - $type" {
553 assert_equal 0 [r lrem mylist 1 nosuchelement]
554 assert_equal "$e foobar foobared zap test foo" [r lrange mylist 0 -1]
555 }
556
557 test "LREM starting from tail with negative count - $type" {
558 create_$type mylist "$e foo bar foobar foobared zap bar test foo foo"
559 assert_equal 1 [r lrem mylist -1 bar]
560 assert_equal "$e foo bar foobar foobared zap test foo foo" [r lrange mylist 0 -1]
561 }
562
563 test "LREM starting from tail with negative count (2) - $type" {
564 assert_equal 2 [r lrem mylist -2 foo]
565 assert_equal "$e foo bar foobar foobared zap test" [r lrange mylist 0 -1]
566 }
567
568 test "LREM deleting objects that may be int encoded - $type" {
569 create_$type myotherlist "$e 1 2 3"
570 assert_equal 1 [r lrem myotherlist 1 2]
571 assert_equal 3 [r llen myotherlist]
572 }
573 }
574 }