]> git.saurik.com Git - redis.git/blobdiff - tests/unit/basic.tcl
Type mismatch errors are now prefixed with WRONGTYPE.
[redis.git] / tests / unit / basic.tcl
index 4c6662c67defb0cd5b313eae2cb5bf75638c4ee8..86498d1fa53905516e60f1c3115c85b4efd5ccfe 100644 (file)
@@ -120,7 +120,19 @@ start_server {tags {"basic"}} {
         r incrby novar 17179869184
     } {34359738368}
 
-    test {INCR fails against key with spaces (no integer encoded)} {
+    test {INCR fails against key with spaces (left)} {
+        r set novar "    11"
+        catch {r incr novar} err
+        format $err
+    } {ERR*}
+
+    test {INCR fails against key with spaces (right)} {
+        r set novar "11    "
+        catch {r incr novar} err
+        format $err
+    } {ERR*}
+
+    test {INCR fails against key with spaces (both)} {
         r set novar "    11    "
         catch {r incr novar} err
         format $err
@@ -131,28 +143,123 @@ start_server {tags {"basic"}} {
         catch {r incr mylist} err
         r rpop mylist
         format $err
-    } {ERR*}
+    } {WRONGTYPE*}
 
     test {DECRBY over 32bit value with over 32bit increment, negative res} {
         r set novar 17179869184
         r decrby novar 17179869185
     } {-1}
 
-    test {SETNX target key missing} {
-        r setnx novar2 foobared
-        r get novar2
-    } {foobared}
+    test {INCRBYFLOAT against non existing key} {
+        r del novar
+        list    [roundFloat [r incrbyfloat novar 1]] \
+                [roundFloat [r get novar]] \
+                [roundFloat [r incrbyfloat novar 0.25]] \
+                [roundFloat [r get novar]]
+    } {1 1 1.25 1.25}
+
+    test {INCRBYFLOAT against key originally set with SET} {
+        r set novar 1.5
+        roundFloat [r incrbyfloat novar 1.5]
+    } {3}
+
+    test {INCRBYFLOAT over 32bit value} {
+        r set novar 17179869184
+        r incrbyfloat novar 1.5
+    } {17179869185.5}
+
+    test {INCRBYFLOAT over 32bit value with over 32bit increment} {
+        r set novar 17179869184
+        r incrbyfloat novar 17179869184
+    } {34359738368}
+
+    test {INCRBYFLOAT fails against key with spaces (left)} {
+        set err {}
+        r set novar "    11"
+        catch {r incrbyfloat novar 1.0} err
+        format $err
+    } {ERR*valid*}
+
+    test {INCRBYFLOAT fails against key with spaces (right)} {
+        set err {}
+        r set novar "11    "
+        catch {r incrbyfloat novar 1.0} err
+        format $err
+    } {ERR*valid*}
+
+    test {INCRBYFLOAT fails against key with spaces (both)} {
+        set err {}
+        r set novar " 11 "
+        catch {r incrbyfloat novar 1.0} err
+        format $err
+    } {ERR*valid*}
+
+    test {INCRBYFLOAT fails against a key holding a list} {
+        r del mylist
+        set err {}
+        r rpush mylist 1
+        catch {r incrbyfloat mylist 1.0} err
+        r del mylist
+        format $err
+    } {WRONGTYPE*}
+
+    test {INCRBYFLOAT does not allow NaN or Infinity} {
+        r set foo 0
+        set err {}
+        catch {r incrbyfloat foo +inf} err
+        set err
+        # p.s. no way I can force NaN to test it from the API because
+        # there is no way to increment / decrement by infinity nor to
+        # perform divisions.
+    } {ERR*would produce*}
+
+    test {INCRBYFLOAT decrement} {
+        r set foo 1
+        roundFloat [r incrbyfloat foo -1.1]
+    } {-0.1}
+
+    test "SETNX target key missing" {
+        r del novar
+        assert_equal 1 [r setnx novar foobared]
+        assert_equal "foobared" [r get novar]
+    }
 
-    test {SETNX target key exists} {
-        r setnx novar2 blabla
-        r get novar2
-    } {foobared}
+    test "SETNX target key exists" {
+        r set novar foobared
+        assert_equal 0 [r setnx novar blabla]
+        assert_equal "foobared" [r get novar]
+    }
 
-    test {SETNX against volatile key} {
+    test "SETNX against not-expired volatile key" {
         r set x 10
         r expire x 10000
-        list [r setnx x 20] [r get x]
-    } {0 10}
+        assert_equal 0 [r setnx x 20]
+        assert_equal 10 [r get x]
+    }
+
+    test "SETNX against expired volatile key" {
+        # Make it very unlikely for the key this test uses to be expired by the
+        # active expiry cycle. This is tightly coupled to the implementation of
+        # active expiry and dbAdd() but currently the only way to test that
+        # SETNX expires a key when it should have been.
+        for {set x 0} {$x < 9999} {incr x} {
+            r setex key-$x 3600 value
+        }
+
+        # This will be one of 10000 expiring keys. A cycle is executed every
+        # 100ms, sampling 10 keys for being expired or not.  This key will be
+        # expired for at most 1s when we wait 2s, resulting in a total sample
+        # of 100 keys. The probability of the success of this test being a
+        # false positive is therefore approx. 1%.
+        r set x 10
+        r expire x 1
+
+        # Wait for the key to expire
+        after 2000
+
+        assert_equal 1 [r setnx x 20]
+        assert_equal 20 [r get x]
+    }
 
     test {EXISTS} {
         set res {}
@@ -235,6 +342,25 @@ start_server {tags {"basic"}} {
         format $err
     } {ERR*}
 
+    test {RENAME with volatile key, should move the TTL as well} {
+        r del mykey mykey2
+        r set mykey foo
+        r expire mykey 100
+        assert {[r ttl mykey] > 95 && [r ttl mykey] <= 100}
+        r rename mykey mykey2
+        assert {[r ttl mykey2] > 95 && [r ttl mykey2] <= 100}
+    }
+
+    test {RENAME with volatile key, should not inherit TTL of target key} {
+        r del mykey mykey2
+        r set mykey foo
+        r set mykey2 bar
+        r expire mykey2 100
+        assert {[r ttl mykey] == -1 && [r ttl mykey2] > 0}
+        r rename mykey mykey2
+        r ttl mykey2
+    } {-1}
+
     test {DEL all keys again (DB 0)} {
         foreach key [r keys *] {
             r del $key
@@ -361,17 +487,229 @@ start_server {tags {"basic"}} {
         list [r msetnx x1 xxx y2 yyy] [r get x1] [r get y2]
     } {1 xxx yyy}
 
-    test {STRLEN against non existing key} {
-        r strlen notakey
-    } {0}
+    test "STRLEN against non-existing key" {
+        assert_equal 0 [r strlen notakey]
+    }
 
-    test {STRLEN against integer} {
+    test "STRLEN against integer-encoded value" {
         r set myinteger -555
-        r strlen myinteger
-    } {4}
+        assert_equal 4 [r strlen myinteger]
+    }
 
-    test {STRLEN against plain string} {
+    test "STRLEN against plain string" {
         r set mystring "foozzz0123456789 baz"
-        r strlen mystring
+        assert_equal 20 [r strlen mystring]
+    }
+
+    test "SETBIT against non-existing key" {
+        r del mykey
+        assert_equal 0 [r setbit mykey 1 1]
+        assert_equal [binary format B* 01000000] [r get mykey]
+    }
+
+    test "SETBIT against string-encoded key" {
+        # Ascii "@" is integer 64 = 01 00 00 00
+        r set mykey "@"
+
+        assert_equal 0 [r setbit mykey 2 1]
+        assert_equal [binary format B* 01100000] [r get mykey]
+        assert_equal 1 [r setbit mykey 1 0]
+        assert_equal [binary format B* 00100000] [r get mykey]
+    }
+
+    test "SETBIT against integer-encoded key" {
+        # Ascii "1" is integer 49 = 00 11 00 01
+        r set mykey 1
+        assert_encoding int mykey
+
+        assert_equal 0 [r setbit mykey 6 1]
+        assert_equal [binary format B* 00110011] [r get mykey]
+        assert_equal 1 [r setbit mykey 2 0]
+        assert_equal [binary format B* 00010011] [r get mykey]
+    }
+
+    test "SETBIT against key with wrong type" {
+        r del mykey
+        r lpush mykey "foo"
+        assert_error "WRONGTYPE*" {r setbit mykey 0 1}
+    }
+
+    test "SETBIT with out of range bit offset" {
+        r del mykey
+        assert_error "*out of range*" {r setbit mykey [expr 4*1024*1024*1024] 1}
+        assert_error "*out of range*" {r setbit mykey -1 1}
+    }
+
+    test "SETBIT with non-bit argument" {
+        r del mykey
+        assert_error "*out of range*" {r setbit mykey 0 -1}
+        assert_error "*out of range*" {r setbit mykey 0  2}
+        assert_error "*out of range*" {r setbit mykey 0 10}
+        assert_error "*out of range*" {r setbit mykey 0 20}
+    }
+
+    test "SETBIT fuzzing" {
+        set str ""
+        set len [expr 256*8]
+        r del mykey
+
+        for {set i 0} {$i < 2000} {incr i} {
+            set bitnum [randomInt $len]
+            set bitval [randomInt 2]
+            set fmt [format "%%-%ds%%d%%-s" $bitnum]
+            set head [string range $str 0 $bitnum-1]
+            set tail [string range $str $bitnum+1 end]
+            set str [string map {" " 0} [format $fmt $head $bitval $tail]]
+
+            r setbit mykey $bitnum $bitval
+            assert_equal [binary format B* $str] [r get mykey]
+        }
+    }
+
+    test "GETBIT against non-existing key" {
+        r del mykey
+        assert_equal 0 [r getbit mykey 0]
+    }
+
+    test "GETBIT against string-encoded key" {
+        # Single byte with 2nd and 3rd bit set
+        r set mykey "`"
+
+        # In-range
+        assert_equal 0 [r getbit mykey 0]
+        assert_equal 1 [r getbit mykey 1]
+        assert_equal 1 [r getbit mykey 2]
+        assert_equal 0 [r getbit mykey 3]
+
+        # Out-range
+        assert_equal 0 [r getbit mykey 8]
+        assert_equal 0 [r getbit mykey 100]
+        assert_equal 0 [r getbit mykey 10000]
+    }
+
+    test "GETBIT against integer-encoded key" {
+        r set mykey 1
+        assert_encoding int mykey
+
+        # Ascii "1" is integer 49 = 00 11 00 01
+        assert_equal 0 [r getbit mykey 0]
+        assert_equal 0 [r getbit mykey 1]
+        assert_equal 1 [r getbit mykey 2]
+        assert_equal 1 [r getbit mykey 3]
+
+        # Out-range
+        assert_equal 0 [r getbit mykey 8]
+        assert_equal 0 [r getbit mykey 100]
+        assert_equal 0 [r getbit mykey 10000]
+    }
+
+    test "SETRANGE against non-existing key" {
+        r del mykey
+        assert_equal 3 [r setrange mykey 0 foo]
+        assert_equal "foo" [r get mykey]
+
+        r del mykey
+        assert_equal 0 [r setrange mykey 0 ""]
+        assert_equal 0 [r exists mykey]
+
+        r del mykey
+        assert_equal 4 [r setrange mykey 1 foo]
+        assert_equal "\000foo" [r get mykey]
+    }
+
+    test "SETRANGE against string-encoded key" {
+        r set mykey "foo"
+        assert_equal 3 [r setrange mykey 0 b]
+        assert_equal "boo" [r get mykey]
+
+        r set mykey "foo"
+        assert_equal 3 [r setrange mykey 0 ""]
+        assert_equal "foo" [r get mykey]
+
+        r set mykey "foo"
+        assert_equal 3 [r setrange mykey 1 b]
+        assert_equal "fbo" [r get mykey]
+
+        r set mykey "foo"
+        assert_equal 7 [r setrange mykey 4 bar]
+        assert_equal "foo\000bar" [r get mykey]
+    }
+
+    test "SETRANGE against integer-encoded key" {
+        r set mykey 1234
+        assert_encoding int mykey
+        assert_equal 4 [r setrange mykey 0 2]
+        assert_encoding raw mykey
+        assert_equal 2234 [r get mykey]
+
+        # Shouldn't change encoding when nothing is set
+        r set mykey 1234
+        assert_encoding int mykey
+        assert_equal 4 [r setrange mykey 0 ""]
+        assert_encoding int mykey
+        assert_equal 1234 [r get mykey]
+
+        r set mykey 1234
+        assert_encoding int mykey
+        assert_equal 4 [r setrange mykey 1 3]
+        assert_encoding raw mykey
+        assert_equal 1334 [r get mykey]
+
+        r set mykey 1234
+        assert_encoding int mykey
+        assert_equal 6 [r setrange mykey 5 2]
+        assert_encoding raw mykey
+        assert_equal "1234\0002" [r get mykey]
+    }
+
+    test "SETRANGE against key with wrong type" {
+        r del mykey
+        r lpush mykey "foo"
+        assert_error "WRONGTYPE*" {r setrange mykey 0 bar}
+    }
+
+    test "SETRANGE with out of range offset" {
+        r del mykey
+        assert_error "*maximum allowed size*" {r setrange mykey [expr 512*1024*1024-4] world}
+
+        r set mykey "hello"
+        assert_error "*out of range*" {r setrange mykey -1 world}
+        assert_error "*maximum allowed size*" {r setrange mykey [expr 512*1024*1024-4] world}
+    }
+
+    test "GETRANGE against non-existing key" {
+        r del mykey
+        assert_equal "" [r getrange mykey 0 -1]
+    }
+
+    test "GETRANGE against string value" {
+        r set mykey "Hello World"
+        assert_equal "Hell" [r getrange mykey 0 3]
+        assert_equal "Hello World" [r getrange mykey 0 -1]
+        assert_equal "orld" [r getrange mykey -4 -1]
+        assert_equal "" [r getrange mykey 5 3]
+        assert_equal " World" [r getrange mykey 5 5000]
+        assert_equal "Hello World" [r getrange mykey -5000 10000]
+    }
+
+    test "GETRANGE against integer-encoded value" {
+        r set mykey 1234
+        assert_equal "123" [r getrange mykey 0 2]
+        assert_equal "1234" [r getrange mykey 0 -1]
+        assert_equal "234" [r getrange mykey -3 -1]
+        assert_equal "" [r getrange mykey 5 3]
+        assert_equal "4" [r getrange mykey 3 5000]
+        assert_equal "1234" [r getrange mykey -5000 10000]
+    }
+
+    test "GETRANGE fuzzing" {
+        for {set i 0} {$i < 1000} {incr i} {
+            r set bin [set bin [randstring 0 1024 binary]]
+            set _start [set start [randomInt 1500]]
+            set _end [set end [randomInt 1500]]
+            if {$_start < 0} {set _start "end-[abs($_start)-1]"}
+            if {$_end < 0} {set _end "end-[abs($_end)-1]"}
+            assert_equal [string range $bin $_start $_end] [r getrange bin $start $end]
+        }
     }
 }