use Test::Most 0.22; use Test::RedisDB; use RedisDB; use Digest::SHA qw(sha1_hex); use Time::HiRes qw(usleep); use Scalar::Util qw(blessed); my $server = Test::RedisDB->new; plan( skip_all => "Can't start redis-server" ) unless $server; my $redis = $server->redisdb_client; plan( skip_all => "Test requires redis-server at least 1.2" ) unless $redis->version ge 1.003015; subtest "Keys and strings commands" => \&cmd_keys_strings; subtest "Scan commands" => \&cmd_scan; subtest "Lists commands" => \&cmd_lists; subtest "Hashes commands" => \&cmd_hashes; subtest "Server info commands" => \&cmd_server; subtest "Sets commands" => \&cmd_sets; subtest "Ordered sets commands" => \&cmd_zsets; subtest "HyperLogLog commands" => \&cmd_hyperloglog; subtest "Scripts" => \&cmd_scripts; subtest "Geo" => \&cmd_geo; sub group_pairs { my $ref = shift; my @res; while (@$ref) { push @res, [ shift @$ref, shift @$ref ]; } return @res; } sub cmd_keys_strings { $redis->flushdb; is $redis->set( 'mykey1', 'myvalue1' ), "OK", "SET mykey1"; is $redis->getset( 'mykey1', 'my new value' ), "myvalue1", "GETSET"; is $redis->exists("mykey1"), 1, "EXISTS"; is $redis->exists("mykey 1"), 0, "not EXISTS"; is $redis->setnx( "mykey1", "new value" ), 0, "SETNX (key exists)"; is $redis->rename( "mykey1", "my first key" ), "OK", "RENAME"; is $redis->get("my first key"), "my new value", "GET my new value"; my $expected = "my new value with appendix"; is $redis->append( "my first key", " with appendix" ), length($expected), "APPEND"; is $redis->get("my first key"), $expected, "GOT value with appendix"; if ( $redis->version >= 2.001002 ) { is $redis->strlen("my first key"), length($expected), "STRLEN"; } is $redis->set( "delme", 123 ), "OK", "SET delme"; is $redis->exists("delme"), 1, "delme exists"; is $redis->del("delme"), 1, "DEL delme"; is $redis->exists("delme"), 0, "delme doesn't exist"; is $redis->incr("counter"), 1, "INCR"; is $redis->incrby( "counter", 77 ), 78, "INCRBY 77"; is $redis->decr("counter"), 77, "DECR"; is $redis->decrby( "counter", 35 ), 42, "DECRBY 35"; if ( $redis->version > 2.005 ) { ok abs( $redis->incrbyfloat( "counter", -2.5 ) - 39.5 ) < 1e-7, "INCRBYFLOAT"; } eq_or_diff [ sort @{ $redis->keys('*') } ], [ sort "my first key", "counter" ], "KEYS"; if ( $redis->version >= 2.001008 ) { is $redis->set( "bits", chr(0x55) ), "OK", "set key to 0x55"; is $redis->getbit( "bits", 0 ), 0, "GETBIT 0"; is $redis->getbit( "bits", 1 ), 1, "GETBIT 1"; is $redis->setbit( "bits", 2, 1 ), 0, "SETBIT 2"; is $redis->getbit( "bits", 2 ), 1, "GETBIT 2"; if ( $redis->version >= 2.006 ) { is $redis->bitcount("bits"), 5, "BITCOUNT"; is $redis->bitop( "NOT", "bits", "bits" ), 1, "BITOP NOT"; is $redis->bitcount("bits"), 3, "BITCOUNT"; is $redis->set( "bits1", "\x75" ), "OK", "set bits1 to \\x75"; is $redis->set( "bits2", "\000\x55" ), "OK", "set bits2 to \\000\\x55"; is $redis->bitop( "OR", "bits3", "bits1", "bits2" ), 2, "BITOP OR"; is $redis->get("bits3"), "\x75\x55", "bits3 == bits1 | bits2"; is $redis->set( "bits4", "\xf0\xf0" ), "OK", "set bits4 to \\xf0\\xf0"; is $redis->bitop( "AND", "bits5", "bits3", "bits4" ), 2, "BITOP AND"; is $redis->get("bits5"), "\x70\x50", "bits5 == bits3 & bits4"; is $redis->bitop( "XOR", "bits6", "bits3", "bits4" ), 2, "BITOP XOR"; is $redis->get("bits6"), "\x85\xa5", "bits6 == bits3 ^ bits4"; if ( $redis->version >= 2.008007 ) { $redis->set( "bits7", "\x01\xfe\x01" ); is $redis->bitpos( "bits7", 0, 1 ), 15, "BITPOS"; } else { diag "Skipped tests for redis >= 2.8.7"; } } else { diag "Skipped tests for redis >= 2.6"; } $redis->set( "range_test", "test getrange" ); is $redis->getrange( "range_test", 5, -1 ), "getrange", "GETRANGE"; is $redis->setrange( "range_test", 5, "set" ), 13, "SETRANGE"; is $redis->get("range_test"), "test setrange", "SETRANGE result is correct"; } else { diag "Skipped tests for redis >= 2.1.8"; } is $redis->mset( aaa => 1, bbb => 2, ccc => 3 ), "OK", "MSET"; is $redis->msetnx( ddd => 4, eee => 5, fff => 6 ), 1, "MSETNX 1"; is $redis->msetnx( fff => 7, ggg => 8, hhh => 9 ), 0, "MSETNX 0"; eq_or_diff $redis->mget(qw(aaa bbb eee fff hhh)), [ qw(1 2 5 6), undef ], "MGET"; is $redis->renamenx( eee => 'iii' ), 1, "RENAMENX 1"; is $redis->renamenx( ddd => 'fff' ), 0, "RENAMENX 0"; eq_or_diff $redis->mget(qw(eee iii ddd fff)), [ undef, qw(5 4 6) ], "RENAMENX works correctly"; if ( $redis->version >= 2.001002 ) { is $redis->setex( "expires", 2, "in two seconds" ), "OK", "SETEX"; ok $redis->ttl("expires") > 0, "TTL >0"; $redis->set( "persistent", "value" ); is $redis->ttl("persistent"), -1, "TTL -1"; is $redis->expire( "persistent", 100 ), 1, "EXPIRE"; ok $redis->ttl("persistent") > 98, "TTL >98"; is $redis->expireat( "persistent", time + 10 ), 1, "EXPIREAT"; my $ttl = $redis->ttl("persistent"); ok( $ttl <= 10 && $ttl > 8, "Correct TTL" ); is $redis->persist("persistent"), 1, "PERSIST"; is $redis->ttl("persistent"), -1, "key will persist"; sleep 3; is $redis->exists("expires"), 0, "expired key was deleted"; } if ( $redis->version >= 2.005 ) { is $redis->setex( "pexpires", 10, "in two seconds" ), "OK", "SETEX"; ok $redis->pttl("pexpires") > 1000, "PTTL > 1000"; is $redis->pexpire( "pexpires", 9000 ), 1, "PEXPIRE"; ok $redis->ttl("pexpires") < 10, "TTL < 10"; is $redis->psetex( "pexpires", 20_000, "20 seconds" ), "OK", "PSETEX"; ok $redis->ttl("pexpires") > 10, "TTL > 10"; is $redis->pexpireat( "pexpires", time * 1000 + 1100 ), 1, "PEXPIREAT"; usleep 1_200_000; is $redis->get("pexpires"), undef, "expired"; } if ( $redis->version >= 2.002003 ) { is $redis->set(qw(object test)), "OK", "Set object"; is $redis->object_refcount("object"), 1, "OBJECT REFCOUNT"; like $redis->object_encoding("object"), qr/raw|embstr/, "OBJECT ENCODING"; my $idle = $redis->object_idletime("object"); ok $idle >= 0 && $idle < 11, "OBJECT IDLETIME"; } if ( $redis->version >= 2.005011 ) { is $redis->set(qw(dump test)), "OK", "Set dump"; my $dump = $redis->dump("dump"); ok $dump, "DUMP"; $redis->del("dump"); is $redis->restore( "dump", 0, $dump ), "OK", "RESTORE"; is $redis->get("dump"), "test", "Restored"; } } sub cmd_scan { plan skip_all => "testing SCAN requires redis 2.8.0" if $redis->version < 2.008; $redis->flushdb; my @all_keys; for ( 1 .. 40 ) { $redis->set( "key$_", $_, RedisDB::IGNORE_REPLY ); push @all_keys, "key$_"; } eq_or_diff [ sort @{ $redis->keys('*') } ], [ sort @all_keys ], "KEYS returned expected list of keys"; my ( $cnt, $cursor ) = ( 0, 0 ); my @keys; while ( $cnt++ < 5 ) { my $res = $redis->scan( $cursor, 'COUNT', 20 ); fail "SCAN returned an error: $res" if blessed $res; $cursor = $res->[0]; push @keys, @{ $res->[1] }; last unless $cursor; } fail "haven't scanned all the keys after $cnt iterations" if $cnt > 5; eq_or_diff [ sort @keys ], [ sort @all_keys ], "SCAN returned all expected keys"; eq_or_diff [ sort @{ $redis->scan_all } ], [ sort @all_keys ], "scan_all returned all keys"; eq_or_diff [ sort @{ $redis->scan_all( MATCH => "*3" ) } ], [ sort grep { /3$/ } @all_keys ], "scan_all returned all matched keys"; is $redis->hmset( "test_hash", map { ( $_, "${_}value" ) } @all_keys ), "OK", "initialized hash with HMSET"; my $hscan = $redis->hscan( "test_hash", 0, "MATCH", "*4", "COUNT", 100 ); is $hscan->[0], 0, "got all matching keys in a single HSCAN call"; eq_or_diff [ sort { $a->[0] cmp $b->[0] } group_pairs $hscan->[1] ], [ map { [ $_, "${_}value" ] } sort grep { /4$/ } @all_keys ], "correct list of keys from HSCAN"; eq_or_diff $redis->hscan_all( "test_hash", MATCH => "*99*" ), [], "hscan_all returned empty list when no key matched"; eq_or_diff $redis->hscan_all( "test_hash", MATCH => "key1" ), [ key1 => "key1value" ], "hscan_all returned expected list of keys/values"; is $redis->sadd( "test_set", @all_keys ), 40, "initialized a set"; my $sscan = $redis->sscan( "test_set", 0, "MATCH", "*3", "COUNT", 100 ); is $sscan->[0], 0, "got all matching elements in a single SSCAN call"; eq_or_diff [ sort @{ $sscan->[1] } ], [ sort grep { /3$/ } @all_keys ], "correct list of elements from SSCAN"; eq_or_diff [ sort @{ $redis->sscan_all( "test_set", MATCH => "*3" ) } ], [ sort grep { /3$/ } @all_keys ], "sscan_all returned correct list of elements"; is $redis->zadd( "test_zset", map { ( $_, "key$_" ) } 1 .. 40 ), 40, "initialized a sorted set"; my $zscan = $redis->zscan( "test_zset", 0, "MATCH", "*2", "COUNT", 100 ); is $zscan->[0], 0, "got all matching elements in a single ZSCAN call"; my $expect = [ sort { $a->[0] cmp $b->[0] } grep { $_->[0] =~ /2$/ } map { [ "key$_", $_ ] } 1 .. 40 ]; eq_or_diff [ sort { $a->[0] cmp $b->[0] } group_pairs $zscan->[1] ], $expect, "correct list of elements from ZSCAN"; eq_or_diff [ sort { $a->[0] cmp $b->[0] } group_pairs $redis->zscan_all( "test_zset", MATCH => "*2" ) ], $expect, "zscan_all returned correct list of elements"; } sub cmd_lists { $redis->flushdb; is $redis->llen("list1"), 0, "LLEN of empty list is 0"; is $redis->rpush( "list1", "V1" ), 1, "RPUSH"; is $redis->lpush( "list1", "V2" ), 2, "LPUSH"; is $redis->rpop("list1"), "V1", "RPOP"; for (qw(V3 V4 V5 V6)) { $redis->rpush( "list1", $_ ); } is $redis->llen("list1"), 5, "LLEN"; is $redis->lindex( "list1", 33 ), undef, "LINDEX for out of range value"; is $redis->lindex( "list1", 1 ), "V3", "LINDEX"; is $redis->lpop("list1"), "V2", "LPOP"; eq_or_diff $redis->lrange( "list1", 1, -2 ), [qw(V4 V5)], "LRANGE"; is $redis->lrem( "list1", 0, "V5" ), 1, "LREM"; is $redis->lset( "list1", 2, "VVI" ), "OK", "LSET"; eq_or_diff $redis->lrange( "list1", 0, -1 ), [qw(V3 V4 VVI)], "LRANGE"; for (qw(V7 V8 V9 V0)) { $redis->rpush( "list1", $_ ); } is $redis->ltrim( "list1", 2, -3 ), "OK", "LTRIM"; eq_or_diff $redis->lrange( "list1", 0, -1 ), [qw(VVI V7 V8)], "LTREAM result is correct"; is $redis->rpoplpush( "list1", "list2" ), "V8", "RPOPLPUSH"; is $redis->llen("list1"), 2, "list1 len is 2"; eq_or_diff $redis->blpop( "list1", "list2", 0 ), [qw(list1 VVI)], "BLPOP"; eq_or_diff $redis->brpop( "list2", "list1", 0 ), [qw(list2 V8)], "BRPOP"; if ( $redis->version >= 2.001001 ) { is $redis->linsert( "list1", "BEFORE", "V7", "V1" ), 2, "LINSERT BEFORE"; is $redis->linsert( "list1", "AFTER", "V1", "V3" ), 3, "LINSERT AFTER"; is $redis->rpushx( "list1", "V8" ), 4, "RPUSHX"; is $redis->lpushx( "list3", "V0" ), 0, "LPUSHX"; eq_or_diff $redis->lrange( "list1", 0, -1 ), [qw(V1 V3 V7 V8)], "list1 contains expected values"; if ( $redis->version >= 2.001007 ) { is $redis->brpoplpush( "list1", "list3", 0 ), "V8", "BRPOPLPUSH"; } } } sub cmd_hashes { $redis->flushdb; is $redis->hset( 'thash', keyb => 'value b' ), 1, "HSET new key"; is $redis->hset( 'thash', keyb => 'valueb' ), 0, "HSET updated key"; is $redis->hsetnx( 'thash', keya => 'valuea' ), 1, "HSETNX new key"; is $redis->hsetnx( 'thash', keyb => 'valueb' ), 0, "HSETNX existing key"; is $redis->hmset( 'thash', keyc => 'valuec', keyd => 'valued' ), 'OK', "HMSET"; is $redis->hexists( 'thash', 'counter' ), 0, "HEXISTS == 0"; is $redis->hincrby( 'thash', counter => 3 ), 3, "HINCRBY new key"; is $redis->hincrby( 'thash', counter => 4 ), 7, "HINCRBY existing key"; is $redis->hexists( 'thash', 'counter' ), 1, "HEXISTS == 1"; is $redis->hget( 'thash', 'counter' ), 7, "HGET"; if ( $redis->version > 2.005 ) { ok abs( $redis->hincrbyfloat( 'thash', 'counter', '-2.5' ) - 4.5 ) < 1e-7, "HINCRBYFLOAT"; } is $redis->hdel( 'thash', 'counter' ), 1, "HDEL"; my %thash = @{ $redis->hgetall('thash') }; my @thash = map { $_ => $thash{$_} } sort keys %thash; eq_or_diff \@thash, [qw(keya valuea keyb valueb keyc valuec keyd valued)], "HGETALL"; eq_or_diff [ sort @{ $redis->hkeys('thash') } ], [qw(keya keyb keyc keyd)], "HKEYS"; eq_or_diff [ sort @{ $redis->hvals('thash') } ], [qw(valuea valueb valuec valued)], "HVALS"; is $redis->hlen('thash'), 4, "HLEN"; eq_or_diff $redis->hmget( 'thash', qw(keyb counter keya) ), [ 'valueb', undef, 'valuea' ], 'HMGET'; } sub cmd_server { my $info = $redis->info; is ref($info), "HASH", "Got hashref from info"; ok exists $info->{redis_version}, "There's redis_version in the hash"; ok $info->{redis_version} =~ /^([0-9]+)[.]([0-9]+)(?:[.]([0-9]+))?/, "it looks like version number"; my $version = 0 + $1 + 0.001 * $2 + ( $3 ? 0.000001 * $3 : 0 ); is '' . $redis->version, "$version", "Correct server version: $version"; my $info2; $redis->info( sub { $info2 = $_[1] } ); $redis->mainloop; is ref($info2), "HASH", "Got hashref in info callback"; is $info2->{redis_version}, $info->{redis_version}, "Same info as from synchronous call"; if ( $redis->version ge 2.006009 ) { eq_or_diff $redis->config_get("dbfilename"), [qw(dbfilename dump_test.rdb)], "CONFIG GET"; my ( $sec, $ms ) = @{ $redis->time }; ok time - $sec < 2, "Server time is correct"; my $redis2 = $server->redisdb_client(connection_name => 'bar'); is $redis->client_getname, undef, "Name for connection is not set"; is $redis->client_setname("foo"), "OK", "Set it to 'foo'"; is $redis->client_getname, "foo", "Now connection name is 'foo'"; my $clients = $redis->client_list; is 0 + @$clients, 2, "Two clients connected to the server"; unless ( $clients->[0]{name} eq 'foo' ) { @$clients = reverse @$clients; } is $clients->[0]{name}, "foo", "First client's name 'foo'"; is $clients->[1]{name}, "bar", "Another's is 'bar'"; is $redis->client_kill( $clients->[1]{addr} ), "OK", "Killed 'bar' connection ($clients->[1]{addr})"; $redis->client_list( sub { $clients = $_[1] } ); $redis->mainloop; is 0 + @$clients, 1, "Only one client is connected"; is $redis2->client_getname, "bar", "Second connection is restored with name 'bar'"; } else { diag "Skipped tests for redis >= 2.6.9"; } if ( $redis->version ge 2.008008 ) { throws_ok { $redis->debug_error("Boo!"); } qr/Boo!/, "DEBUG ERROR"; } else { diag "Skipped tests for redis >= 2.8.8"; } } sub cmd_sets { $redis->flushdb; is $redis->sadd( "set1", "A" ), 1, "SADD set1 A"; is $redis->sadd( "set1", "B" ), 1, "SADD set1 B"; is $redis->sadd( "set1", "C" ), 1, "SADD set1 C"; is $redis->sadd( "set1", "A" ), 0, "SADD set1 A"; is $redis->sadd( "set1", "D" ), 1, "SADD set1 D"; is $redis->scard("set1"), 4, "4 elements in set1"; is $redis->sadd( "set2", "B" ), 1, "SADD set2 B"; is $redis->sadd( "set2", "D" ), 1, "SADD set2 D"; is $redis->sadd( "set2", "F" ), 1, "SADD set2 F"; eq_or_diff [ sort @{ $redis->sdiff( "set1", "set2" ) } ], [qw(A C)], "SDIFF"; is $redis->sdiffstore( "set3", "set2", "set1" ), 1, "SDIFFSTORE set3 set2 set1"; is $redis->sdiffstore( "set4", "set1", "set2" ), 2, "SDIFFSTORE set4 set1 set2"; eq_or_diff $redis->sinter( "set3", "set4" ), [], "SINTER set3 set4 is empty"; is $redis->sinterstore( "set5", "set1", "set2" ), 2, "SINTERSTORE set5 set1 set2"; is $redis->sismember( "set3", "F" ), 1, "SISMEMBER"; is $redis->sismember( "set3", "B" ), 0, "not SISMEMBER"; eq_or_diff [ sort @{ $redis->smembers("set5") } ], [qw(B D)], "SMEMBERS set5"; is $redis->smove( "set3", "set5", "B" ), 0, "SMOVE"; is $redis->smove( "set3", "set5", "F" ), 1, "SMOVE"; eq_or_diff [ sort @{ $redis->sunion( "set4", "set5" ) } ], [qw(A B C D F)], "SUNION"; is $redis->sunionstore( "big_set", "set4", "set5" ), 5, "SUNIONSTORE"; is $redis->sunionstore( "large_set", "big_set" ), 5, "copy set"; is $redis->sismember( "big_set", $redis->srandmember("big_set") ), 1, "SRANDMEMBER"; eq_or_diff $redis->sdiff( "large_set", "big_set" ), [], "SDIFF empty"; my $elem = $redis->spop("big_set"); eq_or_diff $redis->sdiff( "large_set", "big_set" ), [$elem], "SPOP removed element"; is $redis->srem( "large_set", $elem ), 1, "SREM"; eq_or_diff $redis->sdiff( "large_set", "big_set" ), [], "SREM removed element"; } sub cut_precision { @_ = @{ +shift }; my @res; while (@_) { push @res, shift; push @res, 0 + sprintf "%.3f", shift; } return \@res; } sub cmd_zsets { $redis->flushdb; is $redis->zadd( "zset1", 1.24, "one" ), 1, "ZADD add"; is $redis->zadd( "zset1", 1, "one" ), 0, "ZADD update"; my @zset = ( 3.2 => "three", 2.1 => "two", 7 => "four", 5 => "five", 4.1 => "four" ); $redis->zadd( "zset1", splice @zset, 0, 2 ) while @zset; is $redis->zcard("zset1"), 5, "ZCARD"; is $redis->zcount( "zset1", 1.1, 4 ), 2, "ZCOUNT"; is sprintf( "%.2f", $redis->zincrby( "zset1", 0.1, "two" ) ), "2.20", "ZINCRBY"; is sprintf( "%.2f", $redis->zincrby( "zset1", 0, "two" ) ), "2.20", "ZINCRBY 0"; my @zset2 = ( 1 => "A", 2 => "B", 3 => "C", 4 => "D", 5 => "E", 6 => "F" ); $redis->zadd( "zset2", splice @zset2, 0, 2 ) while @zset2; my @zset3 = ( 0.5 => "A", 0.4 => "B", 0.3 => "C", 0.2 => "D", 0.1 => "E" ); $redis->zadd( "zset3", splice @zset3, 0, 2 ) while @zset3; is $redis->zinterstore( "zsum", 2, "zset2", "zset3" ), 5, "ZINTERSTORE"; eq_or_diff cut_precision( $redis->zrange( "zsum", 0, -1, "WITHSCORES" ) ), [qw(A 1.5 B 2.4 C 3.3 D 4.2 E 5.1)], "ZRANGE"; is $redis->zinterstore( "zmax", 2, "zset2", "zset3", "weights", 1, 6, "aggregate", "max" ), 5, "ZINTERSTORE max"; eq_or_diff cut_precision( $redis->zrange( "zmax", 0, -1, "WITHSCORES" ) ), [qw(B 2.4 A 3 C 3 D 4 E 5)], "ZRANGE"; eq_or_diff $redis->zrangebyscore( "zmax", '(3', '5' ), [qw(D E)], "ZRANGEBYSCORE"; is $redis->zrank( "zmax", "D" ), 3, "ZRANK"; is $redis->zrem( "zmax", 'C' ), 1, "ZREM"; is $redis->zrem( "zmax", 'E' ), 1, "ZREM"; is $redis->zrem( "zmax", 'F' ), 0, "ZREM"; eq_or_diff $redis->zrange( "zmax", 0, -1 ), [qw(B A D)], "check result of ZREM"; is $redis->zremrangebyrank( "zmax", 1, 1 ), 1, "ZREMRANGEBYRANK"; eq_or_diff $redis->zrange( "zmax", 0, -1 ), [qw(B D)], "check result of ZREMANGEBYRANK"; if ( $redis->version >= 2.001006 ) { is $redis->zremrangebyscore( "zmax", 3, "+inf" ), 1, "ZREMRANGEBYSCORE"; eq_or_diff $redis->zrange( "zmax", 0, -1 ), [qw(B)], "check result of ZREMRANGEBYSCORE"; eq_or_diff $redis->zrevrange( "zset2", 0, -1 ), [ reverse @{ $redis->zrange( "zset2", 0, -1 ) } ], "ZREVRANGE"; eq_or_diff $redis->zrevrangebyscore( "zset2", 6, 1 ), [ reverse @{ $redis->zrangebyscore( "zset2", 1, 6 ) } ], "ZREVRANGEBYSCORE"; is $redis->zrevrank( "zset2", "D" ), 2, "ZREVRANK"; is $redis->zscore( "zset2", "D" ), 4, "ZSCORE"; is $redis->zunionstore( "zunion", 2, "zset2", "zset3", "aggregate", "sum" ), 6, "ZUNIONSTORE"; eq_or_diff cut_precision( $redis->zrange( "zunion", 0, -1, "WITHSCORES" ) ), [qw(A 1.5 B 2.4 C 3.3 D 4.2 E 5.1 F 6)], "ZUNIONSTORE result is correct"; } if ( $redis->version >= 2.008009 ) { is $redis->zadd( 'zlex', qw(0 a 0 b 0 c 0 d 0 e 0 f 0 g) ), 7, "added 7 elements to zlex"; eq_or_diff $redis->zrangebylex( 'zlex', '-', '[c' ), [qw( a b c )], "ZRAGEBYLEX"; is $redis->zlexcount( 'zlex', '(b', '[e' ), 3, "ZLEXCOUNT"; is $redis->zremrangebylex( 'zlex', '(b', '[e' ), 3, "ZREMRANGEBYLEX"; eq_or_diff $redis->zrange( 'zlex', 0, -1 ), [qw(a b f g)], "correct set after ZREMRANGEBYLEX"; } } sub cmd_scripts { plan skip_all => "This test requires redis-server >= 2.6" unless $redis->version >= 2.005; $redis->flushdb; is $redis->script_flush, 'OK', "SCRIPT FLUSH"; my $script1 = "return {1,2,{3,'test',ARGV[1]}}"; my $sha1 = sha1_hex($script1); eq_or_diff $redis->eval( $script1, 0, 'passed' ), [ 1, 2, [ 3, 'test', 'passed' ] ], "EVAL"; my $script2 = "return redis.call('set',KEYS[1],ARGV[1])"; my $sha2 = sha1_hex($script2); is $redis->eval( $script2, 1, 'eval', 'passed' ), "OK", "eval set"; my $script3 = "return redis.call('get',KEYS[1])"; my $sha3 = sha1_hex($script3); eq_or_diff $redis->script_exists( $sha1, $sha3, $sha2 ), [ 1, 0, 1 ], "SCRIPT EXISTS"; is $redis->script_load($script3), $sha3, "SCRIPT LOAD"; eq_or_diff $redis->evalsha( $sha3, 1, 'eval' ), "passed", "EVALSHA"; } sub cmd_hyperloglog { plan skip_all => "This test requires redis-server >= 2.8.9" unless $redis->version >= 2.008009; $redis->flushdb; is $redis->pfadd( 'hll1', qw(a b c d) ), 1, "PFADD"; is $redis->pfcount('hll1'), 4, "PFCOUNT"; is $redis->pfadd( 'hll2', qw(a b e f) ), 1, "PFADD"; is $redis->pfmerge( 'hll3', 'hll1', 'hll2' ), 'OK', "PFMERGE"; } sub cmd_geo { plan skip_all => "ENABLE_GEO is not set" unless $ENV{ENABLE_GEO}; $redis->flushdb; is $redis->geoadd( 'China', 116.383333, 39.916667, 'Beijing', 126.633333, 45.75, 'Harbin', 104.064722, 30.658611, 'Chengdu', 102.683333, 25.066667, 'Kunming', 100.266667, 25.6, 'Dali', 100.233333, 26.883333, 'Lijiang' ), 6, "GEOADD"; is int $redis->geodist( 'China', 'Chengdu', 'Beijing', 'km' ), 1517, "GEODIST"; eq_or_diff $redis->geohash( 'China', 'Beijing', 'Harbin', 'Kunming' ), [qw(wx4g06eg2j0 yb4h38e94d0 wk3n8e5xzj0)], "GEOHASH"; cmp_deeply $redis->geopos( 'China', 'Dali', 'Lijiang' ), [ [ num( 100.266, 0.001 ), num( 25.6, 0.001 ) ], [ num( 100.233, 0.001 ), num( 26.883, 0.001 ) ] ], "GEOPOS"; eq_or_diff $redis->georadius( 'China', 103.3325, 29.519722, 550, 'km', 'ASC' ), [ 'Chengdu', 'Lijiang', 'Kunming', 'Dali' ], "GEORADIUS"; cmp_deeply $redis->georadiusbymember( 'China', 'Dali', 160, 'mi', 'WITHDIST', 'WITHCOORD' ), [ [ 'Dali', num( 0, 0 ), [ num( 100.266667, 0.001 ), num( 25.6, 0.001 ) ] ], [ 'Lijiang', num( 88, 1 ), [ num( 100.233, 0.001 ), num( 26.883, 0.001 ) ] ], [ 'Kunming', num( 155, 1 ), [ num( 102.683, 0.001 ), num( 25.066, 0.001 ) ] ], ], "GEORADIUSBYMEMBER"; } done_testing;