set ::passed 0
set ::failed 0
+set ::testnum 0
proc test {name code okpattern} {
- puts -nonewline [format "%-70s " $name]
+ incr ::testnum
+ if {$::testnum < $::first || $::testnum > $::last} return
+ puts -nonewline [format "%-70s " "#$::testnum $name"]
flush stdout
set retval [uplevel 1 $code]
if {$okpattern eq $retval || [string match $okpattern $retval]} {
string compare [lindex $a 1] [lindex $b 1]
}
+proc waitForBgsave r {
+ while 1 {
+ set i [$r info]
+ if {[string match {*bgsave_in_progress:1*} $i]} {
+ puts -nonewline "\nWaiting for background save to finish... "
+ flush stdout
+ after 1000
+ } else {
+ break
+ }
+ }
+}
+
+proc waitForBgrewriteaof r {
+ while 1 {
+ set i [$r info]
+ if {[string match {*bgrewriteaof_in_progress:1*} $i]} {
+ puts -nonewline "\nWaiting for background AOF rewrite to finish... "
+ flush stdout
+ after 1000
+ } else {
+ break
+ }
+ }
+}
+
+proc randomInt {max} {
+ expr {int(rand()*$max)}
+}
+
+proc randpath args {
+ set path [expr {int(rand()*[llength $args])}]
+ uplevel 1 [lindex $args $path]
+}
+
+proc randomValue {} {
+ randpath {
+ # Small enough to likely collide
+ randomInt 1000
+ } {
+ # 32 bit compressible signed/unsigned
+ randpath {randomInt 2000000000} {randomInt 4000000000}
+ } {
+ # 64 bit
+ randpath {randomInt 1000000000000}
+ } {
+ # Random string
+ randpath {randstring 0 256 alpha} \
+ {randstring 0 256 compr} \
+ {randstring 0 256 binary}
+ }
+}
+
+proc randomKey {} {
+ randpath {
+ # Small enough to likely collide
+ randomInt 1000
+ } {
+ # 32 bit compressible signed/unsigned
+ randpath {randomInt 2000000000} {randomInt 4000000000}
+ } {
+ # 64 bit
+ randpath {randomInt 1000000000000}
+ } {
+ # Random string
+ randpath {randstring 1 256 alpha} \
+ {randstring 1 256 compr}
+ }
+}
+
+proc createComplexDataset {r ops} {
+ for {set j 0} {$j < $ops} {incr j} {
+ set k [randomKey]
+ set v [randomValue]
+ set d [expr {rand()}]
+ set t [$r type $k]
+
+ if {$t eq {none}} {
+ randpath {
+ $r set $k $v
+ } {
+ $r lpush $k $v
+ } {
+ $r sadd $k $v
+ } {
+ $r zadd $k $d $v
+ }
+ set t [$r type $k]
+ }
+
+ switch $t {
+ {string} {
+ # Nothing to do
+ }
+ {list} {
+ randpath {$r lpush $k $v} \
+ {$r rpush $k $v} \
+ {$r lrem $k 0 $v} \
+ {$r rpop $k} \
+ {$r lpop $k}
+ }
+ {set} {
+ randpath {$r sadd $k $v} \
+ {$r srem $k $v}
+ }
+ {zset} {
+ randpath {$r zadd $k $d $v} \
+ {$r zrem $k $v}
+ }
+ }
+ }
+}
+
+proc datasetDigest r {
+ set keys [lsort [split [$r keys *] " "]]
+ set digest {}
+ foreach k $keys {
+ set t [$r type $k]
+ switch $t {
+ {string} {
+ set aux [::sha1::sha1 -hex [$r get $k]]
+ } {list} {
+ if {[$r llen $k] == 0} {
+ set aux {}
+ } else {
+ set aux [::sha1::sha1 -hex [$r lrange $k 0 -1]]
+ }
+ } {set} {
+ if {[$r scard $k] == 0} {
+ set aux {}
+ } else {
+ set aux [::sha1::sha1 -hex [lsort [$r smembers $k]]]
+ }
+ } {zset} {
+ if {[$r zcard $k] == 0} {
+ set aux {}
+ } else {
+ set aux [::sha1::sha1 -hex [$r zrange $k 0 -1]]
+ }
+ } default {
+ error "Type not supported"
+ }
+ }
+ if {$aux eq {}} continue
+ set digest [::sha1::sha1 -hex [join [list $aux $digest $k] "\n"]]
+ }
+ return $digest
+}
+
proc main {server port} {
set r [redis $server $port]
$r select 9
set err ""
+ set res ""
# The following AUTH test should be enabled only when requirepass
# <PASSWORD> is set in redis.conf and redis-server was started with
$r get x
} {foobar}
+ test {SET and GET an empty item} {
+ $r set x {}
+ $r get x
+ } {}
+
test {DEL against a single item} {
$r del x
$r get x
$r get novar2
} {foobared}
+ test {SETNX will overwrite EXPIREing key} {
+ $r set x 10
+ $r expire x 10000
+ $r setnx x 20
+ $r get x
+ } {20}
+
test {EXISTS} {
set res {}
$r set newkey test
lsort [$r smembers setres]
} [lsort -uniq "[$r smembers set1] [$r smembers set2]"]
+ test {SUNIONSTORE against non existing keys} {
+ $r set setres xxx
+ list [$r sunionstore setres foo111 bar222] [$r exists xxx]
+ } {0 0}
+
test {SINTER against three sets} {
$r sadd set3 999
$r sadd set3 995
test {SAVE - make sure there are all the types as values} {
# Wait for a background saving in progress to terminate
- while 1 {
- set i [$r info]
- if {[string match {*bgsave_in_progress:1*} $i]} {
- puts -nonewline "\nWaiting for background save to finish... "
- flush stdout
- after 100
- } else {
- break
- }
- }
+ waitForBgsave $r
$r lpush mysavelist hello
$r lpush mysavelist world
$r set myemptykey {}
list [$r msetnx x1 xxx y2 yyy] [$r get x1] [$r get y2]
} {1 xxx yyy}
+ test {MSETNX should remove all the volatile keys even on failure} {
+ $r mset x 1 y 2 z 3
+ $r expire y 10000
+ $r expire z 10000
+ list [$r msetnx x A y B z C] [$r mget x y z]
+ } {0 {1 {} {}}}
+
test {ZSET basic ZADD and score update} {
$r zadd ztmp 10 x
$r zadd ztmp 20 y
list $aux1 $aux2
} {{x y z} {y x z}}
+ test {ZCARD basics} {
+ $r zcard ztmp
+ } {3}
+
+ test {ZCARD non existing key} {
+ $r zcard ztmp-blabla
+ } {0}
+
test {ZSCORE} {
set aux {}
set err {}
set _ $err
} {}
+ test {ZRANGEBYSCORE with LIMIT} {
+ $r del zset
+ $r zadd zset 1 a
+ $r zadd zset 2 b
+ $r zadd zset 3 c
+ $r zadd zset 4 d
+ $r zadd zset 5 e
+ list \
+ [$r zrangebyscore zset 0 10 LIMIT 0 2] \
+ [$r zrangebyscore zset 0 10 LIMIT 2 3] \
+ [$r zrangebyscore zset 0 10 LIMIT 2 10] \
+ [$r zrangebyscore zset 0 10 LIMIT 20 10]
+ } {{a b} {c d e} {c d e} {}}
+
+ test {ZREMRANGE basics} {
+ $r del zset
+ $r zadd zset 1 a
+ $r zadd zset 2 b
+ $r zadd zset 3 c
+ $r zadd zset 4 d
+ $r zadd zset 5 e
+ list [$r zremrangebyscore zset 2 4] [$r zrange zset 0 -1]
+ } {3 {a e}}
+
+ test {ZREMRANGE from -inf to +inf} {
+ $r del zset
+ $r zadd zset 1 a
+ $r zadd zset 2 b
+ $r zadd zset 3 c
+ $r zadd zset 4 d
+ $r zadd zset 5 e
+ list [$r zremrangebyscore zset -inf +inf] [$r zrange zset 0 -1]
+ } {5 {}}
+
+ test {SORT against sorted sets} {
+ $r del zset
+ $r zadd zset 1 a
+ $r zadd zset 5 b
+ $r zadd zset 2 c
+ $r zadd zset 10 d
+ $r zadd zset 3 e
+ $r sort zset alpha desc
+ } {e d c b a}
+
test {Sorted sets +inf and -inf handling} {
$r del zset
$r zadd zset -100 a
} {0}
}
+ test {BGSAVE} {
+ $r flushdb
+ $r save
+ $r set x 10
+ $r bgsave
+ waitForBgsave $r
+ $r debug reload
+ $r get x
+ } {10}
+
+ test {Handle an empty query well} {
+ set fd [$r channel]
+ puts -nonewline $fd "\r\n"
+ flush $fd
+ $r ping
+ } {PONG}
+
+ test {Negative multi bulk command does not create problems} {
+ set fd [$r channel]
+ puts -nonewline $fd "*-10\r\n"
+ flush $fd
+ $r ping
+ } {PONG}
+
+ test {Negative multi bulk payload} {
+ set fd [$r channel]
+ puts -nonewline $fd "SET x -10\r\n"
+ flush $fd
+ gets $fd
+ } {*invalid bulk*}
+
+ test {Too big bulk payload} {
+ set fd [$r channel]
+ puts -nonewline $fd "SET x 2000000000\r\n"
+ flush $fd
+ gets $fd
+ } {*invalid bulk*count*}
+
+ test {Multi bulk request not followed by bulk args} {
+ set fd [$r channel]
+ puts -nonewline $fd "*1\r\nfoo\r\n"
+ flush $fd
+ gets $fd
+ } {*protocol error*}
+
+ test {Generic wrong number of args} {
+ catch {$r ping x y z} err
+ set _ $err
+ } {*wrong*arguments*ping*}
+
+ test {SELECT an out of range DB} {
+ catch {$r select 1000000} err
+ set _ $err
+ } {*invalid*}
+
+ if {![catch {package require sha1}]} {
+ test {Check consistency of different data types after a reload} {
+ $r flushdb
+ createComplexDataset $r 10000
+ set sha1 [datasetDigest $r]
+ $r debug reload
+ set sha1_after [datasetDigest $r]
+ expr {$sha1 eq $sha1_after}
+ } {1}
+
+ test {Same dataset digest if saving/reloading as AOF?} {
+ $r bgrewriteaof
+ waitForBgrewriteaof $r
+ $r debug loadaof
+ set sha1_after [datasetDigest $r]
+ expr {$sha1 eq $sha1_after}
+ } {1}
+ }
+
+ test {EXPIRES after a reload (snapshot + append only file)} {
+ $r flushdb
+ $r set x 10
+ $r expire x 1000
+ $r save
+ $r debug reload
+ set ttl [$r ttl x]
+ set e1 [expr {$ttl > 900 && $ttl <= 1000}]
+ $r bgrewriteaof
+ waitForBgrewriteaof $r
+ set ttl [$r ttl x]
+ set e2 [expr {$ttl > 900 && $ttl <= 1000}]
+ list $e1 $e2
+ } {1 1}
+
# Leave the user with a clean DB before to exit
test {FLUSHDB} {
set aux {}
if {$::failed > 0} {
puts "\n*** WARNING!!! $::failed FAILED TESTS ***\n"
}
- close $fd
}
proc stress {} {
$r close
}
+# Set a few configuration defaults
+set ::host 127.0.0.1
+set ::port 6379
+set ::stress 0
+set ::flush 0
+set ::first 0
+set ::last 1000000
+
+# Parse arguments
+for {set j 0} {$j < [llength $argv]} {incr j} {
+ set opt [lindex $argv $j]
+ set arg [lindex $argv [expr $j+1]]
+ set lastarg [expr {$arg eq {}}]
+ if {$opt eq {-h} && !$lastarg} {
+ set ::host $arg
+ incr j
+ } elseif {$opt eq {-p} && !$lastarg} {
+ set ::port $arg
+ incr j
+ } elseif {$opt eq {-stress}} {
+ set ::stress 1
+ } elseif {$opt eq {--flush}} {
+ set ::flush 1
+ } elseif {$opt eq {--first} && !$lastarg} {
+ set ::first $arg
+ incr j
+ } elseif {$opt eq {--last} && !$lastarg} {
+ set ::last $arg
+ incr j
+ } else {
+ puts "Wrong argument: $opt"
+ exit 1
+ }
+}
+
# Before to run the test check if DB 9 and DB 10 are empty
set r [redis]
+
+if {$::flush} {
+ $r flushall
+}
+
$r select 9
set db9size [$r dbsize]
$r select 10
unset db9size
unset db10size
-if {[llength $argv] == 0} {
- main 127.0.0.1 6379
-} elseif {[llength $argv] == 1 && [lindex $argv 0] eq {stress}} {
+if {$::stress} {
stress
} else {
- main [lindex $argv 0] [lindex $argv 1]
+ main $::host $::port
}