summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Vosmaer <jacob@gitlab.com>2017-01-02 16:12:06 +0100
committerJacob Vosmaer <jacob@gitlab.com>2017-01-02 16:29:14 +0100
commit3fe9cea03a6384fd8f57f10e172c134ed5c0552d (patch)
tree1148f745058b8885cd07edeb414f5822b05d616f
parenta3712cc18de8283b25c3a8a034ecc8c9b7feca48 (diff)
downloadgitlab-shell-3fe9cea03a6384fd8f57f10e172c134ed5c0552d.tar.gz
Vendor redis_rb from Rubygems, not GitHubredis-full-gem
Also just include the entire gem. Space used is negligible and this way we don't have to think about what to leave in/out. Create a vendor-gem script in Ruby instead of Make.
-rw-r--r--.gitignore1
-rw-r--r--Makefile12
-rw-r--r--lib/vendor/redis/.gitignore16
-rw-r--r--lib/vendor/redis/.travis.yml65
-rw-r--r--lib/vendor/redis/.travis/Gemfile11
-rw-r--r--lib/vendor/redis/.yardopts3
-rw-r--r--lib/vendor/redis/CHANGELOG.md363
-rw-r--r--lib/vendor/redis/Gemfile4
-rw-r--r--lib/vendor/redis/LICENSE20
-rw-r--r--lib/vendor/redis/README.md410
-rw-r--r--lib/vendor/redis/Rakefile87
-rw-r--r--lib/vendor/redis/benchmarking/logging.rb71
-rw-r--r--lib/vendor/redis/benchmarking/pipeline.rb51
-rw-r--r--lib/vendor/redis/benchmarking/speed.rb21
-rw-r--r--lib/vendor/redis/benchmarking/suite.rb24
-rw-r--r--lib/vendor/redis/benchmarking/worker.rb71
-rw-r--r--lib/vendor/redis/examples/basic.rb15
-rw-r--r--lib/vendor/redis/examples/consistency.rb114
-rw-r--r--lib/vendor/redis/examples/dist_redis.rb43
-rw-r--r--lib/vendor/redis/examples/incr-decr.rb17
-rw-r--r--lib/vendor/redis/examples/list.rb26
-rw-r--r--lib/vendor/redis/examples/pubsub.rb37
-rw-r--r--lib/vendor/redis/examples/sentinel.rb41
-rwxr-xr-xlib/vendor/redis/examples/sentinel/start49
-rw-r--r--lib/vendor/redis/examples/sets.rb36
-rw-r--r--lib/vendor/redis/examples/unicorn/config.ru3
-rw-r--r--lib/vendor/redis/examples/unicorn/unicorn.rb20
-rw-r--r--lib/vendor/redis/redis.gemspec44
-rw-r--r--lib/vendor/redis/test/bitpos_test.rb69
-rw-r--r--lib/vendor/redis/test/blocking_commands_test.rb42
-rw-r--r--lib/vendor/redis/test/client_test.rb59
-rw-r--r--lib/vendor/redis/test/command_map_test.rb30
-rw-r--r--lib/vendor/redis/test/commands_on_hashes_test.rb21
-rw-r--r--lib/vendor/redis/test/commands_on_hyper_log_log_test.rb21
-rw-r--r--lib/vendor/redis/test/commands_on_lists_test.rb20
-rw-r--r--lib/vendor/redis/test/commands_on_sets_test.rb77
-rw-r--r--lib/vendor/redis/test/commands_on_sorted_sets_test.rb137
-rw-r--r--lib/vendor/redis/test/commands_on_strings_test.rb101
-rw-r--r--lib/vendor/redis/test/commands_on_value_types_test.rb133
-rw-r--r--lib/vendor/redis/test/connection_handling_test.rb277
-rw-r--r--lib/vendor/redis/test/distributed_blocking_commands_test.rb46
-rw-r--r--lib/vendor/redis/test/distributed_commands_on_hashes_test.rb10
-rw-r--r--lib/vendor/redis/test/distributed_commands_on_hyper_log_log_test.rb33
-rw-r--r--lib/vendor/redis/test/distributed_commands_on_lists_test.rb22
-rw-r--r--lib/vendor/redis/test/distributed_commands_on_sets_test.rb83
-rw-r--r--lib/vendor/redis/test/distributed_commands_on_sorted_sets_test.rb18
-rw-r--r--lib/vendor/redis/test/distributed_commands_on_strings_test.rb59
-rw-r--r--lib/vendor/redis/test/distributed_commands_on_value_types_test.rb95
-rw-r--r--lib/vendor/redis/test/distributed_commands_requiring_clustering_test.rb164
-rw-r--r--lib/vendor/redis/test/distributed_connection_handling_test.rb23
-rw-r--r--lib/vendor/redis/test/distributed_internals_test.rb79
-rw-r--r--lib/vendor/redis/test/distributed_key_tags_test.rb52
-rw-r--r--lib/vendor/redis/test/distributed_persistence_control_commands_test.rb26
-rw-r--r--lib/vendor/redis/test/distributed_publish_subscribe_test.rb92
-rw-r--r--lib/vendor/redis/test/distributed_remote_server_control_commands_test.rb66
-rw-r--r--lib/vendor/redis/test/distributed_scripting_test.rb102
-rw-r--r--lib/vendor/redis/test/distributed_sorting_test.rb20
-rw-r--r--lib/vendor/redis/test/distributed_test.rb58
-rw-r--r--lib/vendor/redis/test/distributed_transactions_test.rb32
-rw-r--r--lib/vendor/redis/test/encoding_test.rb18
-rw-r--r--lib/vendor/redis/test/error_replies_test.rb59
-rw-r--r--lib/vendor/redis/test/fork_safety_test.rb65
-rw-r--r--lib/vendor/redis/test/helper.rb232
-rw-r--r--lib/vendor/redis/test/helper_test.rb24
-rw-r--r--lib/vendor/redis/test/internals_test.rb437
-rw-r--r--lib/vendor/redis/test/lint/blocking_commands.rb150
-rw-r--r--lib/vendor/redis/test/lint/hashes.rb162
-rw-r--r--lib/vendor/redis/test/lint/hyper_log_log.rb60
-rw-r--r--lib/vendor/redis/test/lint/lists.rb143
-rw-r--r--lib/vendor/redis/test/lint/sets.rb125
-rw-r--r--lib/vendor/redis/test/lint/sorted_sets.rb316
-rw-r--r--lib/vendor/redis/test/lint/strings.rb260
-rw-r--r--lib/vendor/redis/test/lint/value_types.rb122
-rw-r--r--lib/vendor/redis/test/persistence_control_commands_test.rb26
-rw-r--r--lib/vendor/redis/test/pipelining_commands_test.rb242
-rw-r--r--lib/vendor/redis/test/publish_subscribe_test.rb282
-rw-r--r--lib/vendor/redis/test/remote_server_control_commands_test.rb118
-rw-r--r--lib/vendor/redis/test/scanning_test.rb413
-rw-r--r--lib/vendor/redis/test/scripting_test.rb78
-rw-r--r--lib/vendor/redis/test/sentinel_command_test.rb80
-rw-r--r--lib/vendor/redis/test/sentinel_test.rb255
-rw-r--r--lib/vendor/redis/test/sorting_test.rb59
-rw-r--r--lib/vendor/redis/test/ssl_test.rb66
-rw-r--r--lib/vendor/redis/test/support/connection/hiredis.rb1
-rw-r--r--lib/vendor/redis/test/support/connection/ruby.rb1
-rw-r--r--lib/vendor/redis/test/support/connection/synchrony.rb17
-rw-r--r--lib/vendor/redis/test/support/redis_mock.rb130
-rwxr-xr-xlib/vendor/redis/test/support/ssl/gen_certs.sh31
-rw-r--r--lib/vendor/redis/test/support/ssl/trusted-ca.crt25
-rw-r--r--lib/vendor/redis/test/support/ssl/trusted-ca.key27
-rw-r--r--lib/vendor/redis/test/support/ssl/trusted-cert.crt81
-rw-r--r--lib/vendor/redis/test/support/ssl/trusted-cert.key28
-rw-r--r--lib/vendor/redis/test/support/ssl/untrusted-ca.crt26
-rw-r--r--lib/vendor/redis/test/support/ssl/untrusted-ca.key27
-rw-r--r--lib/vendor/redis/test/support/ssl/untrusted-cert.crt82
-rw-r--r--lib/vendor/redis/test/support/ssl/untrusted-cert.key28
-rw-r--r--lib/vendor/redis/test/support/wire/synchrony.rb24
-rw-r--r--lib/vendor/redis/test/support/wire/thread.rb5
-rw-r--r--lib/vendor/redis/test/synchrony_driver.rb88
-rw-r--r--lib/vendor/redis/test/test.conf.erb9
-rw-r--r--lib/vendor/redis/test/thread_safety_test.rb62
-rw-r--r--lib/vendor/redis/test/transactions_test.rb264
-rw-r--r--lib/vendor/redis/test/unknown_commands_test.rb14
-rw-r--r--lib/vendor/redis/test/url_param_test.rb138
-rwxr-xr-xsupport/vendor-gem.rb38
105 files changed, 8668 insertions, 12 deletions
diff --git a/.gitignore b/.gitignore
index f41180b..ef7f0ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ tags
.bundle/
custom_hooks
hooks/*.d
+/*.gem
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 2a78178..0000000
--- a/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-REDIS_RB_VERSION=v3.3.0
-REDIS_RB_VENDOR_DIR=lib/vendor/redis
-PWD=`pwd`
-
-all:
-
-update-redis:
- rm -rf $(REDIS_RB_VENDOR_DIR)
- git clone -b $(REDIS_RB_VERSION) https://github.com/redis/redis-rb.git $(REDIS_RB_VENDOR_DIR)
- rm -rf $(REDIS_RB_VENDOR_DIR)/.git
-
-.PHONY=update-redis
diff --git a/lib/vendor/redis/.gitignore b/lib/vendor/redis/.gitignore
new file mode 100644
index 0000000..9f59639
--- /dev/null
+++ b/lib/vendor/redis/.gitignore
@@ -0,0 +1,16 @@
+*.rdb
+*.swp
+Gemfile.lock
+*.gem
+/tmp/
+/.idea
+/.yardoc
+/coverage/*
+/doc/
+/examples/sentinel/sentinel.conf
+/nohup.out
+/pkg/*
+/rdsrv
+/redis/*
+/test/db
+/test/test.conf
diff --git a/lib/vendor/redis/.travis.yml b/lib/vendor/redis/.travis.yml
new file mode 100644
index 0000000..5f97abf
--- /dev/null
+++ b/lib/vendor/redis/.travis.yml
@@ -0,0 +1,65 @@
+language: ruby
+
+rvm:
+ - 1.8.7
+ - 1.9.3
+ - 2.0
+ - 2.1
+ - 2.2
+ - 2.3.0
+ - jruby-18mode
+ - jruby-19mode
+ - jruby-9.0.5.0
+ - rbx-2
+
+gemfile: ".travis/Gemfile"
+
+sudo: false
+
+env:
+ global:
+ - VERBOSE=true
+ - TIMEOUT=1
+ matrix:
+ - conn=ruby REDIS_BRANCH=2.8
+ - conn=hiredis REDIS_BRANCH=2.8
+ - conn=synchrony REDIS_BRANCH=2.8
+ - conn=ruby REDIS_BRANCH=unstable
+
+branches:
+ only:
+ - master
+
+matrix:
+ exclude:
+ # hiredis
+ - rvm: jruby-18mode
+ gemfile: .travis/Gemfile
+ env: conn=hiredis REDIS_BRANCH=2.8
+ - rvm: jruby-19mode
+ gemfile: .travis/Gemfile
+ env: conn=hiredis REDIS_BRANCH=2.8
+ - rvm: jruby-9.0.5.0
+ gemfile: .travis/Gemfile
+ env: conn=hiredis REDIS_BRANCH=2.8
+
+ # synchrony
+ - rvm: 1.8.7
+ gemfile: .travis/Gemfile
+ env: conn=synchrony REDIS_BRANCH=2.8
+ - rvm: jruby-18mode
+ gemfile: .travis/Gemfile
+ env: conn=synchrony REDIS_BRANCH=2.8
+ - rvm: jruby-19mode
+ gemfile: .travis/Gemfile
+ env: conn=synchrony REDIS_BRANCH=2.8
+ - rvm: jruby-9.0.5.0
+ gemfile: .travis/Gemfile
+ env: conn=synchrony REDIS_BRANCH=2.8
+ allow_failures:
+ - rvm: rbx-2
+
+notifications:
+ irc:
+ - irc.freenode.net#redis-rb
+ email: false
diff --git a/lib/vendor/redis/.travis/Gemfile b/lib/vendor/redis/.travis/Gemfile
new file mode 100644
index 0000000..3fd1163
--- /dev/null
+++ b/lib/vendor/redis/.travis/Gemfile
@@ -0,0 +1,11 @@
+source "https://rubygems.org"
+
+gemspec :path => "../"
+
+case ENV["conn"]
+when "hiredis"
+ gem "hiredis"
+when "synchrony"
+ gem "hiredis"
+ gem "em-synchrony"
+end
diff --git a/lib/vendor/redis/.yardopts b/lib/vendor/redis/.yardopts
new file mode 100644
index 0000000..90e3cdc
--- /dev/null
+++ b/lib/vendor/redis/.yardopts
@@ -0,0 +1,3 @@
+--exclude redis/connection
+--exclude redis/compat
+--markup markdown
diff --git a/lib/vendor/redis/CHANGELOG.md b/lib/vendor/redis/CHANGELOG.md
new file mode 100644
index 0000000..ae5506d
--- /dev/null
+++ b/lib/vendor/redis/CHANGELOG.md
@@ -0,0 +1,363 @@
+# 4.x (unreleased)
+
+## Planned breaking changes:
+* `Redis#client` will no longer expose the underlying `Redis::Client`;
+ it has not yet been determined how 4.0 will expose the underlying
+ functionality, but we will make every attempt to provide a final minor
+ release of 3.x that provides the new interfaces in order to facilitate
+ a smooth transition.
+
+* Ruby 1.8.7 (and the 1.8 modes of JRuby and Rubinius) will no longer be
+ supported; 1.8.x entered end-of-life in June of 2012 and stopped receiving
+ security updates in June of 2013; continuing to support it would prevent
+ the use of newer features of Ruby.
+
+# 3.3.0
+
+* Added support for SSL/TLS. Redis doesn't support SSL natively, so you still
+ need to run a terminating proxy on Redis' side. See #496.
+
+* Added `read_timeout` and `write_timeout` options. See #437, #482.
+
+* Added support for pub/sub with timeouts. See #329.
+
+* Added `Redis#call`, `Redis#queue` and `Redis#commit` as a more minimal API to
+ the client.
+
+* Deprecated `Redis#disconnect!` in favor of `Redis#close`.
+
+# 3.2.2
+
+* Added support for `ZADD` options `NX`, `XX`, `CH`, `INCR`. See #547.
+
+* Added support for sentinel commands. See #556.
+
+* New `:id` option allows you to identify the client against Redis. See #510.
+
+* `Redis::Distributed` will raise when adding two nodes with the same ID.
+ See #354.
+
+# 3.2.1
+
+* Added support for `PUBSUB` command.
+
+* More low-level socket errors are now raised as `CannotConnectError`.
+
+* Added `:connect_timeout` option.
+
+* Added support for `:limit` option for `ZREVRANGEBYLEX`.
+
+* Fixed an issue where connections become inconsistent when using Ruby's
+ Timeout module outside of the client (see #501, #502).
+
+* Added `Redis#disconnect!` as a public-API way of disconnecting the client
+ (without needing to use `QUIT`). See #506.
+
+* Fixed Sentinel support with Hiredis.
+
+* Fixed Sentinel support when using authentication and databases.
+
+* Improved resilience when trying to contact sentinels.
+
+# 3.2.0
+
+* Redis Sentinel support.
+
+# 3.1.0
+
+* Added debug log sanitization (#428).
+
+* Added support for HyperLogLog commands (Redis 2.8.9, #432).
+
+* Added support for `BITPOS` command (Redis 2.9.11, #412).
+
+* The client will now automatically reconnect after a fork (#414).
+
+* If you want to disable the fork-safety check and prefer to share the
+ connection across child processes, you can now pass the `inherit_socket`
+ option (#409).
+
+* If you want the client to attempt to reconnect more than once, you can now
+ pass the `reconnect_attempts` option (#347)
+
+# 3.0.7
+
+* Added method `Redis#dup` to duplicate a Redis connection.
+
+* IPv6 support.
+
+# 3.0.6
+
+* Added support for `SCAN` and variants.
+
+# 3.0.5
+
+* Fix calling #select from a pipeline (#309).
+
+* Added method `Redis#connected?`.
+
+* Added support for `MIGRATE` (Redis 2.6).
+
+* Support extended SET command (#343, thanks to @benubois).
+
+# 3.0.4
+
+* Ensure #watch without a block returns "OK" (#332).
+
+* Make futures identifiable (#330).
+
+* Fix an issue preventing STORE in a SORT with multiple GETs (#328).
+
+# 3.0.3
+
+* Blocking list commands (`BLPOP`, `BRPOP`, `BRPOPLPUSH`) use a socket
+ timeout equal to the sum of the command's timeout and the Redis
+ client's timeout, instead of disabling socket timeout altogether.
+
+* Ruby 2.0 compatibility.
+
+* Added support for `DUMP` and `RESTORE` (Redis 2.6).
+
+* Added support for `BITCOUNT` and `BITOP` (Redis 2.6).
+
+* Call `#to_s` on value argument for `SET`, `SETEX`, `PSETEX`, `GETSET`,
+ `SETNX`, and `SETRANGE`.
+
+# 3.0.2
+
+* Unescape CGI escaped password in URL.
+
+* Fix test to check availability of `UNIXSocket`.
+
+* Fix handling of score = +/- infinity for sorted set commands.
+
+* Replace array splats with concatenation where possible.
+
+* Raise if `EXEC` returns an error.
+
+* Passing a nil value in options hash no longer overwrites the default.
+
+* Allow string keys in options hash passed to `Redis.new` or
+ `Redis.connect`.
+
+* Fix uncaught error triggering unrelated error (synchrony driver).
+
+ See f7ffd5f1a628029691084de69e5b46699bb8b96d and #248.
+
+# 3.0.1
+
+* Fix reconnect logic not kicking in on a write error.
+
+ See 427dbd52928af452f35aa0a57b621bee56cdcb18 and #238.
+
+# 3.0.0
+
+### Upgrading from 2.x to 3.0
+
+The following items are the most important changes to review when
+upgrading from redis-rb 2.x. A full list of changes can be found below.
+
+* The methods for the following commands have changed the arguments they
+ take, their return value, or both.
+
+ * `BLPOP`, `BRPOP`, `BRPOPLPUSH`
+ * `SORT`
+ * `MSETNX`
+ * `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE`, `ZREVRANGEBYSCORE`
+ * `ZINCRBY`, `ZSCORE`
+
+* The return value from `#pipelined` and `#multi` no longer contains
+ unprocessed replies, but the same replies that would be returned if
+ the command had not been executed in these blocks.
+
+* The client raises custom errors on connection errors, instead of
+ `RuntimeError` and errors in the `Errno` family.
+
+### Changes
+
+* Added support for scripting commands (Redis 2.6).
+
+ Scripts can be executed using `#eval` and `#evalsha`. Both can
+ commands can either take two arrays to specify `KEYS` and `ARGV`, or
+ take a hash containing `:keys` and `:argv` to specify `KEYS` and
+ `ARGV`.
+
+ ```ruby
+ redis.eval("return ARGV[1] * ARGV[2]", :argv => [2, 3])
+ # => 6
+ ```
+
+ Subcommands of the `SCRIPT` command can be executed via the
+ `#script` method.
+
+ For example:
+
+ ```ruby
+ redis.script(:load, "return ARGV[1] * ARGV[2]")
+ # => "58db5d365a1922f32e7aa717722141ea9c2b0cf3"
+ redis.script(:exists, "58db5d365a1922f32e7aa717722141ea9c2b0cf3")
+ # => true
+ redis.script(:flush)
+ # => "OK"
+ ```
+
+* The repository now lives at [https://github.com/redis/redis-rb](https://github.com/redis/redis-rb).
+ Thanks, Ezra!
+
+* Added support for `PEXPIRE`, `PEXPIREAT`, `PTTL`, `PSETEX`,
+ `INCRYBYFLOAT`, `HINCRYBYFLOAT` and `TIME` (Redis 2.6).
+
+* `Redis.current` is now thread unsafe, because the client itself is thread safe.
+
+ In the future you'll be able to do something like:
+
+ ```ruby
+ Redis.current = Redis::Pool.connect
+ ```
+
+ This makes `Redis.current` actually usable in multi-threaded environments,
+ while not affecting those running a single thread.
+
+* Change API for `BLPOP`, `BRPOP` and `BRPOPLPUSH`.
+
+ Both `BLPOP` and `BRPOP` now take a single argument equal to a
+ string key, or an array with string keys, followed by an optional
+ hash with a `:timeout` key. When not specified, the timeout defaults
+ to `0` to not time out.
+
+ ```ruby
+ redis.blpop(["list1", "list2"], :timeout => 1.0)
+ ```
+
+ `BRPOPLPUSH` also takes an optional hash with a `:timeout` key as
+ last argument for consistency. When not specified, the timeout
+ defaults to `0` to not time out.
+
+ ```ruby
+ redis.brpoplpush("some_list", "another_list", :timeout => 1.0)
+ ```
+
+* When `SORT` is passed multiple key patterns to get via the `:get`
+ option, it now returns an array per result element, holding all `GET`
+ substitutions.
+
+* The `MSETNX` command now returns a boolean.
+
+* The `ZRANGE`, `ZREVRANGE`, `ZRANGEBYSCORE` and `ZREVRANGEBYSCORE` commands
+ now return an array containing `[String, Float]` pairs when
+ `:with_scores => true` is passed.
+
+ For example:
+
+ ```ruby
+ redis.zrange("zset", 0, -1, :with_scores => true)
+ # => [["foo", 1.0], ["bar", 2.0]]
+ ```
+
+* The `ZINCRBY` and `ZSCORE` commands now return a `Float` score instead
+ of a string holding a representation of the score.
+
+* The client now raises custom exceptions where it makes sense.
+
+ If by any chance you were rescuing low-level exceptions (`Errno::*`),
+ you should now rescue as follows:
+
+ Errno::ECONNRESET -> Redis::ConnectionError
+ Errno::EPIPE -> Redis::ConnectionError
+ Errno::ECONNABORTED -> Redis::ConnectionError
+ Errno::EBADF -> Redis::ConnectionError
+ Errno::EINVAL -> Redis::ConnectionError
+ Errno::EAGAIN -> Redis::TimeoutError
+ Errno::ECONNREFUSED -> Redis::CannotConnectError
+
+* Always raise exceptions originating from erroneous command invocation
+ inside pipelines and MULTI/EXEC blocks.
+
+ The old behavior (swallowing exceptions) could cause application bugs
+ to go unnoticed.
+
+* Implement futures for assigning values inside pipelines and MULTI/EXEC
+ blocks. Futures are assigned their value after the pipeline or
+ MULTI/EXEC block has executed.
+
+ ```ruby
+ $redis.pipelined do
+ @future = $redis.get "key"
+ end
+
+ puts @future.value
+ ```
+
+* Ruby 1.8.6 is officially not supported.
+
+* Support `ZCOUNT` in `Redis::Distributed` (Michael Dungan).
+
+* Pipelined commands now return the same replies as when called outside
+ a pipeline.
+
+ In the past, pipelined replies were returned without post-processing.
+
+* Support `SLOWLOG` command (Michael Bernstein).
+
+* Calling `SHUTDOWN` effectively disconnects the client (Stefan Kaes).
+
+* Basic support for mapping commands so that they can be renamed on the
+ server.
+
+* Connecting using a URL now checks that a host is given.
+
+ It's just a small sanity check, cf. #126
+
+* Support variadic commands introduced in Redis 2.4.
+
+# 2.2.2
+
+* Added method `Redis::Distributed#hsetnx`.
+
+# 2.2.1
+
+* Internal API: Client#call and family are now called with a single array
+ argument, since splatting a large number of arguments (100K+) results in a
+ stack overflow on 1.9.2.
+
+* The `INFO` command can optionally take a subcommand. When the subcommand is
+ `COMMANDSTATS`, the client will properly format the returned statistics per
+ command. Subcommands for `INFO` are available since Redis v2.3.0 (unstable).
+
+* Change `IO#syswrite` back to the buffered `IO#write` since some Rubies do
+ short writes for large (1MB+) buffers and some don't (see issue #108).
+
+# 2.2.0
+
+* Added method `Redis#without_reconnect` that ensures the client will not try
+ to reconnect when running the code inside the specified block.
+
+* Thread-safe by default. Thread safety can be explicitly disabled by passing
+ `:thread_safe => false` as argument.
+
+* Commands called inside a MULTI/EXEC no longer raise error replies, since a
+ successful EXEC means the commands inside the block were executed.
+
+* MULTI/EXEC blocks are pipelined.
+
+* Don't disconnect on error replies.
+
+* Use `IO#syswrite` instead of `IO#write` because write buffering is not
+ necessary.
+
+* Connect to a unix socket by passing the `:path` option as argument.
+
+* The timeout value is coerced into a float, allowing sub-second timeouts.
+
+* Accept both `:with_scores` _and_ `:withscores` as argument to sorted set
+ commands.
+
+* Use [hiredis](https://github.com/pietern/hiredis-rb) (v0.3 or higher) by
+ requiring "redis/connection/hiredis".
+
+* Use [em-synchrony](https://github.com/igrigorik/em-synchrony) by requiring
+ "redis/connection/synchrony".
+
+# 2.1.1
+
+See commit log.
diff --git a/lib/vendor/redis/Gemfile b/lib/vendor/redis/Gemfile
new file mode 100644
index 0000000..73d38bc
--- /dev/null
+++ b/lib/vendor/redis/Gemfile
@@ -0,0 +1,4 @@
+# encoding: utf-8
+source 'https://rubygems.org'
+
+gemspec
diff --git a/lib/vendor/redis/LICENSE b/lib/vendor/redis/LICENSE
new file mode 100644
index 0000000..5e648fa
--- /dev/null
+++ b/lib/vendor/redis/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Ezra Zygmuntowicz
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/lib/vendor/redis/README.md b/lib/vendor/redis/README.md
new file mode 100644
index 0000000..138bf9c
--- /dev/null
+++ b/lib/vendor/redis/README.md
@@ -0,0 +1,410 @@
+# redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link]
+
+[travis-image]: https://secure.travis-ci.org/redis/redis-rb.png?branch=master
+[travis-link]: http://travis-ci.org/redis/redis-rb
+[travis-home]: http://travis-ci.org/
+[inchpages-image]: http://inch-ci.org/github/redis/redis-rb.png
+[inchpages-link]: http://inch-ci.org/github/redis/redis-rb
+
+A Ruby client library for [Redis][redis-home].
+
+[redis-home]: http://redis.io
+
+A Ruby client that tries to match Redis' API one-to-one, while still
+providing an idiomatic interface. It features thread-safety, client-side
+sharding, pipelining, and an obsession for performance.
+
+## Upgrading from 2.x to 3.0
+
+Please refer to the [CHANGELOG][changelog-3.0.0] for a summary of the
+most important changes, as well as a full list of changes.
+
+[changelog-3.0.0]: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#300
+
+## Getting started
+
+To install **redis-rb**, run the following command:
+
+```
+ gem install redis
+```
+
+Or if you are using **bundler**, add
+
+```
+ gem 'redis', '~>3.2'
+```
+
+to your `Gemfile`, and run `bundle install`
+
+As of version 2.0 this client only targets Redis version 2.0 and higher.
+You can use an older version of this client if you need to interface
+with a Redis instance older than 2.0, but this is no longer supported.
+
+You can connect to Redis by instantiating the `Redis` class:
+
+```ruby
+require "redis"
+
+redis = Redis.new
+```
+
+This assumes Redis was started with a default configuration, and is
+listening on `localhost`, port 6379. If you need to connect to a remote
+server or a different port, try:
+
+```ruby
+redis = Redis.new(:host => "10.0.1.1", :port => 6380, :db => 15)
+```
+
+You can also specify connection options as a [`redis://` URL][redis-url]:
+
+```ruby
+redis = Redis.new(:url => "redis://:p4ssw0rd@10.0.1.1:6380/15")
+```
+
+[redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
+
+By default, the client will try to read the `REDIS_URL` environment variable
+and use that as URL to connect to. The above statement is therefore equivalent
+to setting this environment variable and calling `Redis.new` without arguments.
+
+To connect to Redis listening on a Unix socket, try:
+
+```ruby
+redis = Redis.new(:path => "/tmp/redis.sock")
+```
+
+To connect to a password protected Redis instance, use:
+
+```ruby
+redis = Redis.new(:password => "mysecret")
+```
+
+The Redis class exports methods that are named identical to the commands
+they execute. The arguments these methods accept are often identical to
+the arguments specified on the [Redis website][redis-commands]. For
+instance, the `SET` and `GET` commands can be called like this:
+
+[redis-commands]: http://redis.io/commands
+
+```ruby
+redis.set("mykey", "hello world")
+# => "OK"
+
+redis.get("mykey")
+# => "hello world"
+```
+
+All commands, their arguments and return values are documented, and
+available on [rdoc.info][rdoc].
+
+[rdoc]: http://rdoc.info/github/redis/redis-rb/
+
+## Sentinel support
+
+The client is able to perform automatic failovers by using [Redis
+Sentinel](http://redis.io/topics/sentinel). Make sure to run Redis 2.8+
+if you want to use this feature.
+
+To connect using Sentinel, use:
+
+```ruby
+SENTINELS = [{:host => "127.0.0.1", :port => 26380},
+ {:host => "127.0.0.1", :port => 26381}]
+
+redis = Redis.new(:url => "redis://mymaster", :sentinels => SENTINELS, :role => :master)
+```
+
+* The master name identifies a group of Redis instances composed of a master
+and one or more slaves (`mymaster` in the example).
+
+* It is possible to optionally provide a role. The allowed roles are `master`
+and `slave`. When the role is `slave`, the client will try to connect to a
+random slave of the specified master. If a role is not specified, the client
+will connect to the master.
+
+* When using the Sentinel support you need to specify a list of sentinels to
+connect to. The list does not need to enumerate all your Sentinel instances,
+but a few so that if one is down the client will try the next one. The client
+is able to remember the last Sentinel that was able to reply correctly and will
+use it for the next requests.
+
+## Storing objects
+
+Redis only stores strings as values. If you want to store an object, you
+can use a serialization mechanism such as JSON:
+
+```ruby
+require "json"
+
+redis.set "foo", [1, 2, 3].to_json
+# => OK
+
+JSON.parse(redis.get("foo"))
+# => [1, 2, 3]
+```
+
+## Pipelining
+
+When multiple commands are executed sequentially, but are not dependent,
+the calls can be *pipelined*. This means that the client doesn't wait
+for reply of the first command before sending the next command. The
+advantage is that multiple commands are sent at once, resulting in
+faster overall execution.
+
+The client can be instructed to pipeline commands by using the
+`#pipelined` method. After the block is executed, the client sends all
+commands to Redis and gathers their replies. These replies are returned
+by the `#pipelined` method.
+
+```ruby
+redis.pipelined do
+ redis.set "foo", "bar"
+ redis.incr "baz"
+end
+# => ["OK", 1]
+```
+
+### Executing commands atomically
+
+You can use `MULTI/EXEC` to run a number of commands in an atomic
+fashion. This is similar to executing a pipeline, but the commands are
+preceded by a call to `MULTI`, and followed by a call to `EXEC`. Like
+the regular pipeline, the replies to the commands are returned by the
+`#multi` method.
+
+```ruby
+redis.multi do
+ redis.set "foo", "bar"
+ redis.incr "baz"
+end
+# => ["OK", 1]
+```
+
+### Futures
+
+Replies to commands in a pipeline can be accessed via the *futures* they
+emit (since redis-rb 3.0). All calls inside a pipeline block return a
+`Future` object, which responds to the `#value` method. When the
+pipeline has successfully executed, all futures are assigned their
+respective replies and can be used.
+
+```ruby
+redis.pipelined do
+ @set = redis.set "foo", "bar"
+ @incr = redis.incr "baz"
+end
+
+@set.value
+# => "OK"
+
+@incr.value
+# => 1
+```
+
+## Error Handling
+
+In general, if something goes wrong you'll get an exception. For example, if
+it can't connect to the server a `Redis::CannotConnectError` error will be raised.
+
+```ruby
+begin
+ redis.ping
+rescue Exception => e
+ e.inspect
+# => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>
+
+ e.message
+# => Timed out connecting to Redis on 10.0.1.1:6380
+end
+```
+
+See lib/redis/errors.rb for information about what exceptions are possible.
+
+## Timeouts
+
+The client allows you to configure connect, read, and write timeouts.
+Passing a single `timeout` option will set all three values:
+
+```ruby
+Redis.new(:timeout => 1)
+```
+
+But you can use specific values for each of them:
+
+```ruby
+Redis.new(
+ :connect_timeout => 0.2,
+ :read_timeout => 1.0,
+ :write_timeout => 0.5
+)
+```
+
+All timeout values are specified in seconds.
+
+When using pub/sub, you can subscribe to a channel using a timeout as well:
+
+```ruby
+redis.subscribe_with_timeout(5, "news") do |on|
+ on.message do |channel, message|
+ # ...
+ end
+end
+```
+
+If no message is received after 5 seconds, the client will unsubscribe.
+
+
+## SSL/TLS Support
+
+This library supports natively terminating client side SSL/TLS connections
+when talking to Redis via a server-side proxy such as [stunnel], [hitch],
+or [ghostunnel].
+
+To enable SSL support, pass the `:ssl => :true` option when configuring the
+Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
+You will also need to pass in an `:ssl_params => { ... }` hash used to
+configure the `OpenSSL::SSL::SSLContext` object used for the connection:
+
+```ruby
+redis = Redis.new(
+ :url => "rediss://:p4ssw0rd@10.0.1.1:6381/15",
+ :ssl_params => {
+ :ca_file => "/path/to/ca.crt"
+ }
+)
+```
+
+The options given to `:ssl_params` are passed directly to the
+`OpenSSL::SSL::SSLContext#set_params` method and can be any valid attribute
+of the SSL context. Please see the [OpenSSL::SSL::SSLContext documentation]
+for all of the available attributes.
+
+Here is an example of passing in params that can be used for SSL client
+certificate authentication (a.k.a. mutual TLS):
+
+```ruby
+redis = Redis.new(
+ :url => "rediss://:p4ssw0rd@10.0.1.1:6381/15",
+ :ssl_params => {
+ :ca_file => "/path/to/ca.crt",
+ :cert => OpenSSL::X509::Certificate.new(File.read("client.crt")),
+ :key => OpenSSL::PKey::RSA.new(File.read("client.key"))
+ }
+)
+```
+
+[stunnel]: https://www.stunnel.org/
+[hitch]: https://hitch-tls.org/
+[ghostunnel]: https://github.com/square/ghostunnel
+[OpenSSL::SSL::SSLContext documentation]: http://ruby-doc.org/stdlib-2.3.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
+
+*NOTE:* SSL is only supported by the default "Ruby" driver
+
+
+## Expert-Mode Options
+
+ - `inherit_socket: true`: disable safety check that prevents a forked child
+ from sharing a socket with its parent; this is potentially useful in order to mitigate connection churn when:
+ - many short-lived forked children of one process need to talk
+ to redis, AND
+ - your own code prevents the parent process from using the redis
+ connection while a child is alive
+
+ Improper use of `inherit_socket` will result in corrupted and/or incorrect
+ responses.
+
+## Alternate drivers
+
+By default, redis-rb uses Ruby's socket library to talk with Redis.
+To use an alternative connection driver it should be specified as option
+when instantiating the client object. These instructions are only valid
+for **redis-rb 3.0**. For instructions on how to use alternate drivers from
+**redis-rb 2.2**, please refer to an [older README][readme-2.2.2].
+
+[readme-2.2.2]: https://github.com/redis/redis-rb/blob/v2.2.2/README.md
+
+### hiredis
+
+The hiredis driver uses the connection facility of hiredis-rb. In turn,
+hiredis-rb is a binding to the official hiredis client library. It
+optimizes for speed, at the cost of portability. Because it is a C
+extension, JRuby is not supported (by default).
+
+It is best to use hiredis when you have large replies (for example:
+`LRANGE`, `SMEMBERS`, `ZRANGE`, etc.) and/or use big pipelines.
+
+In your Gemfile, include hiredis:
+
+```ruby
+gem "redis", "~> 3.0.1"
+gem "hiredis", "~> 0.4.5"
+```
+
+When instantiating the client object, specify hiredis:
+
+```ruby
+redis = Redis.new(:driver => :hiredis)
+```
+
+### synchrony
+
+The synchrony driver adds support for [em-synchrony][em-synchrony].
+This makes redis-rb work with EventMachine's asynchronous I/O, while not
+changing the exposed API. The hiredis gem needs to be available as
+well, because the synchrony driver uses hiredis for parsing the Redis
+protocol.
+
+[em-synchrony]: https://github.com/igrigorik/em-synchrony
+
+In your Gemfile, include em-synchrony and hiredis:
+
+```ruby
+gem "redis", "~> 3.0.1"
+gem "hiredis", "~> 0.4.5"
+gem "em-synchrony"
+```
+
+When instantiating the client object, specify synchrony:
+
+```ruby
+redis = Redis.new(:driver => :synchrony)
+```
+
+## Testing
+
+This library is tested using [Travis][travis-home], where it is tested
+against the following interpreters and drivers:
+
+* MRI 1.8.7 (drivers: ruby, hiredis)
+* MRI 1.9.3 (drivers: ruby, hiredis, synchrony)
+* MRI 2.0 (drivers: ruby, hiredis, synchrony)
+* MRI 2.1 (drivers: ruby, hiredis, synchrony)
+* MRI 2.2 (drivers: ruby, hiredis, synchrony)
+* MRI 2.3 (drivers: ruby, hiredis, synchrony)
+* JRuby 1.7 (1.8 mode) (drivers: ruby)
+* JRuby 1.7 (1.9 mode) (drivers: ruby)
+
+## Contributors
+
+(ordered chronologically with more than 5 commits, see `git shortlog -sn` for
+all contributors)
+
+* Ezra Zygmuntowicz
+* Taylor Weibley
+* Matthew Clark
+* Brian McKinney
+* Luca Guidi
+* Salvatore Sanfilippo
+* Chris Wanstrath
+* Damian Janowski
+* Michel Martens
+* Nick Quaranto
+* Pieter Noordhuis
+* Ilya Grigorik
+
+## Contributing
+
+[Fork the project](https://github.com/redis/redis-rb) and send pull
+requests. You can also ask for help at `#redis-rb` on Freenode.
diff --git a/lib/vendor/redis/Rakefile b/lib/vendor/redis/Rakefile
new file mode 100644
index 0000000..2d9e137
--- /dev/null
+++ b/lib/vendor/redis/Rakefile
@@ -0,0 +1,87 @@
+require "rake/testtask"
+
+ENV["REDIS_BRANCH"] ||= "unstable"
+
+REDIS_DIR = File.expand_path(File.join("..", "test"), __FILE__)
+REDIS_CNF = File.join(REDIS_DIR, "test.conf")
+REDIS_CNF_TEMPLATE = File.join(REDIS_DIR, "test.conf.erb")
+REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
+REDIS_LOG = File.join(REDIS_DIR, "db", "redis.log")
+REDIS_SOCKET = File.join(REDIS_DIR, "db", "redis.sock")
+BINARY = "tmp/redis-#{ENV["REDIS_BRANCH"]}/src/redis-server"
+
+task :default => :run
+
+desc "Run tests and manage server start/stop"
+task :run => [:start, :test, :stop]
+
+desc "Start the Redis server"
+task :start => [BINARY, REDIS_CNF] do
+ sh "#{BINARY} --version"
+
+ redis_running = \
+ begin
+ File.exists?(REDIS_PID) && Process.kill(0, File.read(REDIS_PID).to_i)
+ rescue Errno::ESRCH
+ FileUtils.rm REDIS_PID
+ false
+ end
+
+ unless redis_running
+ unless system("#{BINARY} #{REDIS_CNF}")
+ abort "could not start redis-server"
+ end
+ end
+
+ at_exit do
+ Rake::Task["stop"].invoke
+ end
+end
+
+desc "Stop the Redis server"
+task :stop do
+ if File.exists?(REDIS_PID)
+ Process.kill "INT", File.read(REDIS_PID).to_i
+ FileUtils.rm REDIS_PID
+ end
+end
+
+desc "Clean up testing artifacts"
+task :clean do
+ FileUtils.rm_f(BINARY)
+ FileUtils.rm_f(REDIS_CNF)
+end
+
+file BINARY do
+ branch = ENV.fetch("REDIS_BRANCH")
+
+ sh <<-SH
+ mkdir -p tmp;
+ cd tmp;
+ rm -rf redis-#{branch};
+ wget https://github.com/antirez/redis/archive/#{branch}.tar.gz -O #{branch}.tar.gz;
+ tar xf #{branch}.tar.gz;
+ cd redis-#{branch};
+ make
+ SH
+end
+
+file REDIS_CNF => [REDIS_CNF_TEMPLATE, __FILE__] do |t|
+ require 'erb'
+
+ erb = t.prerequisites[0]
+ template = File.read(erb)
+
+ File.open(REDIS_CNF, 'w') do |file|
+ file.puts "\# This file was auto-generated at #{Time.now}",
+ "\# from (#{erb})",
+ "\#"
+ conf = ERB.new(template).result
+ file << conf
+ end
+end
+
+Rake::TestTask.new do |t|
+ t.options = "-v" if $VERBOSE
+ t.test_files = FileList["test/*_test.rb"]
+end
diff --git a/lib/vendor/redis/benchmarking/logging.rb b/lib/vendor/redis/benchmarking/logging.rb
new file mode 100644
index 0000000..353e91b
--- /dev/null
+++ b/lib/vendor/redis/benchmarking/logging.rb
@@ -0,0 +1,71 @@
+# Run with
+#
+# $ ruby -Ilib benchmarking/logging.rb
+#
+
+begin
+ require "bench"
+rescue LoadError
+ $stderr.puts "`gem install bench` and try again."
+ exit 1
+end
+
+require "redis"
+require "logger"
+
+def log(level, namespace = nil)
+ logger = (namespace || Kernel).const_get(:Logger).new("/dev/null")
+ logger.level = (namespace || Logger).const_get(level)
+ logger
+end
+
+def stress(redis)
+ redis.flushdb
+
+ n = (ARGV.shift || 2000).to_i
+
+ n.times do |i|
+ key = "foo:#{i}"
+ redis.set key, i
+ redis.get key
+ end
+end
+
+default = Redis.new
+
+logging_redises = [
+ Redis.new(:logger => log(:DEBUG)),
+ Redis.new(:logger => log(:INFO)),
+]
+
+begin
+ require "log4r"
+
+ logging_redises += [
+ Redis.new(:logger => log(:DEBUG, Log4r)),
+ Redis.new(:logger => log(:INFO, Log4r)),
+ ]
+rescue LoadError
+ $stderr.puts "Log4r not installed. `gem install log4r` if you want to compare it against Ruby's Logger (spoiler: it's much faster)."
+end
+
+benchmark "Default options (no logger)" do
+ stress(default)
+end
+
+logging_redises.each do |redis|
+ logger = redis.client.logger
+
+ case logger
+ when Logger
+ level = Logger::SEV_LABEL[logger.level]
+ when Log4r::Logger
+ level = logger.levels[logger.level]
+ end
+
+ benchmark "#{logger.class} on #{level}" do
+ stress(redis)
+ end
+end
+
+run 10
diff --git a/lib/vendor/redis/benchmarking/pipeline.rb b/lib/vendor/redis/benchmarking/pipeline.rb
new file mode 100644
index 0000000..ecc98ba
--- /dev/null
+++ b/lib/vendor/redis/benchmarking/pipeline.rb
@@ -0,0 +1,51 @@
+require "benchmark"
+
+$:.push File.join(File.dirname(__FILE__), 'lib')
+
+require 'redis'
+
+ITERATIONS = 10000
+
+@r = Redis.new
+
+Benchmark.bmbm do |benchmark|
+ benchmark.report("set") do
+ @r.flushdb
+
+ ITERATIONS.times do |i|
+ @r.set("foo#{i}", "Hello world!")
+ @r.get("foo#{i}")
+ end
+ end
+
+ benchmark.report("set (pipelined)") do
+ @r.flushdb
+
+ @r.pipelined do
+ ITERATIONS.times do |i|
+ @r.set("foo#{i}", "Hello world!")
+ @r.get("foo#{i}")
+ end
+ end
+ end
+
+ benchmark.report("lpush+ltrim") do
+ @r.flushdb
+
+ ITERATIONS.times do |i|
+ @r.lpush "lpush#{i}", i
+ @r.ltrim "ltrim#{i}", 0, 30
+ end
+ end
+
+ benchmark.report("lpush+ltrim (pipelined)") do
+ @r.flushdb
+
+ @r.pipelined do
+ ITERATIONS.times do |i|
+ @r.lpush "lpush#{i}", i
+ @r.ltrim "ltrim#{i}", 0, 30
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/benchmarking/speed.rb b/lib/vendor/redis/benchmarking/speed.rb
new file mode 100644
index 0000000..3780bff
--- /dev/null
+++ b/lib/vendor/redis/benchmarking/speed.rb
@@ -0,0 +1,21 @@
+# Run with
+#
+# $ ruby -Ilib benchmarking/speed.rb
+#
+
+require "benchmark"
+require "redis"
+
+r = Redis.new
+n = (ARGV.shift || 20000).to_i
+
+elapsed = Benchmark.realtime do
+ # n sets, n gets
+ n.times do |i|
+ key = "foo#{i}"
+ r[key] = key * 10
+ r[key]
+ end
+end
+
+puts '%.2f Kops' % (2 * n / 1000 / elapsed)
diff --git a/lib/vendor/redis/benchmarking/suite.rb b/lib/vendor/redis/benchmarking/suite.rb
new file mode 100644
index 0000000..55fd1d6
--- /dev/null
+++ b/lib/vendor/redis/benchmarking/suite.rb
@@ -0,0 +1,24 @@
+require 'fileutils'
+
+def run_in_background(command)
+ fork { system command }
+end
+
+def with_all_segments(&block)
+ 0.upto(9) do |segment_number|
+ block_size = 100000
+ start_index = segment_number * block_size
+ end_index = start_index + block_size - 1
+ block.call(start_index, end_index)
+ end
+end
+
+#with_all_segments do |start_index, end_index|
+# puts "Initializing keys from #{start_index} to #{end_index}"
+# system "ruby worker.rb initialize #{start_index} #{end_index} 0"
+#end
+
+with_all_segments do |start_index, end_index|
+ run_in_background "ruby worker.rb write #{start_index} #{end_index} 10"
+ run_in_background "ruby worker.rb read #{start_index} #{end_index} 1"
+end
diff --git a/lib/vendor/redis/benchmarking/worker.rb b/lib/vendor/redis/benchmarking/worker.rb
new file mode 100644
index 0000000..836d03b
--- /dev/null
+++ b/lib/vendor/redis/benchmarking/worker.rb
@@ -0,0 +1,71 @@
+BENCHMARK_ROOT = File.dirname(__FILE__)
+REDIS_ROOT = File.join(BENCHMARK_ROOT, "..", "lib")
+
+$: << REDIS_ROOT
+require 'redis'
+require 'benchmark'
+
+def show_usage
+ puts <<-EOL
+ Usage: worker.rb [read:write] <start_index> <end_index> <sleep_msec>
+ EOL
+end
+
+def shift_from_argv
+ value = ARGV.shift
+ unless value
+ show_usage
+ exit -1
+ end
+ value
+end
+
+operation = shift_from_argv.to_sym
+start_index = shift_from_argv.to_i
+end_index = shift_from_argv.to_i
+sleep_msec = shift_from_argv.to_i
+sleep_duration = sleep_msec/1000.0
+
+redis = Redis.new
+
+case operation
+ when :initialize
+
+ start_index.upto(end_index) do |i|
+ redis[i] = 0
+ end
+
+ when :clear
+
+ start_index.upto(end_index) do |i|
+ redis.delete(i)
+ end
+
+ when :read, :write
+
+ puts "Starting to #{operation} at segment #{end_index + 1}"
+
+ loop do
+ t1 = Time.now
+ start_index.upto(end_index) do |i|
+ case operation
+ when :read
+ redis.get(i)
+ when :write
+ redis.incr(i)
+ else
+ raise "Unknown operation: #{operation}"
+ end
+ sleep sleep_duration
+ end
+ t2 = Time.now
+
+ requests_processed = end_index - start_index
+ time = t2 - t1
+ puts "#{t2.strftime("%H:%M")} [segment #{end_index + 1}] : Processed #{requests_processed} requests in #{time} seconds - #{(requests_processed/time).round} requests/sec"
+ end
+
+ else
+ raise "Unknown operation: #{operation}"
+end
+
diff --git a/lib/vendor/redis/examples/basic.rb b/lib/vendor/redis/examples/basic.rb
new file mode 100644
index 0000000..7800cf7
--- /dev/null
+++ b/lib/vendor/redis/examples/basic.rb
@@ -0,0 +1,15 @@
+require 'redis'
+
+r = Redis.new
+
+r.del('foo')
+
+puts
+
+p'set foo to "bar"'
+r['foo'] = 'bar'
+
+puts
+
+p 'value of foo'
+p r['foo']
diff --git a/lib/vendor/redis/examples/consistency.rb b/lib/vendor/redis/examples/consistency.rb
new file mode 100644
index 0000000..df34e1a
--- /dev/null
+++ b/lib/vendor/redis/examples/consistency.rb
@@ -0,0 +1,114 @@
+# This file implements a simple consistency test for Redis-rb (or any other
+# Redis environment if you pass a different client object) where a client
+# writes to the database using INCR in order to increment keys, but actively
+# remember the value the key should have. Before every write a read is performed
+# to check if the value in the database matches the value expected.
+#
+# In this way this program can check for lost writes, or acknowledged writes
+# that were executed.
+#
+# Copyright (C) 2013-2014 Salvatore Sanfilippo <antirez@gmail.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+require 'redis'
+
+class ConsistencyTester
+ def initialize(redis)
+ @r = redis
+ @working_set = 10000
+ @keyspace = 100000
+ @writes = 0
+ @reads = 0
+ @failed_writes = 0
+ @failed_reads = 0
+ @lost_writes = 0
+ @not_ack_writes = 0
+ @delay = 0
+ @cached = {} # We take our view of data stored in the DB.
+ @prefix = [Process.pid.to_s,Time.now.usec,@r.object_id,""].join("|")
+ @errtime = {}
+ end
+
+ def genkey
+ # Write more often to a small subset of keys
+ ks = rand() > 0.5 ? @keyspace : @working_set
+ @prefix+"key_"+rand(ks).to_s
+ end
+
+ def check_consistency(key,value)
+ expected = @cached[key]
+ return if !expected # We lack info about previous state.
+ if expected > value
+ @lost_writes += expected-value
+ elsif expected < value
+ @not_ack_writes += value-expected
+ end
+ end
+
+ def puterr(msg)
+ if !@errtime[msg] || Time.now.to_i != @errtime[msg]
+ puts msg
+ end
+ @errtime[msg] = Time.now.to_i
+ end
+
+ def test
+ last_report = Time.now.to_i
+ while true
+ # Read
+ key = genkey
+ begin
+ val = @r.get(key)
+ check_consistency(key,val.to_i)
+ @reads += 1
+ rescue => e
+ puterr "Reading: #{e.class}: #{e.message} (#{e.backtrace.first})"
+ @failed_reads += 1
+ end
+
+ # Write
+ begin
+ @cached[key] = @r.incr(key).to_i
+ @writes += 1
+ rescue => e
+ puterr "Writing: #{e.class}: #{e.message} (#{e.backtrace.first})"
+ @failed_writes += 1
+ end
+
+ # Report
+ sleep @delay
+ if Time.now.to_i != last_report
+ report = "#{@reads} R (#{@failed_reads} err) | " +
+ "#{@writes} W (#{@failed_writes} err) | "
+ report += "#{@lost_writes} lost | " if @lost_writes > 0
+ report += "#{@not_ack_writes} noack | " if @not_ack_writes > 0
+ last_report = Time.now.to_i
+ puts report
+ end
+ end
+ end
+end
+
+Sentinels = [{:host => "127.0.0.1", :port => 26379},
+ {:host => "127.0.0.1", :port => 26380}]
+r = Redis.new(:url => "redis://master1", :sentinels => Sentinels, :role => :master)
+tester = ConsistencyTester.new(r)
+tester.test
diff --git a/lib/vendor/redis/examples/dist_redis.rb b/lib/vendor/redis/examples/dist_redis.rb
new file mode 100644
index 0000000..fe1c5da
--- /dev/null
+++ b/lib/vendor/redis/examples/dist_redis.rb
@@ -0,0 +1,43 @@
+require "redis"
+require "redis/distributed"
+
+r = Redis::Distributed.new %w[redis://localhost:6379 redis://localhost:6380 redis://localhost:6381 redis://localhost:6382]
+
+r.flushdb
+
+r['urmom'] = 'urmom'
+r['urdad'] = 'urdad'
+r['urmom1'] = 'urmom1'
+r['urdad1'] = 'urdad1'
+r['urmom2'] = 'urmom2'
+r['urdad2'] = 'urdad2'
+r['urmom3'] = 'urmom3'
+r['urdad3'] = 'urdad3'
+p r['urmom']
+p r['urdad']
+p r['urmom1']
+p r['urdad1']
+p r['urmom2']
+p r['urdad2']
+p r['urmom3']
+p r['urdad3']
+
+r.rpush 'listor', 'foo1'
+r.rpush 'listor', 'foo2'
+r.rpush 'listor', 'foo3'
+r.rpush 'listor', 'foo4'
+r.rpush 'listor', 'foo5'
+
+p r.rpop('listor')
+p r.rpop('listor')
+p r.rpop('listor')
+p r.rpop('listor')
+p r.rpop('listor')
+
+puts "key distribution:"
+
+r.ring.nodes.each do |node|
+ p [node.client, node.keys("*")]
+end
+r.flushdb
+p r.keys('*')
diff --git a/lib/vendor/redis/examples/incr-decr.rb b/lib/vendor/redis/examples/incr-decr.rb
new file mode 100644
index 0000000..98ff9d1
--- /dev/null
+++ b/lib/vendor/redis/examples/incr-decr.rb
@@ -0,0 +1,17 @@
+require 'redis'
+
+r = Redis.new
+
+puts
+p 'incr'
+r.del 'counter'
+
+p r.incr('counter')
+p r.incr('counter')
+p r.incr('counter')
+
+puts
+p 'decr'
+p r.decr('counter')
+p r.decr('counter')
+p r.decr('counter')
diff --git a/lib/vendor/redis/examples/list.rb b/lib/vendor/redis/examples/list.rb
new file mode 100644
index 0000000..b2f25cb
--- /dev/null
+++ b/lib/vendor/redis/examples/list.rb
@@ -0,0 +1,26 @@
+require 'rubygems'
+require 'redis'
+
+r = Redis.new
+
+r.del 'logs'
+
+puts
+
+p "pushing log messages into a LIST"
+r.rpush 'logs', 'some log message'
+r.rpush 'logs', 'another log message'
+r.rpush 'logs', 'yet another log message'
+r.rpush 'logs', 'also another log message'
+
+puts
+p 'contents of logs LIST'
+
+p r.lrange('logs', 0, -1)
+
+puts
+p 'Trim logs LIST to last 2 elements(easy circular buffer)'
+
+r.ltrim('logs', -2, -1)
+
+p r.lrange('logs', 0, -1)
diff --git a/lib/vendor/redis/examples/pubsub.rb b/lib/vendor/redis/examples/pubsub.rb
new file mode 100644
index 0000000..9da1506
--- /dev/null
+++ b/lib/vendor/redis/examples/pubsub.rb
@@ -0,0 +1,37 @@
+require "redis"
+
+puts <<-EOS
+To play with this example use redis-cli from another terminal, like this:
+
+ $ redis-cli publish one hello
+
+Finally force the example to exit sending the 'exit' message with:
+
+ $ redis-cli publish two exit
+
+EOS
+
+redis = Redis.new
+
+trap(:INT) { puts; exit }
+
+begin
+ redis.subscribe(:one, :two) do |on|
+ on.subscribe do |channel, subscriptions|
+ puts "Subscribed to ##{channel} (#{subscriptions} subscriptions)"
+ end
+
+ on.message do |channel, message|
+ puts "##{channel}: #{message}"
+ redis.unsubscribe if message == "exit"
+ end
+
+ on.unsubscribe do |channel, subscriptions|
+ puts "Unsubscribed from ##{channel} (#{subscriptions} subscriptions)"
+ end
+ end
+rescue Redis::BaseConnectionError => error
+ puts "#{error}, retrying in 1s"
+ sleep 1
+ retry
+end
diff --git a/lib/vendor/redis/examples/sentinel.rb b/lib/vendor/redis/examples/sentinel.rb
new file mode 100644
index 0000000..62fc5a4
--- /dev/null
+++ b/lib/vendor/redis/examples/sentinel.rb
@@ -0,0 +1,41 @@
+require 'redis'
+
+# This example creates a master-slave setup with a sentinel, then connects to
+# it and sends write commands in a loop.
+#
+# After 30 seconds, the master dies. You will be able to see how a new master
+# is elected and things continue to work as if nothing happened.
+#
+# To run this example:
+#
+# $ ruby -I./lib examples/sentinel.rb
+#
+
+at_exit do
+ begin
+ Process.kill(:INT, $redises)
+ rescue Errno::ESRCH
+ end
+
+ Process.waitall
+end
+
+$redises = spawn("examples/sentinel/start")
+
+Sentinels = [{:host => "127.0.0.1", :port => 26379},
+ {:host => "127.0.0.1", :port => 26380}]
+r = Redis.new(:url => "redis://master1", :sentinels => Sentinels, :role => :master)
+
+# Set keys into a loop.
+#
+# The example traps errors so that you can actually try to failover while
+# running the script to see redis-rb reconfiguring.
+(0..1000000).each{|i|
+ begin
+ r.set(i,i)
+ $stdout.write("SET (#{i} times)\n") if i % 100 == 0
+ rescue => e
+ $stdout.write("E")
+ end
+ sleep(0.01)
+}
diff --git a/lib/vendor/redis/examples/sentinel/start b/lib/vendor/redis/examples/sentinel/start
new file mode 100755
index 0000000..46567e1
--- /dev/null
+++ b/lib/vendor/redis/examples/sentinel/start
@@ -0,0 +1,49 @@
+#! /usr/bin/env ruby
+
+# This is a helper script used together with examples/sentinel.rb
+# It runs two Redis masters, two slaves for each of them, and two sentinels.
+# After 30 seconds, the first master dies.
+#
+# You don't need to run this script yourself. Rather, use examples/sentinel.rb.
+
+require "fileutils"
+
+$pids = []
+
+at_exit do
+ $pids.each do |pid|
+ begin
+ Process.kill(:INT, pid)
+ rescue Errno::ESRCH
+ end
+ end
+
+ Process.waitall
+end
+
+base = File.expand_path(File.dirname(__FILE__))
+
+# Masters
+$pids << spawn("redis-server --port 6380 --loglevel warning")
+$pids << spawn("redis-server --port 6381 --loglevel warning")
+
+# Slaves of Master 1
+$pids << spawn("redis-server --port 63800 --slaveof 127.0.0.1 6380 --loglevel warning")
+$pids << spawn("redis-server --port 63801 --slaveof 127.0.0.1 6380 --loglevel warning")
+
+# Slaves of Master 2
+$pids << spawn("redis-server --port 63810 --slaveof 127.0.0.1 6381 --loglevel warning")
+$pids << spawn("redis-server --port 63811 --slaveof 127.0.0.1 6381 --loglevel warning")
+
+FileUtils.cp(File.join(base, "sentinel.conf"), "tmp/sentinel1.conf")
+FileUtils.cp(File.join(base, "sentinel.conf"), "tmp/sentinel2.conf")
+
+# Sentinels
+$pids << spawn("redis-server tmp/sentinel1.conf --sentinel --port 26379")
+$pids << spawn("redis-server tmp/sentinel2.conf --sentinel --port 26380")
+
+sleep 30
+
+Process.kill(:KILL, $pids[0])
+
+Process.waitall
diff --git a/lib/vendor/redis/examples/sets.rb b/lib/vendor/redis/examples/sets.rb
new file mode 100644
index 0000000..31c49c3
--- /dev/null
+++ b/lib/vendor/redis/examples/sets.rb
@@ -0,0 +1,36 @@
+require 'rubygems'
+require 'redis'
+
+r = Redis.new
+
+r.del 'foo-tags'
+r.del 'bar-tags'
+
+puts
+p "create a set of tags on foo-tags"
+
+r.sadd 'foo-tags', 'one'
+r.sadd 'foo-tags', 'two'
+r.sadd 'foo-tags', 'three'
+
+puts
+p "create a set of tags on bar-tags"
+
+r.sadd 'bar-tags', 'three'
+r.sadd 'bar-tags', 'four'
+r.sadd 'bar-tags', 'five'
+
+puts
+p 'foo-tags'
+
+p r.smembers('foo-tags')
+
+puts
+p 'bar-tags'
+
+p r.smembers('bar-tags')
+
+puts
+p 'intersection of foo-tags and bar-tags'
+
+p r.sinter('foo-tags', 'bar-tags')
diff --git a/lib/vendor/redis/examples/unicorn/config.ru b/lib/vendor/redis/examples/unicorn/config.ru
new file mode 100644
index 0000000..ede5573
--- /dev/null
+++ b/lib/vendor/redis/examples/unicorn/config.ru
@@ -0,0 +1,3 @@
+run lambda { |env|
+ [200, {"Content-Type" => "text/plain"}, [Redis.current.randomkey]]
+}
diff --git a/lib/vendor/redis/examples/unicorn/unicorn.rb b/lib/vendor/redis/examples/unicorn/unicorn.rb
new file mode 100644
index 0000000..957d384
--- /dev/null
+++ b/lib/vendor/redis/examples/unicorn/unicorn.rb
@@ -0,0 +1,20 @@
+require "redis"
+
+worker_processes 3
+
+# If you set the connection to Redis *before* forking,
+# you will cause forks to share a file descriptor.
+#
+# This causes a concurrency problem by which one fork
+# can read or write to the socket while others are
+# performing other operations.
+#
+# Most likely you'll be getting ProtocolError exceptions
+# mentioning a wrong initial byte in the reply.
+#
+# Thus we need to connect to Redis after forking the
+# worker processes.
+
+after_fork do |server, worker|
+ Redis.current.disconnect!
+end
diff --git a/lib/vendor/redis/redis.gemspec b/lib/vendor/redis/redis.gemspec
new file mode 100644
index 0000000..3099b6d
--- /dev/null
+++ b/lib/vendor/redis/redis.gemspec
@@ -0,0 +1,44 @@
+# -*- encoding: utf-8 -*-
+
+$:.unshift File.expand_path("../lib", __FILE__)
+
+require "redis/version"
+
+Gem::Specification.new do |s|
+ s.name = "redis"
+
+ s.version = Redis::VERSION
+
+ s.homepage = "https://github.com/redis/redis-rb"
+
+ s.summary = "A Ruby client library for Redis"
+
+ s.description = <<-EOS
+ A Ruby client that tries to match Redis' API one-to-one, while still
+ providing an idiomatic interface. It features thread-safety,
+ client-side sharding, pipelining, and an obsession for performance.
+ EOS
+
+ s.license = "MIT"
+
+ s.authors = [
+ "Ezra Zygmuntowicz",
+ "Taylor Weibley",
+ "Matthew Clark",
+ "Brian McKinney",
+ "Salvatore Sanfilippo",
+ "Luca Guidi",
+ "Michel Martens",
+ "Damian Janowski",
+ "Pieter Noordhuis"
+ ]
+
+ s.email = ["redis-db@googlegroups.com"]
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+
+ s.add_development_dependency("rake", "<11.0.0")
+ s.add_development_dependency("test-unit", "3.1.5")
+end
diff --git a/lib/vendor/redis/test/bitpos_test.rb b/lib/vendor/redis/test/bitpos_test.rb
new file mode 100644
index 0000000..118294d
--- /dev/null
+++ b/lib/vendor/redis/test/bitpos_test.rb
@@ -0,0 +1,69 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+unless defined?(Enumerator)
+ Enumerator = Enumerable::Enumerator
+end
+
+class TestBitpos < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_bitpos_empty_zero
+ target_version "2.9.11" do
+ r.del "foo"
+ assert_equal 0, r.bitpos("foo", 0)
+ end
+ end
+
+ def test_bitpos_empty_one
+ target_version "2.9.11" do
+ r.del "foo"
+ assert_equal -1, r.bitpos("foo", 1)
+ end
+ end
+
+ def test_bitpos_zero
+ target_version "2.9.11" do
+ r.set "foo", "\xff\xf0\x00"
+ assert_equal 12, r.bitpos("foo", 0)
+ end
+ end
+
+ def test_bitpos_one
+ target_version "2.9.11" do
+ r.set "foo", "\x00\x0f\x00"
+ assert_equal 12, r.bitpos("foo", 1)
+ end
+ end
+
+ def test_bitpos_zero_end_is_given
+ target_version "2.9.11" do
+ r.set "foo", "\xff\xff\xff"
+ assert_equal 24, r.bitpos("foo", 0)
+ assert_equal 24, r.bitpos("foo", 0, 0)
+ assert_equal -1, r.bitpos("foo", 0, 0, -1)
+ end
+ end
+
+ def test_bitpos_one_intervals
+ target_version "2.9.11" do
+ r.set "foo", "\x00\xff\x00"
+ assert_equal 8, r.bitpos("foo", 1, 0, -1)
+ assert_equal 8, r.bitpos("foo", 1, 1, -1)
+ assert_equal -1, r.bitpos("foo", 1, 2, -1)
+ assert_equal -1, r.bitpos("foo", 1, 2, 200)
+ assert_equal 8, r.bitpos("foo", 1, 1, 1)
+ end
+ end
+
+ def test_bitpos_raise_exception_if_stop_not_start
+ target_version "2.9.11" do
+ assert_raises(ArgumentError) do
+ r.bitpos("foo", 0, nil, 2)
+ end
+ end
+ end
+
+end
diff --git a/lib/vendor/redis/test/blocking_commands_test.rb b/lib/vendor/redis/test/blocking_commands_test.rb
new file mode 100644
index 0000000..4a2a965
--- /dev/null
+++ b/lib/vendor/redis/test/blocking_commands_test.rb
@@ -0,0 +1,42 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/blocking_commands"
+
+class TestBlockingCommands < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::BlockingCommands
+
+ def assert_takes_longer_than_client_timeout
+ timeout = OPTIONS[:timeout]
+ delay = timeout * 2
+
+ mock(:delay => delay) do |r|
+ t1 = Time.now
+ yield(r)
+ t2 = Time.now
+
+ assert timeout == r.client.timeout
+ assert delay <= (t2 - t1)
+ end
+ end
+
+ def test_blpop_disable_client_timeout
+ assert_takes_longer_than_client_timeout do |r|
+ assert_equal ["foo", "0"], r.blpop("foo")
+ end
+ end
+
+ def test_brpop_disable_client_timeout
+ assert_takes_longer_than_client_timeout do |r|
+ assert_equal ["foo", "0"], r.brpop("foo")
+ end
+ end
+
+ def test_brpoplpush_disable_client_timeout
+ assert_takes_longer_than_client_timeout do |r|
+ assert_equal "0", r.brpoplpush("foo", "bar")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/client_test.rb b/lib/vendor/redis/test/client_test.rb
new file mode 100644
index 0000000..1d0b8d3
--- /dev/null
+++ b/lib/vendor/redis/test/client_test.rb
@@ -0,0 +1,59 @@
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestClient < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_call
+ result = r.call("PING")
+ assert_equal result, "PONG"
+ end
+
+ def test_call_with_arguments
+ result = r.call("SET", "foo", "bar")
+ assert_equal result, "OK"
+ end
+
+ def test_call_integers
+ result = r.call("INCR", "foo")
+ assert_equal result, 1
+ end
+
+ def test_call_raise
+ assert_raises(Redis::CommandError) do
+ r.call("INCR")
+ end
+ end
+
+ def test_queue_commit
+ r.queue("SET", "foo", "bar")
+ r.queue("GET", "foo")
+ result = r.commit
+
+ assert_equal result, ["OK", "bar"]
+ end
+
+ def test_commit_raise
+ r.queue("SET", "foo", "bar")
+ r.queue("INCR")
+
+ assert_raise(Redis::CommandError) do
+ r.commit
+ end
+ end
+
+ def test_queue_after_error
+ r.queue("SET", "foo", "bar")
+ r.queue("INCR")
+
+ assert_raise(Redis::CommandError) do
+ r.commit
+ end
+
+ r.queue("SET", "foo", "bar")
+ r.queue("INCR", "baz")
+ result = r.commit
+
+ assert_equal result, ["OK", 1]
+ end
+end
diff --git a/lib/vendor/redis/test/command_map_test.rb b/lib/vendor/redis/test/command_map_test.rb
new file mode 100644
index 0000000..cb401db
--- /dev/null
+++ b/lib/vendor/redis/test/command_map_test.rb
@@ -0,0 +1,30 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestCommandMap < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_override_existing_commands
+ r.set("counter", 1)
+
+ assert_equal 2, r.incr("counter")
+
+ r.client.command_map[:incr] = :decr
+
+ assert_equal 1, r.incr("counter")
+ end
+
+ def test_override_non_existing_commands
+ r.set("key", "value")
+
+ assert_raise Redis::CommandError do
+ r.idontexist("key")
+ end
+
+ r.client.command_map[:idontexist] = :get
+
+ assert_equal "value", r.idontexist("key")
+ end
+end
diff --git a/lib/vendor/redis/test/commands_on_hashes_test.rb b/lib/vendor/redis/test/commands_on_hashes_test.rb
new file mode 100644
index 0000000..f3bbfa5
--- /dev/null
+++ b/lib/vendor/redis/test/commands_on_hashes_test.rb
@@ -0,0 +1,21 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/hashes"
+
+class TestCommandsOnHashes < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::Hashes
+
+ def test_mapped_hmget_in_a_pipeline_returns_hash
+ r.hset("foo", "f1", "s1")
+ r.hset("foo", "f2", "s2")
+
+ result = r.pipelined do
+ r.mapped_hmget("foo", "f1", "f2")
+ end
+
+ assert_equal result[0], { "f1" => "s1", "f2" => "s2" }
+ end
+end
diff --git a/lib/vendor/redis/test/commands_on_hyper_log_log_test.rb b/lib/vendor/redis/test/commands_on_hyper_log_log_test.rb
new file mode 100644
index 0000000..a2fc95b
--- /dev/null
+++ b/lib/vendor/redis/test/commands_on_hyper_log_log_test.rb
@@ -0,0 +1,21 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/hyper_log_log"
+
+class TestCommandsOnHyperLogLog < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::HyperLogLog
+
+ def test_pfmerge
+ target_version "2.8.9" do
+ r.pfadd "foo", "s1"
+ r.pfadd "bar", "s2"
+
+ assert_equal true, r.pfmerge("res", "foo", "bar")
+ assert_equal 2, r.pfcount("res")
+ end
+ end
+
+end \ No newline at end of file
diff --git a/lib/vendor/redis/test/commands_on_lists_test.rb b/lib/vendor/redis/test/commands_on_lists_test.rb
new file mode 100644
index 0000000..2916c28
--- /dev/null
+++ b/lib/vendor/redis/test/commands_on_lists_test.rb
@@ -0,0 +1,20 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/lists"
+
+class TestCommandsOnLists < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::Lists
+
+ def test_rpoplpush
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal "s2", r.rpoplpush("foo", "bar")
+ assert_equal ["s2"], r.lrange("bar", 0, -1)
+ assert_equal "s1", r.rpoplpush("foo", "bar")
+ assert_equal ["s1", "s2"], r.lrange("bar", 0, -1)
+ end
+end
diff --git a/lib/vendor/redis/test/commands_on_sets_test.rb b/lib/vendor/redis/test/commands_on_sets_test.rb
new file mode 100644
index 0000000..7ac2f4e
--- /dev/null
+++ b/lib/vendor/redis/test/commands_on_sets_test.rb
@@ -0,0 +1,77 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/sets"
+
+class TestCommandsOnSets < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::Sets
+
+ def test_smove
+ r.sadd "foo", "s1"
+ r.sadd "bar", "s2"
+
+ assert r.smove("foo", "bar", "s1")
+ assert r.sismember("bar", "s1")
+ end
+
+ def test_sinter
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+
+ assert_equal ["s2"], r.sinter("foo", "bar")
+ end
+
+ def test_sinterstore
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+
+ r.sinterstore("baz", "foo", "bar")
+
+ assert_equal ["s2"], r.smembers("baz")
+ end
+
+ def test_sunion
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ assert_equal ["s1", "s2", "s3"], r.sunion("foo", "bar").sort
+ end
+
+ def test_sunionstore
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ r.sunionstore("baz", "foo", "bar")
+
+ assert_equal ["s1", "s2", "s3"], r.smembers("baz").sort
+ end
+
+ def test_sdiff
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ assert_equal ["s1"], r.sdiff("foo", "bar")
+ assert_equal ["s3"], r.sdiff("bar", "foo")
+ end
+
+ def test_sdiffstore
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ r.sdiffstore("baz", "foo", "bar")
+
+ assert_equal ["s1"], r.smembers("baz")
+ end
+end
diff --git a/lib/vendor/redis/test/commands_on_sorted_sets_test.rb b/lib/vendor/redis/test/commands_on_sorted_sets_test.rb
new file mode 100644
index 0000000..0a424be
--- /dev/null
+++ b/lib/vendor/redis/test/commands_on_sorted_sets_test.rb
@@ -0,0 +1,137 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/sorted_sets"
+
+class TestCommandsOnSortedSets < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::SortedSets
+
+ def test_zrangebylex
+ target_version "2.8.9" do
+ r.zadd "foo", 0, "aaren"
+ r.zadd "foo", 0, "abagael"
+ r.zadd "foo", 0, "abby"
+ r.zadd "foo", 0, "abbygail"
+
+ assert_equal ["aaren", "abagael", "abby", "abbygail"], r.zrangebylex("foo", "[a", "[a\xff")
+ assert_equal ["aaren", "abagael"], r.zrangebylex("foo", "[a", "[a\xff", :limit => [0, 2])
+ assert_equal ["abby", "abbygail"], r.zrangebylex("foo", "(abb", "(abb\xff")
+ assert_equal ["abbygail"], r.zrangebylex("foo", "(abby", "(abby\xff")
+ end
+ end
+
+ def test_zrevrangebylex
+ target_version "2.9.9" do
+ r.zadd "foo", 0, "aaren"
+ r.zadd "foo", 0, "abagael"
+ r.zadd "foo", 0, "abby"
+ r.zadd "foo", 0, "abbygail"
+
+ assert_equal ["abbygail", "abby", "abagael", "aaren"], r.zrevrangebylex("foo", "[a\xff", "[a")
+ assert_equal ["abbygail", "abby"], r.zrevrangebylex("foo", "[a\xff", "[a", :limit => [0, 2])
+ assert_equal ["abbygail", "abby"], r.zrevrangebylex("foo", "(abb\xff", "(abb")
+ assert_equal ["abbygail"], r.zrevrangebylex("foo", "(abby\xff", "(abby")
+ end
+ end
+
+ def test_zcount
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal 2, r.zcount("foo", 2, 3)
+ end
+
+ def test_zunionstore
+ r.zadd "foo", 1, "s1"
+ r.zadd "bar", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "bar", 4, "s4"
+
+ assert_equal 4, r.zunionstore("foobar", ["foo", "bar"])
+ assert_equal ["s1", "s2", "s3", "s4"], r.zrange("foobar", 0, -1)
+ end
+
+ def test_zunionstore_with_weights
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 3, "s3"
+ r.zadd "bar", 20, "s2"
+ r.zadd "bar", 40, "s4"
+
+ assert_equal 4, r.zunionstore("foobar", ["foo", "bar"])
+ assert_equal ["s1", "s3", "s2", "s4"], r.zrange("foobar", 0, -1)
+
+ assert_equal 4, r.zunionstore("foobar", ["foo", "bar"], :weights => [10, 1])
+ assert_equal ["s1", "s2", "s3", "s4"], r.zrange("foobar", 0, -1)
+ end
+
+ def test_zunionstore_with_aggregate
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "bar", 4, "s2"
+ r.zadd "bar", 3, "s3"
+
+ assert_equal 3, r.zunionstore("foobar", ["foo", "bar"])
+ assert_equal ["s1", "s3", "s2"], r.zrange("foobar", 0, -1)
+
+ assert_equal 3, r.zunionstore("foobar", ["foo", "bar"], :aggregate => :min)
+ assert_equal ["s1", "s2", "s3"], r.zrange("foobar", 0, -1)
+
+ assert_equal 3, r.zunionstore("foobar", ["foo", "bar"], :aggregate => :max)
+ assert_equal ["s1", "s3", "s2"], r.zrange("foobar", 0, -1)
+ end
+
+ def test_zinterstore
+ r.zadd "foo", 1, "s1"
+ r.zadd "bar", 2, "s1"
+ r.zadd "foo", 3, "s3"
+ r.zadd "bar", 4, "s4"
+
+ assert_equal 1, r.zinterstore("foobar", ["foo", "bar"])
+ assert_equal ["s1"], r.zrange("foobar", 0, -1)
+ end
+
+ def test_zinterstore_with_weights
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "bar", 20, "s2"
+ r.zadd "bar", 30, "s3"
+ r.zadd "bar", 40, "s4"
+
+ assert_equal 2, r.zinterstore("foobar", ["foo", "bar"])
+ assert_equal ["s2", "s3"], r.zrange("foobar", 0, -1)
+
+ assert_equal 2, r.zinterstore("foobar", ["foo", "bar"], :weights => [10, 1])
+ assert_equal ["s2", "s3"], r.zrange("foobar", 0, -1)
+
+ assert_equal 40.0, r.zscore("foobar", "s2")
+ assert_equal 60.0, r.zscore("foobar", "s3")
+ end
+
+ def test_zinterstore_with_aggregate
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "bar", 20, "s2"
+ r.zadd "bar", 30, "s3"
+ r.zadd "bar", 40, "s4"
+
+ assert_equal 2, r.zinterstore("foobar", ["foo", "bar"])
+ assert_equal ["s2", "s3"], r.zrange("foobar", 0, -1)
+ assert_equal 22.0, r.zscore("foobar", "s2")
+ assert_equal 33.0, r.zscore("foobar", "s3")
+
+ assert_equal 2, r.zinterstore("foobar", ["foo", "bar"], :aggregate => :min)
+ assert_equal ["s2", "s3"], r.zrange("foobar", 0, -1)
+ assert_equal 2.0, r.zscore("foobar", "s2")
+ assert_equal 3.0, r.zscore("foobar", "s3")
+
+ assert_equal 2, r.zinterstore("foobar", ["foo", "bar"], :aggregate => :max)
+ assert_equal ["s2", "s3"], r.zrange("foobar", 0, -1)
+ assert_equal 20.0, r.zscore("foobar", "s2")
+ assert_equal 30.0, r.zscore("foobar", "s3")
+ end
+end
diff --git a/lib/vendor/redis/test/commands_on_strings_test.rb b/lib/vendor/redis/test/commands_on_strings_test.rb
new file mode 100644
index 0000000..9172aac
--- /dev/null
+++ b/lib/vendor/redis/test/commands_on_strings_test.rb
@@ -0,0 +1,101 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/strings"
+
+class TestCommandsOnStrings < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::Strings
+
+ def test_mget
+ r.set("foo", "s1")
+ r.set("bar", "s2")
+
+ assert_equal ["s1", "s2"] , r.mget("foo", "bar")
+ assert_equal ["s1", "s2", nil], r.mget("foo", "bar", "baz")
+ end
+
+ def test_mget_mapped
+ r.set("foo", "s1")
+ r.set("bar", "s2")
+
+ response = r.mapped_mget("foo", "bar")
+
+ assert_equal "s1", response["foo"]
+ assert_equal "s2", response["bar"]
+
+ response = r.mapped_mget("foo", "bar", "baz")
+
+ assert_equal "s1", response["foo"]
+ assert_equal "s2", response["bar"]
+ assert_equal nil , response["baz"]
+ end
+
+ def test_mapped_mget_in_a_pipeline_returns_hash
+ r.set("foo", "s1")
+ r.set("bar", "s2")
+
+ result = r.pipelined do
+ r.mapped_mget("foo", "bar")
+ end
+
+ assert_equal result[0], { "foo" => "s1", "bar" => "s2" }
+ end
+
+ def test_mset
+ r.mset(:foo, "s1", :bar, "s2")
+
+ assert_equal "s1", r.get("foo")
+ assert_equal "s2", r.get("bar")
+ end
+
+ def test_mset_mapped
+ r.mapped_mset(:foo => "s1", :bar => "s2")
+
+ assert_equal "s1", r.get("foo")
+ assert_equal "s2", r.get("bar")
+ end
+
+ def test_msetnx
+ r.set("foo", "s1")
+ assert_equal false, r.msetnx(:foo, "s2", :bar, "s3")
+ assert_equal "s1", r.get("foo")
+ assert_equal nil, r.get("bar")
+
+ r.del("foo")
+ assert_equal true, r.msetnx(:foo, "s2", :bar, "s3")
+ assert_equal "s2", r.get("foo")
+ assert_equal "s3", r.get("bar")
+ end
+
+ def test_msetnx_mapped
+ r.set("foo", "s1")
+ assert_equal false, r.mapped_msetnx(:foo => "s2", :bar => "s3")
+ assert_equal "s1", r.get("foo")
+ assert_equal nil, r.get("bar")
+
+ r.del("foo")
+ assert_equal true, r.mapped_msetnx(:foo => "s2", :bar => "s3")
+ assert_equal "s2", r.get("foo")
+ assert_equal "s3", r.get("bar")
+ end
+
+ def test_bitop
+ try_encoding("UTF-8") do
+ target_version "2.5.10" do
+ r.set("foo", "a")
+ r.set("bar", "b")
+
+ r.bitop(:and, "foo&bar", "foo", "bar")
+ assert_equal "\x60", r.get("foo&bar")
+ r.bitop(:or, "foo|bar", "foo", "bar")
+ assert_equal "\x63", r.get("foo|bar")
+ r.bitop(:xor, "foo^bar", "foo", "bar")
+ assert_equal "\x03", r.get("foo^bar")
+ r.bitop(:not, "~foo", "foo")
+ assert_equal "\x9E", r.get("~foo")
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/commands_on_value_types_test.rb b/lib/vendor/redis/test/commands_on_value_types_test.rb
new file mode 100644
index 0000000..6b2f211
--- /dev/null
+++ b/lib/vendor/redis/test/commands_on_value_types_test.rb
@@ -0,0 +1,133 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/value_types"
+
+class TestCommandsOnValueTypes < Test::Unit::TestCase
+
+ include Helper::Client
+ include Lint::ValueTypes
+
+ def test_del
+ r.set "foo", "s1"
+ r.set "bar", "s2"
+ r.set "baz", "s3"
+
+ assert_equal ["bar", "baz", "foo"], r.keys("*").sort
+
+ assert_equal 1, r.del("foo")
+
+ assert_equal ["bar", "baz"], r.keys("*").sort
+
+ assert_equal 2, r.del("bar", "baz")
+
+ assert_equal [], r.keys("*").sort
+ end
+
+ def test_del_with_array_argument
+ r.set "foo", "s1"
+ r.set "bar", "s2"
+ r.set "baz", "s3"
+
+ assert_equal ["bar", "baz", "foo"], r.keys("*").sort
+
+ assert_equal 1, r.del(["foo"])
+
+ assert_equal ["bar", "baz"], r.keys("*").sort
+
+ assert_equal 2, r.del(["bar", "baz"])
+
+ assert_equal [], r.keys("*").sort
+ end
+
+ def test_randomkey
+ assert r.randomkey.to_s.empty?
+
+ r.set("foo", "s1")
+
+ assert_equal "foo", r.randomkey
+
+ r.set("bar", "s2")
+
+ 4.times do
+ assert ["foo", "bar"].include?(r.randomkey)
+ end
+ end
+
+ def test_rename
+ r.set("foo", "s1")
+ r.rename "foo", "bar"
+
+ assert_equal "s1", r.get("bar")
+ assert_equal nil, r.get("foo")
+ end
+
+ def test_renamenx
+ r.set("foo", "s1")
+ r.set("bar", "s2")
+
+ assert_equal false, r.renamenx("foo", "bar")
+
+ assert_equal "s1", r.get("foo")
+ assert_equal "s2", r.get("bar")
+ end
+
+ def test_dbsize
+ assert_equal 0, r.dbsize
+
+ r.set("foo", "s1")
+
+ assert_equal 1, r.dbsize
+ end
+
+ def test_flushdb
+ r.set("foo", "s1")
+ r.set("bar", "s2")
+
+ assert_equal 2, r.dbsize
+
+ r.flushdb
+
+ assert_equal 0, r.dbsize
+ end
+
+ def test_flushall
+ redis_mock(:flushall => lambda { "+FLUSHALL" }) do |redis|
+ assert_equal "FLUSHALL", redis.flushall
+ end
+ end
+
+ def test_migrate
+ redis_mock(:migrate => lambda { |*args| args }) do |redis|
+ options = { :host => "127.0.0.1", :port => 1234 }
+
+ ex = assert_raise(RuntimeError) do
+ redis.migrate("foo", options.reject { |key, _| key == :host })
+ end
+ assert ex.message =~ /host not specified/
+
+ ex = assert_raise(RuntimeError) do
+ redis.migrate("foo", options.reject { |key, _| key == :port })
+ end
+ assert ex.message =~ /port not specified/
+
+ default_db = redis.client.db.to_i
+ default_timeout = redis.client.timeout.to_i
+
+ # Test defaults
+ actual = redis.migrate("foo", options)
+ expected = ["127.0.0.1", "1234", "foo", default_db.to_s, default_timeout.to_s]
+ assert_equal expected, actual
+
+ # Test db override
+ actual = redis.migrate("foo", options.merge(:db => default_db + 1))
+ expected = ["127.0.0.1", "1234", "foo", (default_db + 1).to_s, default_timeout.to_s]
+ assert_equal expected, actual
+
+ # Test timeout override
+ actual = redis.migrate("foo", options.merge(:timeout => default_timeout + 1))
+ expected = ["127.0.0.1", "1234", "foo", default_db.to_s, (default_timeout + 1).to_s]
+ assert_equal expected, actual
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/connection_handling_test.rb b/lib/vendor/redis/test/connection_handling_test.rb
new file mode 100644
index 0000000..f11553b
--- /dev/null
+++ b/lib/vendor/redis/test/connection_handling_test.rb
@@ -0,0 +1,277 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestConnectionHandling < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_auth
+ commands = {
+ :auth => lambda { |password| $auth = password; "+OK" },
+ :get => lambda { |key| $auth == "secret" ? "$3\r\nbar" : "$-1" },
+ }
+
+ redis_mock(commands, :password => "secret") do |redis|
+ assert_equal "bar", redis.get("foo")
+ end
+ end
+
+ def test_id
+ commands = {
+ :client => lambda { |cmd, name| $name = [cmd, name]; "+OK" },
+ :ping => lambda { "+PONG" },
+ }
+
+ redis_mock(commands, :id => "client-name") do |redis|
+ assert_equal "PONG", redis.ping
+ end
+
+ assert_equal ["setname","client-name"], $name
+ end
+
+ def test_ping
+ assert_equal "PONG", r.ping
+ end
+
+ def test_select
+ r.set "foo", "bar"
+
+ r.select 14
+ assert_equal nil, r.get("foo")
+
+ r.client.disconnect
+
+ assert_equal nil, r.get("foo")
+ end
+
+ def test_quit
+ r.quit
+
+ assert !r.client.connected?
+ end
+
+ def test_close
+ quit = 0
+
+ commands = {
+ :quit => lambda do
+ quit += 1
+ "+OK"
+ end
+ }
+
+ redis_mock(commands) do |redis|
+ assert_equal 0, quit
+
+ redis.quit
+
+ assert_equal 1, quit
+
+ redis.ping
+
+ redis.close
+
+ assert_equal 1, quit
+
+ assert !redis.connected?
+ end
+ end
+
+ def test_disconnect
+ quit = 0
+
+ commands = {
+ :quit => lambda do
+ quit += 1
+ "+OK"
+ end
+ }
+
+ redis_mock(commands) do |redis|
+ assert_equal 0, quit
+
+ redis.quit
+
+ assert_equal 1, quit
+
+ redis.ping
+
+ redis.disconnect!
+
+ assert_equal 1, quit
+
+ assert !redis.connected?
+ end
+ end
+
+ def test_shutdown
+ commands = {
+ :shutdown => lambda { :exit }
+ }
+
+ redis_mock(commands) do |redis|
+ # SHUTDOWN does not reply: test that it does not raise here.
+ assert_equal nil, redis.shutdown
+ end
+ end
+
+ def test_shutdown_with_error
+ connections = 0
+ commands = {
+ :select => lambda { |*_| connections += 1; "+OK\r\n" },
+ :connections => lambda { ":#{connections}\r\n" },
+ :shutdown => lambda { "-ERR could not shutdown\r\n" }
+ }
+
+ redis_mock(commands) do |redis|
+ connections = redis.connections
+
+ # SHUTDOWN replies with an error: test that it gets raised
+ assert_raise Redis::CommandError do
+ redis.shutdown
+ end
+
+ # The connection should remain in tact
+ assert_equal connections, redis.connections
+ end
+ end
+
+ def test_shutdown_from_pipeline
+ commands = {
+ :shutdown => lambda { :exit }
+ }
+
+ redis_mock(commands) do |redis|
+ result = redis.pipelined do
+ redis.shutdown
+ end
+
+ assert_equal nil, result
+ assert !redis.client.connected?
+ end
+ end
+
+ def test_shutdown_with_error_from_pipeline
+ connections = 0
+ commands = {
+ :select => lambda { |*_| connections += 1; "+OK\r\n" },
+ :connections => lambda { ":#{connections}\r\n" },
+ :shutdown => lambda { "-ERR could not shutdown\r\n" }
+ }
+
+ redis_mock(commands) do |redis|
+ connections = redis.connections
+
+ # SHUTDOWN replies with an error: test that it gets raised
+ assert_raise Redis::CommandError do
+ redis.pipelined do
+ redis.shutdown
+ end
+ end
+
+ # The connection should remain in tact
+ assert_equal connections, redis.connections
+ end
+ end
+
+ def test_shutdown_from_multi_exec
+ commands = {
+ :multi => lambda { "+OK\r\n" },
+ :shutdown => lambda { "+QUEUED\r\n" },
+ :exec => lambda { :exit }
+ }
+
+ redis_mock(commands) do |redis|
+ result = redis.multi do
+ redis.shutdown
+ end
+
+ assert_equal nil, result
+ assert !redis.client.connected?
+ end
+ end
+
+ def test_shutdown_with_error_from_multi_exec
+ connections = 0
+ commands = {
+ :select => lambda { |*_| connections += 1; "+OK\r\n" },
+ :connections => lambda { ":#{connections}\r\n" },
+ :multi => lambda { "+OK\r\n" },
+ :shutdown => lambda { "+QUEUED\r\n" },
+ :exec => lambda { "*1\r\n-ERR could not shutdown\r\n" }
+ }
+
+ redis_mock(commands) do |redis|
+ connections = redis.connections
+
+ # SHUTDOWN replies with an error: test that it gets returned
+ # We should test for Redis::CommandError here, but hiredis doesn't yet do
+ # custom error classes.
+ err = nil
+
+ begin
+ redis.multi { redis.shutdown }
+ rescue => err
+ end
+
+ assert err.kind_of?(StandardError)
+
+ # The connection should remain intact
+ assert_equal connections, redis.connections
+ end
+ end
+
+ def test_slaveof
+ redis_mock(:slaveof => lambda { |host, port| "+SLAVEOF #{host} #{port}" }) do |redis|
+ assert_equal "SLAVEOF somehost 6381", redis.slaveof("somehost", 6381)
+ end
+ end
+
+ def test_bgrewriteaof
+ redis_mock(:bgrewriteaof => lambda { "+BGREWRITEAOF" }) do |redis|
+ assert_equal "BGREWRITEAOF", redis.bgrewriteaof
+ end
+ end
+
+ def test_config_get
+ assert r.config(:get, "*")["timeout"] != nil
+
+ config = r.config(:get, "timeout")
+ assert_equal ["timeout"], config.keys
+ assert config.values.compact.size > 0
+ end
+
+ def test_config_set
+ begin
+ assert_equal "OK", r.config(:set, "timeout", 200)
+ assert_equal "200", r.config(:get, "*")["timeout"]
+
+ assert_equal "OK", r.config(:set, "timeout", 100)
+ assert_equal "100", r.config(:get, "*")["timeout"]
+ ensure
+ r.config :set, "timeout", 300
+ end
+ end
+
+ driver(:ruby, :hiredis) do
+ def test_consistency_on_multithreaded_env
+ t = nil
+
+ commands = {
+ :set => lambda { |key, value| t.kill; "+OK\r\n" },
+ :incr => lambda { |key| ":1\r\n" },
+ }
+
+ redis_mock(commands) do |redis|
+ t = Thread.new do
+ redis.set("foo", "bar")
+ end
+
+ t.join
+
+ assert_equal 1, redis.incr("baz")
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_blocking_commands_test.rb b/lib/vendor/redis/test/distributed_blocking_commands_test.rb
new file mode 100644
index 0000000..b28cf27
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_blocking_commands_test.rb
@@ -0,0 +1,46 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/blocking_commands"
+
+class TestDistributedBlockingCommands < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::BlockingCommands
+
+ def test_blpop_raises
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.blpop(["foo", "bar"])
+ end
+ end
+
+ def test_blpop_raises_with_old_prototype
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.blpop("foo", "bar", 0)
+ end
+ end
+
+ def test_brpop_raises
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.brpop(["foo", "bar"])
+ end
+ end
+
+ def test_brpop_raises_with_old_prototype
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.brpop("foo", "bar", 0)
+ end
+ end
+
+ def test_brpoplpush_raises
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.brpoplpush("foo", "bar")
+ end
+ end
+
+ def test_brpoplpush_raises_with_old_prototype
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.brpoplpush("foo", "bar", 0)
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_commands_on_hashes_test.rb b/lib/vendor/redis/test/distributed_commands_on_hashes_test.rb
new file mode 100644
index 0000000..ffd14f5
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_on_hashes_test.rb
@@ -0,0 +1,10 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/hashes"
+
+class TestDistributedCommandsOnHashes < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::Hashes
+end
diff --git a/lib/vendor/redis/test/distributed_commands_on_hyper_log_log_test.rb b/lib/vendor/redis/test/distributed_commands_on_hyper_log_log_test.rb
new file mode 100644
index 0000000..c118b95
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_on_hyper_log_log_test.rb
@@ -0,0 +1,33 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/hyper_log_log"
+
+class TestDistributedCommandsOnHyperLogLog < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::HyperLogLog
+
+ def test_pfmerge
+ target_version "2.8.9" do
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.pfadd "foo", "s1"
+ r.pfadd "bar", "s2"
+
+ assert r.pfmerge("res", "foo", "bar")
+ end
+ end
+ end
+
+ def test_pfcount_multiple_keys_diff_nodes
+ target_version "2.8.9" do
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.pfadd "foo", "s1"
+ r.pfadd "bar", "s2"
+
+ assert r.pfcount("res", "foo", "bar")
+ end
+ end
+ end
+
+end
diff --git a/lib/vendor/redis/test/distributed_commands_on_lists_test.rb b/lib/vendor/redis/test/distributed_commands_on_lists_test.rb
new file mode 100644
index 0000000..d22f3be
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_on_lists_test.rb
@@ -0,0 +1,22 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/lists"
+
+class TestDistributedCommandsOnLists < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::Lists
+
+ def test_rpoplpush
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.rpoplpush("foo", "bar")
+ end
+ end
+
+ def test_brpoplpush
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.brpoplpush("foo", "bar", :timeout => 1)
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_commands_on_sets_test.rb b/lib/vendor/redis/test/distributed_commands_on_sets_test.rb
new file mode 100644
index 0000000..43a070c
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_on_sets_test.rb
@@ -0,0 +1,83 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/sets"
+
+class TestDistributedCommandsOnSets < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::Sets
+
+ def test_smove
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.sadd "foo", "s1"
+ r.sadd "bar", "s2"
+
+ r.smove("foo", "bar", "s1")
+ end
+ end
+
+ def test_sinter
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+
+ r.sinter("foo", "bar")
+ end
+ end
+
+ def test_sinterstore
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+
+ r.sinterstore("baz", "foo", "bar")
+ end
+ end
+
+ def test_sunion
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ r.sunion("foo", "bar")
+ end
+ end
+
+ def test_sunionstore
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ r.sunionstore("baz", "foo", "bar")
+ end
+ end
+
+ def test_sdiff
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ r.sdiff("foo", "bar")
+ end
+ end
+
+ def test_sdiffstore
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "bar", "s2"
+ r.sadd "bar", "s3"
+
+ r.sdiffstore("baz", "foo", "bar")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_commands_on_sorted_sets_test.rb b/lib/vendor/redis/test/distributed_commands_on_sorted_sets_test.rb
new file mode 100644
index 0000000..a4150b8
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_on_sorted_sets_test.rb
@@ -0,0 +1,18 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/sorted_sets"
+
+class TestDistributedCommandsOnSortedSets < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::SortedSets
+
+ def test_zcount
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal 2, r.zcount("foo", 2, 3)
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_commands_on_strings_test.rb b/lib/vendor/redis/test/distributed_commands_on_strings_test.rb
new file mode 100644
index 0000000..ad83c12
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_on_strings_test.rb
@@ -0,0 +1,59 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/strings"
+
+class TestDistributedCommandsOnStrings < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::Strings
+
+ def test_mget
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.mget("foo", "bar")
+ end
+ end
+
+ def test_mget_mapped
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.mapped_mget("foo", "bar")
+ end
+ end
+
+ def test_mset
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.mset(:foo, "s1", :bar, "s2")
+ end
+ end
+
+ def test_mset_mapped
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.mapped_mset(:foo => "s1", :bar => "s2")
+ end
+ end
+
+ def test_msetnx
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.set("foo", "s1")
+ r.msetnx(:foo, "s2", :bar, "s3")
+ end
+ end
+
+ def test_msetnx_mapped
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.set("foo", "s1")
+ r.mapped_msetnx(:foo => "s2", :bar => "s3")
+ end
+ end
+
+ def test_bitop
+ target_version "2.5.10" do
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.set("foo", "a")
+ r.set("bar", "b")
+
+ r.bitop(:and, "foo&bar", "foo", "bar")
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_commands_on_value_types_test.rb b/lib/vendor/redis/test/distributed_commands_on_value_types_test.rb
new file mode 100644
index 0000000..0be9ce2
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_on_value_types_test.rb
@@ -0,0 +1,95 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+require "lint/value_types"
+
+class TestDistributedCommandsOnValueTypes < Test::Unit::TestCase
+
+ include Helper::Distributed
+ include Lint::ValueTypes
+
+ def test_del
+ r.set "foo", "s1"
+ r.set "bar", "s2"
+ r.set "baz", "s3"
+
+ assert_equal ["bar", "baz", "foo"], r.keys("*").sort
+
+ assert_equal 1, r.del("foo")
+
+ assert_equal ["bar", "baz"], r.keys("*").sort
+
+ assert_equal 2, r.del("bar", "baz")
+
+ assert_equal [], r.keys("*").sort
+ end
+
+ def test_del_with_array_argument
+ r.set "foo", "s1"
+ r.set "bar", "s2"
+ r.set "baz", "s3"
+
+ assert_equal ["bar", "baz", "foo"], r.keys("*").sort
+
+ assert_equal 1, r.del(["foo"])
+
+ assert_equal ["bar", "baz"], r.keys("*").sort
+
+ assert_equal 2, r.del(["bar", "baz"])
+
+ assert_equal [], r.keys("*").sort
+ end
+
+ def test_randomkey
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.randomkey
+ end
+ end
+
+ def test_rename
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.set("foo", "s1")
+ r.rename "foo", "bar"
+ end
+
+ assert_equal "s1", r.get("foo")
+ assert_equal nil, r.get("bar")
+ end
+
+ def test_renamenx
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.set("foo", "s1")
+ r.rename "foo", "bar"
+ end
+
+ assert_equal "s1", r.get("foo")
+ assert_equal nil , r.get("bar")
+ end
+
+ def test_dbsize
+ assert_equal [0], r.dbsize
+
+ r.set("foo", "s1")
+
+ assert_equal [1], r.dbsize
+ end
+
+ def test_flushdb
+ r.set("foo", "s1")
+ r.set("bar", "s2")
+
+ assert_equal [2], r.dbsize
+
+ r.flushdb
+
+ assert_equal [0], r.dbsize
+ end
+
+ def test_migrate
+ r.set("foo", "s1")
+
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.migrate("foo", {})
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_commands_requiring_clustering_test.rb b/lib/vendor/redis/test/distributed_commands_requiring_clustering_test.rb
new file mode 100644
index 0000000..da8063c
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_commands_requiring_clustering_test.rb
@@ -0,0 +1,164 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedCommandsRequiringClustering < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_rename
+ r.set("{qux}foo", "s1")
+ r.rename "{qux}foo", "{qux}bar"
+
+ assert_equal "s1", r.get("{qux}bar")
+ assert_equal nil, r.get("{qux}foo")
+ end
+
+ def test_renamenx
+ r.set("{qux}foo", "s1")
+ r.set("{qux}bar", "s2")
+
+ assert_equal false, r.renamenx("{qux}foo", "{qux}bar")
+
+ assert_equal "s1", r.get("{qux}foo")
+ assert_equal "s2", r.get("{qux}bar")
+ end
+
+ def test_brpoplpush
+ r.rpush "{qux}foo", "s1"
+ r.rpush "{qux}foo", "s2"
+
+ assert_equal "s2", r.brpoplpush("{qux}foo", "{qux}bar", :timeout => 1)
+ assert_equal ["s2"], r.lrange("{qux}bar", 0, -1)
+ end
+
+ def test_rpoplpush
+ r.rpush "{qux}foo", "s1"
+ r.rpush "{qux}foo", "s2"
+
+ assert_equal "s2", r.rpoplpush("{qux}foo", "{qux}bar")
+ assert_equal ["s2"], r.lrange("{qux}bar", 0, -1)
+ assert_equal "s1", r.rpoplpush("{qux}foo", "{qux}bar")
+ assert_equal ["s1", "s2"], r.lrange("{qux}bar", 0, -1)
+ end
+
+ def test_smove
+ r.sadd "{qux}foo", "s1"
+ r.sadd "{qux}bar", "s2"
+
+ assert r.smove("{qux}foo", "{qux}bar", "s1")
+ assert r.sismember("{qux}bar", "s1")
+ end
+
+ def test_sinter
+ r.sadd "{qux}foo", "s1"
+ r.sadd "{qux}foo", "s2"
+ r.sadd "{qux}bar", "s2"
+
+ assert_equal ["s2"], r.sinter("{qux}foo", "{qux}bar")
+ end
+
+ def test_sinterstore
+ r.sadd "{qux}foo", "s1"
+ r.sadd "{qux}foo", "s2"
+ r.sadd "{qux}bar", "s2"
+
+ r.sinterstore("{qux}baz", "{qux}foo", "{qux}bar")
+
+ assert_equal ["s2"], r.smembers("{qux}baz")
+ end
+
+ def test_sunion
+ r.sadd "{qux}foo", "s1"
+ r.sadd "{qux}foo", "s2"
+ r.sadd "{qux}bar", "s2"
+ r.sadd "{qux}bar", "s3"
+
+ assert_equal ["s1", "s2", "s3"], r.sunion("{qux}foo", "{qux}bar").sort
+ end
+
+ def test_sunionstore
+ r.sadd "{qux}foo", "s1"
+ r.sadd "{qux}foo", "s2"
+ r.sadd "{qux}bar", "s2"
+ r.sadd "{qux}bar", "s3"
+
+ r.sunionstore("{qux}baz", "{qux}foo", "{qux}bar")
+
+ assert_equal ["s1", "s2", "s3"], r.smembers("{qux}baz").sort
+ end
+
+ def test_sdiff
+ r.sadd "{qux}foo", "s1"
+ r.sadd "{qux}foo", "s2"
+ r.sadd "{qux}bar", "s2"
+ r.sadd "{qux}bar", "s3"
+
+ assert_equal ["s1"], r.sdiff("{qux}foo", "{qux}bar")
+ assert_equal ["s3"], r.sdiff("{qux}bar", "{qux}foo")
+ end
+
+ def test_sdiffstore
+ r.sadd "{qux}foo", "s1"
+ r.sadd "{qux}foo", "s2"
+ r.sadd "{qux}bar", "s2"
+ r.sadd "{qux}bar", "s3"
+
+ r.sdiffstore("{qux}baz", "{qux}foo", "{qux}bar")
+
+ assert_equal ["s1"], r.smembers("{qux}baz")
+ end
+
+ def test_sort
+ r.set("{qux}foo:1", "s1")
+ r.set("{qux}foo:2", "s2")
+
+ r.rpush("{qux}bar", "1")
+ r.rpush("{qux}bar", "2")
+
+ assert_equal ["s1"], r.sort("{qux}bar", :get => "{qux}foo:*", :limit => [0, 1])
+ assert_equal ["s2"], r.sort("{qux}bar", :get => "{qux}foo:*", :limit => [0, 1], :order => "desc alpha")
+ end
+
+ def test_sort_with_an_array_of_gets
+ r.set("{qux}foo:1:a", "s1a")
+ r.set("{qux}foo:1:b", "s1b")
+
+ r.set("{qux}foo:2:a", "s2a")
+ r.set("{qux}foo:2:b", "s2b")
+
+ r.rpush("{qux}bar", "1")
+ r.rpush("{qux}bar", "2")
+
+ assert_equal [["s1a", "s1b"]], r.sort("{qux}bar", :get => ["{qux}foo:*:a", "{qux}foo:*:b"], :limit => [0, 1])
+ assert_equal [["s2a", "s2b"]], r.sort("{qux}bar", :get => ["{qux}foo:*:a", "{qux}foo:*:b"], :limit => [0, 1], :order => "desc alpha")
+ assert_equal [["s1a", "s1b"], ["s2a", "s2b"]], r.sort("{qux}bar", :get => ["{qux}foo:*:a", "{qux}foo:*:b"])
+ end
+
+ def test_sort_with_store
+ r.set("{qux}foo:1", "s1")
+ r.set("{qux}foo:2", "s2")
+
+ r.rpush("{qux}bar", "1")
+ r.rpush("{qux}bar", "2")
+
+ r.sort("{qux}bar", :get => "{qux}foo:*", :store => "{qux}baz")
+ assert_equal ["s1", "s2"], r.lrange("{qux}baz", 0, -1)
+ end
+
+ def test_bitop
+ target_version "2.5.10" do
+ r.set("{qux}foo", "a")
+ r.set("{qux}bar", "b")
+
+ r.bitop(:and, "{qux}foo&bar", "{qux}foo", "{qux}bar")
+ assert_equal "\x60", r.get("{qux}foo&bar")
+ r.bitop(:or, "{qux}foo|bar", "{qux}foo", "{qux}bar")
+ assert_equal "\x63", r.get("{qux}foo|bar")
+ r.bitop(:xor, "{qux}foo^bar", "{qux}foo", "{qux}bar")
+ assert_equal "\x03", r.get("{qux}foo^bar")
+ r.bitop(:not, "{qux}~foo", "{qux}foo")
+ assert_equal "\x9E", r.get("{qux}~foo")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_connection_handling_test.rb b/lib/vendor/redis/test/distributed_connection_handling_test.rb
new file mode 100644
index 0000000..3308860
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_connection_handling_test.rb
@@ -0,0 +1,23 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedConnectionHandling < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_ping
+ assert_equal ["PONG"], r.ping
+ end
+
+ def test_select
+ r.set "foo", "bar"
+
+ r.select 14
+ assert_equal nil, r.get("foo")
+
+ r.select 15
+
+ assert_equal "bar", r.get("foo")
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_internals_test.rb b/lib/vendor/redis/test/distributed_internals_test.rb
new file mode 100644
index 0000000..887881f
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_internals_test.rb
@@ -0,0 +1,79 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedInternals < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_provides_a_meaningful_inspect
+ nodes = ["redis://localhost:#{PORT}/15", *NODES]
+ redis = Redis::Distributed.new nodes
+
+ assert_equal "#<Redis client v#{Redis::VERSION} for #{redis.nodes.map(&:id).join(', ')}>", redis.inspect
+ end
+
+ def test_default_as_urls
+ nodes = ["redis://localhost:#{PORT}/15", *NODES]
+ redis = Redis::Distributed.new nodes
+ assert_equal ["redis://localhost:#{PORT}/15", *NODES], redis.nodes.map { |node| node.client.id}
+ end
+
+ def test_default_as_config_hashes
+ nodes = [OPTIONS.merge(:host => '127.0.0.1'), OPTIONS.merge(:host => 'somehost', :port => PORT.next)]
+ redis = Redis::Distributed.new nodes
+ assert_equal ["redis://127.0.0.1:#{PORT}/15","redis://somehost:#{PORT.next}/15"], redis.nodes.map { |node| node.client.id }
+ end
+
+ def test_as_mix_and_match
+ nodes = ["redis://127.0.0.1:7389/15", OPTIONS.merge(:host => 'somehost'), OPTIONS.merge(:host => 'somehost', :port => PORT.next)]
+ redis = Redis::Distributed.new nodes
+ assert_equal ["redis://127.0.0.1:7389/15", "redis://somehost:#{PORT}/15", "redis://somehost:#{PORT.next}/15"], redis.nodes.map { |node| node.client.id }
+ end
+
+ def test_override_id
+ nodes = [OPTIONS.merge(:host => '127.0.0.1', :id => "test"), OPTIONS.merge( :host => 'somehost', :port => PORT.next, :id => "test1")]
+ redis = Redis::Distributed.new nodes
+ assert_equal redis.nodes.first.client.id, "test"
+ assert_equal redis.nodes.last.client.id, "test1"
+ assert_equal "#<Redis client v#{Redis::VERSION} for #{redis.nodes.map(&:id).join(', ')}>", redis.inspect
+ end
+
+ def test_can_be_duped_to_create_a_new_connection
+ redis = Redis::Distributed.new(NODES)
+
+ clients = redis.info[0]["connected_clients"].to_i
+
+ r2 = redis.dup
+ r2.ping
+
+ assert_equal clients + 1, redis.info[0]["connected_clients"].to_i
+ end
+
+ def test_keeps_options_after_dup
+ r1 = Redis::Distributed.new(NODES, :tag => /^(\w+):/)
+
+ assert_raise(Redis::Distributed::CannotDistribute) do
+ r1.sinter("foo", "bar")
+ end
+
+ assert_equal [], r1.sinter("baz:foo", "baz:bar")
+
+ r2 = r1.dup
+
+ assert_raise(Redis::Distributed::CannotDistribute) do
+ r2.sinter("foo", "bar")
+ end
+
+ assert_equal [], r2.sinter("baz:foo", "baz:bar")
+ end
+
+ def test_colliding_node_ids
+ nodes = ["redis://localhost:#{PORT}/15", "redis://localhost:#{PORT}/15", *NODES]
+
+ assert_raise(RuntimeError) do
+ Redis::Distributed.new nodes
+ end
+ end
+
+end
diff --git a/lib/vendor/redis/test/distributed_key_tags_test.rb b/lib/vendor/redis/test/distributed_key_tags_test.rb
new file mode 100644
index 0000000..12b6d68
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_key_tags_test.rb
@@ -0,0 +1,52 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedKeyTags < Test::Unit::TestCase
+
+ include Helper
+ include Helper::Distributed
+
+ def test_hashes_consistently
+ r1 = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
+ r2 = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
+ r3 = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
+
+ assert_equal r1.node_for("foo").id, r2.node_for("foo").id
+ assert_equal r1.node_for("foo").id, r3.node_for("foo").id
+ end
+
+ def test_allows_clustering_of_keys
+ r = Redis::Distributed.new(NODES)
+ r.add_node("redis://127.0.0.1:#{PORT}/14")
+ r.flushdb
+
+ 100.times do |i|
+ r.set "{foo}users:#{i}", i
+ end
+
+ assert_equal [0, 100], r.nodes.map { |node| node.keys.size }
+ end
+
+ def test_distributes_keys_if_no_clustering_is_used
+ r.add_node("redis://127.0.0.1:#{PORT}/14")
+ r.flushdb
+
+ r.set "users:1", 1
+ r.set "users:4", 4
+
+ assert_equal [1, 1], r.nodes.map { |node| node.keys.size }
+ end
+
+ def test_allows_passing_a_custom_tag_extractor
+ r = Redis::Distributed.new(NODES, :tag => /^(.+?):/)
+ r.add_node("redis://127.0.0.1:#{PORT}/14")
+ r.flushdb
+
+ 100.times do |i|
+ r.set "foo:users:#{i}", i
+ end
+
+ assert_equal [0, 100], r.nodes.map { |node| node.keys.size }
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_persistence_control_commands_test.rb b/lib/vendor/redis/test/distributed_persistence_control_commands_test.rb
new file mode 100644
index 0000000..c243601
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_persistence_control_commands_test.rb
@@ -0,0 +1,26 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedPersistenceControlCommands < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_save
+ redis_mock(:save => lambda { "+SAVE" }) do |redis|
+ assert_equal ["SAVE"], redis.save
+ end
+ end
+
+ def test_bgsave
+ redis_mock(:bgsave => lambda { "+BGSAVE" }) do |redis|
+ assert_equal ["BGSAVE"], redis.bgsave
+ end
+ end
+
+ def test_lastsave
+ redis_mock(:lastsave => lambda { "+LASTSAVE" }) do |redis|
+ assert_equal ["LASTSAVE"], redis.lastsave
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_publish_subscribe_test.rb b/lib/vendor/redis/test/distributed_publish_subscribe_test.rb
new file mode 100644
index 0000000..df36506
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_publish_subscribe_test.rb
@@ -0,0 +1,92 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedPublishSubscribe < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_subscribe_and_unsubscribe
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.subscribe("foo", "bar") { }
+ end
+
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.subscribe("{qux}foo", "bar") { }
+ end
+ end
+
+ def test_subscribe_and_unsubscribe_with_tags
+ @subscribed = false
+ @unsubscribed = false
+
+ wire = Wire.new do
+ r.subscribe("foo") do |on|
+ on.subscribe do |channel, total|
+ @subscribed = true
+ @t1 = total
+ end
+
+ on.message do |channel, message|
+ if message == "s1"
+ r.unsubscribe
+ @message = message
+ end
+ end
+
+ on.unsubscribe do |channel, total|
+ @unsubscribed = true
+ @t2 = total
+ end
+ end
+ end
+
+ # Wait until the subscription is active before publishing
+ Wire.pass while !@subscribed
+
+ Redis::Distributed.new(NODES).publish("foo", "s1")
+
+ wire.join
+
+ assert @subscribed
+ assert_equal 1, @t1
+ assert @unsubscribed
+ assert_equal 0, @t2
+ assert_equal "s1", @message
+ end
+
+ def test_subscribe_within_subscribe
+ @channels = []
+
+ wire = Wire.new do
+ r.subscribe("foo") do |on|
+ on.subscribe do |channel, total|
+ @channels << channel
+
+ r.subscribe("bar") if channel == "foo"
+ r.unsubscribe if channel == "bar"
+ end
+ end
+ end
+
+ wire.join
+
+ assert_equal ["foo", "bar"], @channels
+ end
+
+ def test_other_commands_within_a_subscribe
+ assert_raise Redis::CommandError do
+ r.subscribe("foo") do |on|
+ on.subscribe do |channel, total|
+ r.set("bar", "s2")
+ end
+ end
+ end
+ end
+
+ def test_subscribe_without_a_block
+ assert_raise LocalJumpError do
+ r.subscribe("foo")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_remote_server_control_commands_test.rb b/lib/vendor/redis/test/distributed_remote_server_control_commands_test.rb
new file mode 100644
index 0000000..7799d4f
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_remote_server_control_commands_test.rb
@@ -0,0 +1,66 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedRemoteServerControlCommands < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_info
+ keys = [
+ "redis_version",
+ "uptime_in_seconds",
+ "uptime_in_days",
+ "connected_clients",
+ "used_memory",
+ "total_connections_received",
+ "total_commands_processed",
+ ]
+
+ infos = r.info
+
+ infos.each do |info|
+ keys.each do |k|
+ msg = "expected #info to include #{k}"
+ assert info.keys.include?(k), msg
+ end
+ end
+ end
+
+ def test_info_commandstats
+ target_version "2.5.7" do
+ r.nodes.each { |n| n.config(:resetstat) }
+ r.ping # Executed on every node
+
+ r.info(:commandstats).each do |info|
+ assert_equal "1", info["ping"]["calls"]
+ end
+ end
+ end
+
+ def test_monitor
+ begin
+ r.monitor
+ rescue Exception => ex
+ ensure
+ assert ex.kind_of?(NotImplementedError)
+ end
+ end
+
+ def test_echo
+ assert_equal ["foo bar baz\n"], r.echo("foo bar baz\n")
+ end
+
+ def test_time
+ target_version "2.5.4" do
+ # Test that the difference between the time that Ruby reports and the time
+ # that Redis reports is minimal (prevents the test from being racy).
+ r.time.each do |rv|
+ redis_usec = rv[0] * 1_000_000 + rv[1]
+ ruby_usec = Integer(Time.now.to_f * 1_000_000)
+
+ assert 500_000 > (ruby_usec - redis_usec).abs
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_scripting_test.rb b/lib/vendor/redis/test/distributed_scripting_test.rb
new file mode 100644
index 0000000..00bdaa6
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_scripting_test.rb
@@ -0,0 +1,102 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedScripting < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def to_sha(script)
+ r.script(:load, script).first
+ end
+
+ def test_script_exists
+ target_version "2.5.9" do # 2.6-rc1
+ a = to_sha("return 1")
+ b = a.succ
+
+ assert_equal [true], r.script(:exists, a)
+ assert_equal [false], r.script(:exists, b)
+ assert_equal [[true]], r.script(:exists, [a])
+ assert_equal [[false]], r.script(:exists, [b])
+ assert_equal [[true, false]], r.script(:exists, [a, b])
+ end
+ end
+
+ def test_script_flush
+ target_version "2.5.9" do # 2.6-rc1
+ sha = to_sha("return 1")
+ assert r.script(:exists, sha).first
+ assert_equal ["OK"], r.script(:flush)
+ assert !r.script(:exists, sha).first
+ end
+ end
+
+ def test_script_kill
+ target_version "2.5.9" do # 2.6-rc1
+ redis_mock(:script => lambda { |arg| "+#{arg.upcase}" }) do |redis|
+ assert_equal ["KILL"], redis.script(:kill)
+ end
+ end
+ end
+
+ def test_eval
+ target_version "2.5.9" do # 2.6-rc1
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.eval("return #KEYS")
+ end
+
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.eval("return KEYS", ["k1", "k2"])
+ end
+
+ assert_equal ["k1"], r.eval("return KEYS", ["k1"])
+ assert_equal ["a1", "a2"], r.eval("return ARGV", ["k1"], ["a1", "a2"])
+ end
+ end
+
+ def test_eval_with_options_hash
+ target_version "2.5.9" do # 2.6-rc1
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.eval("return #KEYS", {})
+ end
+
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.eval("return KEYS", { :keys => ["k1", "k2"] })
+ end
+
+ assert_equal ["k1"], r.eval("return KEYS", { :keys => ["k1"] })
+ assert_equal ["a1", "a2"], r.eval("return ARGV", { :keys => ["k1"], :argv => ["a1", "a2"] })
+ end
+ end
+
+ def test_evalsha
+ target_version "2.5.9" do # 2.6-rc1
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.evalsha(to_sha("return #KEYS"))
+ end
+
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.evalsha(to_sha("return KEYS"), ["k1", "k2"])
+ end
+
+ assert_equal ["k1"], r.evalsha(to_sha("return KEYS"), ["k1"])
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), ["k1"], ["a1", "a2"])
+ end
+ end
+
+ def test_evalsha_with_options_hash
+ target_version "2.5.9" do # 2.6-rc1
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.evalsha(to_sha("return #KEYS"), {})
+ end
+
+ assert_raises(Redis::Distributed::CannotDistribute) do
+ r.evalsha(to_sha("return KEYS"), { :keys => ["k1", "k2"] })
+ end
+
+ assert_equal ["k1"], r.evalsha(to_sha("return KEYS"), { :keys => ["k1"] })
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), { :keys => ["k1"], :argv => ["a1", "a2"] })
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_sorting_test.rb b/lib/vendor/redis/test/distributed_sorting_test.rb
new file mode 100644
index 0000000..4ae3cf5
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_sorting_test.rb
@@ -0,0 +1,20 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedSorting < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_sort
+ assert_raise(Redis::Distributed::CannotDistribute) do
+ r.set("foo:1", "s1")
+ r.set("foo:2", "s2")
+
+ r.rpush("bar", "1")
+ r.rpush("bar", "2")
+
+ r.sort("bar", :get => "foo:*", :limit => [0, 1])
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_test.rb b/lib/vendor/redis/test/distributed_test.rb
new file mode 100644
index 0000000..b55287b
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_test.rb
@@ -0,0 +1,58 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributed < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_handle_multiple_servers
+ @r = Redis::Distributed.new ["redis://localhost:#{PORT}/15", *NODES]
+
+ 100.times do |idx|
+ @r.set(idx.to_s, "foo#{idx}")
+ end
+
+ 100.times do |idx|
+ assert_equal "foo#{idx}", @r.get(idx.to_s)
+ end
+
+ assert_equal "0", @r.keys("*").sort.first
+ assert_equal "string", @r.type("1")
+ end
+
+ def test_add_nodes
+ logger = Logger.new("/dev/null")
+
+ @r = Redis::Distributed.new NODES, :logger => logger, :timeout => 10
+
+ assert_equal "127.0.0.1", @r.nodes[0].client.host
+ assert_equal PORT, @r.nodes[0].client.port
+ assert_equal 15, @r.nodes[0].client.db
+ assert_equal 10, @r.nodes[0].client.timeout
+ assert_equal logger, @r.nodes[0].client.logger
+
+ @r.add_node("redis://127.0.0.1:6380/14")
+
+ assert_equal "127.0.0.1", @r.nodes[1].client.host
+ assert_equal 6380, @r.nodes[1].client.port
+ assert_equal 14, @r.nodes[1].client.db
+ assert_equal 10, @r.nodes[1].client.timeout
+ assert_equal logger, @r.nodes[1].client.logger
+ end
+
+ def test_pipelining_commands_cannot_be_distributed
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.pipelined do
+ r.lpush "foo", "s1"
+ r.lpush "foo", "s2"
+ end
+ end
+ end
+
+ def test_unknown_commands_does_not_work_by_default
+ assert_raise NoMethodError do
+ r.not_yet_implemented_command
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/distributed_transactions_test.rb b/lib/vendor/redis/test/distributed_transactions_test.rb
new file mode 100644
index 0000000..abfb8aa
--- /dev/null
+++ b/lib/vendor/redis/test/distributed_transactions_test.rb
@@ -0,0 +1,32 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestDistributedTransactions < Test::Unit::TestCase
+
+ include Helper::Distributed
+
+ def test_multi_discard
+ @foo = nil
+
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.multi { @foo = 1 }
+ end
+
+ assert_equal nil, @foo
+
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.discard
+ end
+ end
+
+ def test_watch_unwatch
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.watch("foo")
+ end
+
+ assert_raise Redis::Distributed::CannotDistribute do
+ r.unwatch
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/encoding_test.rb b/lib/vendor/redis/test/encoding_test.rb
new file mode 100644
index 0000000..cb54bcb
--- /dev/null
+++ b/lib/vendor/redis/test/encoding_test.rb
@@ -0,0 +1,18 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestEncoding < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_returns_properly_encoded_strings
+ if defined?(Encoding)
+ with_external_encoding("UTF-8") do
+ r.set "foo", "שלום"
+
+ assert_equal "Shalom שלום", "Shalom " + r.get("foo")
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/error_replies_test.rb b/lib/vendor/redis/test/error_replies_test.rb
new file mode 100644
index 0000000..08ec81e
--- /dev/null
+++ b/lib/vendor/redis/test/error_replies_test.rb
@@ -0,0 +1,59 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestErrorReplies < Test::Unit::TestCase
+
+ include Helper::Client
+
+ # Every test shouldn't disconnect from the server. Also, when error replies are
+ # in play, the protocol should never get into an invalid state where there are
+ # pending replies in the connection. Calling INFO after every test ensures that
+ # the protocol is still in a valid state.
+ def with_reconnection_check
+ before = r.info["total_connections_received"]
+ yield(r)
+ after = r.info["total_connections_received"]
+ ensure
+ assert_equal before, after
+ end
+
+ def test_error_reply_for_single_command
+ with_reconnection_check do
+ begin
+ r.unknown_command
+ rescue => ex
+ ensure
+ assert ex.message =~ /unknown command/i
+ end
+ end
+ end
+
+ def test_raise_first_error_reply_in_pipeline
+ with_reconnection_check do
+ begin
+ r.pipelined do
+ r.set("foo", "s1")
+ r.incr("foo") # not an integer
+ r.lpush("foo", "value") # wrong kind of value
+ end
+ rescue => ex
+ ensure
+ assert ex.message =~ /not an integer/i
+ end
+ end
+ end
+
+ def test_recover_from_raise_in__call_loop
+ with_reconnection_check do
+ begin
+ r.client.call_loop([:invalid_monitor]) do
+ assert false # Should never be executed
+ end
+ rescue => ex
+ ensure
+ assert ex.message =~ /unknown command/i
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/fork_safety_test.rb b/lib/vendor/redis/test/fork_safety_test.rb
new file mode 100644
index 0000000..a49d5b4
--- /dev/null
+++ b/lib/vendor/redis/test/fork_safety_test.rb
@@ -0,0 +1,65 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestForkSafety < Test::Unit::TestCase
+
+ include Helper::Client
+ include Helper::Skipable
+
+ driver(:ruby, :hiredis) do
+ def test_fork_safety
+ redis = Redis.new(OPTIONS)
+ redis.set "foo", 1
+
+ child_pid = fork do
+ begin
+ # InheritedError triggers a reconnect,
+ # so we need to disable reconnects to force
+ # the exception bubble up
+ redis.without_reconnect do
+ redis.set "foo", 2
+ end
+ rescue Redis::InheritedError
+ exit 127
+ end
+ end
+
+ _, status = Process.wait2(child_pid)
+
+ assert_equal 127, status.exitstatus
+ assert_equal "1", redis.get("foo")
+
+ rescue NotImplementedError => error
+ raise unless error.message =~ /fork is not available/
+ return skip(error.message)
+ end
+
+ def test_fork_safety_with_enabled_inherited_socket
+ redis = Redis.new(OPTIONS.merge(:inherit_socket => true))
+ redis.set "foo", 1
+
+ child_pid = fork do
+ begin
+ # InheritedError triggers a reconnect,
+ # so we need to disable reconnects to force
+ # the exception bubble up
+ redis.without_reconnect do
+ redis.set "foo", 2
+ end
+ rescue Redis::InheritedError
+ exit 127
+ end
+ end
+
+ _, status = Process.wait2(child_pid)
+
+ assert_equal 0, status.exitstatus
+ assert_equal "2", redis.get("foo")
+
+ rescue NotImplementedError => error
+ raise unless error.message =~ /fork is not available/
+ return skip(error.message)
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/helper.rb b/lib/vendor/redis/test/helper.rb
new file mode 100644
index 0000000..1694407
--- /dev/null
+++ b/lib/vendor/redis/test/helper.rb
@@ -0,0 +1,232 @@
+$:.unshift File.expand_path("../lib", File.dirname(__FILE__))
+$:.unshift File.expand_path(File.dirname(__FILE__))
+
+require "test/unit"
+require "logger"
+require "stringio"
+
+(class Random; def self.rand(*args) super end; end) unless defined?(Random)
+
+begin
+ require "ruby-debug"
+rescue LoadError
+end
+
+$VERBOSE = true
+
+ENV["conn"] ||= "ruby"
+
+require "redis"
+require "redis/distributed"
+require "redis/connection/#{ENV["conn"]}"
+
+require "support/redis_mock"
+require "support/connection/#{ENV["conn"]}"
+
+PORT = 6381
+OPTIONS = {:port => PORT, :db => 15, :timeout => Float(ENV["TIMEOUT"] || 0.1)}
+NODES = ["redis://127.0.0.1:#{PORT}/15"]
+
+def init(redis)
+ begin
+ redis.select 14
+ redis.flushdb
+ redis.select 15
+ redis.flushdb
+ redis
+ rescue Redis::CannotConnectError
+ puts <<-EOS
+
+ Cannot connect to Redis.
+
+ Make sure Redis is running on localhost, port #{PORT}.
+ This testing suite connects to the database 15.
+
+ Try this once:
+
+ $ rake clean
+
+ Then run the build again:
+
+ $ rake
+
+ EOS
+ exit 1
+ end
+end
+
+def driver(*drivers, &blk)
+ if drivers.map(&:to_s).include?(ENV["conn"])
+ class_eval(&blk)
+ end
+end
+
+module Helper
+
+ def run(runner)
+ if respond_to?(:around)
+ around { super(runner) }
+ else
+ super
+ end
+ end
+
+ def silent
+ verbose, $VERBOSE = $VERBOSE, false
+
+ begin
+ yield
+ ensure
+ $VERBOSE = verbose
+ end
+ end
+
+ def with_external_encoding(encoding)
+ original_encoding = Encoding.default_external
+
+ begin
+ silent { Encoding.default_external = Encoding.find(encoding) }
+ yield
+ ensure
+ silent { Encoding.default_external = original_encoding }
+ end
+ end
+
+ def try_encoding(encoding, &block)
+ if defined?(Encoding)
+ with_external_encoding(encoding, &block)
+ else
+ yield
+ end
+ end
+
+ class Version
+
+ include Comparable
+
+ attr :parts
+
+ def initialize(v)
+ case v
+ when Version
+ @parts = v.parts
+ else
+ @parts = v.to_s.split(".")
+ end
+ end
+
+ def <=>(other)
+ other = Version.new(other)
+ length = [self.parts.length, other.parts.length].max
+ length.times do |i|
+ a, b = self.parts[i], other.parts[i]
+
+ return -1 if a.nil?
+ return +1 if b.nil?
+ return a.to_i <=> b.to_i if a != b
+ end
+
+ 0
+ end
+ end
+
+ module Generic
+
+ include Helper
+
+ attr_reader :log
+ attr_reader :redis
+
+ alias :r :redis
+
+ def setup
+ @log = StringIO.new
+ @redis = init _new_client
+
+ # Run GC to make sure orphaned connections are closed.
+ GC.start
+ end
+
+ def teardown
+ @redis.quit if @redis
+ end
+
+ def redis_mock(commands, options = {}, &blk)
+ RedisMock.start(commands, options) do |port|
+ yield _new_client(options.merge(:port => port))
+ end
+ end
+
+ def redis_mock_with_handler(handler, options = {}, &blk)
+ RedisMock.start_with_handler(handler, options) do |port|
+ yield _new_client(options.merge(:port => port))
+ end
+ end
+
+ def assert_in_range(range, value)
+ assert range.include?(value), "expected #{value} to be in #{range.inspect}"
+ end
+
+ def target_version(target)
+ if version < target
+ skip("Requires Redis > #{target}") if respond_to?(:skip)
+ else
+ yield
+ end
+ end
+ end
+
+ module Client
+
+ include Generic
+
+ def version
+ Version.new(redis.info["redis_version"])
+ end
+
+ private
+
+ def _format_options(options)
+ OPTIONS.merge(:logger => ::Logger.new(@log)).merge(options)
+ end
+
+ def _new_client(options = {})
+ Redis.new(_format_options(options).merge(:driver => ENV["conn"]))
+ end
+ end
+
+ module Distributed
+
+ include Generic
+
+ def version
+ Version.new(redis.info.first["redis_version"])
+ end
+
+ private
+
+ def _format_options(options)
+ {
+ :timeout => OPTIONS[:timeout],
+ :logger => ::Logger.new(@log),
+ }.merge(options)
+ end
+
+ def _new_client(options = {})
+ Redis::Distributed.new(NODES, _format_options(options).merge(:driver => ENV["conn"]))
+ end
+ end
+
+ # Basic support for `skip` in 1.8.x
+ # Note: YOU MUST use `return skip(message)` in order to appropriately bail
+ # from a running test.
+ module Skipable
+ Skipped = Class.new(RuntimeError)
+
+ def skip(message = nil, bt = caller)
+ return super if defined?(super)
+
+ $stderr.puts("SKIPPED: #{self} #{message || 'no reason given'}")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/helper_test.rb b/lib/vendor/redis/test/helper_test.rb
new file mode 100644
index 0000000..23da68d
--- /dev/null
+++ b/lib/vendor/redis/test/helper_test.rb
@@ -0,0 +1,24 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestHelper < Test::Unit::TestCase
+
+ include Helper
+
+ def test_version_comparison
+ v = Version.new("2.0.1")
+
+ assert v > "1"
+ assert v > "2"
+ assert v < "3"
+ assert v < "10"
+
+ assert v < "2.1"
+ assert v < "2.0.2"
+ assert v < "2.0.1.1"
+ assert v < "2.0.10"
+
+ assert v == "2.0.1"
+ end
+end
diff --git a/lib/vendor/redis/test/internals_test.rb b/lib/vendor/redis/test/internals_test.rb
new file mode 100644
index 0000000..865c600
--- /dev/null
+++ b/lib/vendor/redis/test/internals_test.rb
@@ -0,0 +1,437 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestInternals < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_logger
+ r.ping
+
+ assert log.string["[Redis] command=PING"]
+ assert log.string =~ /\[Redis\] call_time=\d+\.\d+ ms/
+ end
+
+ def test_logger_with_pipelining
+ r.pipelined do
+ r.set "foo", "bar"
+ r.get "foo"
+ end
+
+ assert log.string[" command=SET args=\"foo\" \"bar\""]
+ assert log.string[" command=GET args=\"foo\""]
+ end
+
+ def test_recovers_from_failed_commands
+ # See https://github.com/redis/redis-rb/issues#issue/28
+
+ assert_raise(Redis::CommandError) do
+ r.command_that_doesnt_exist
+ end
+
+ assert_nothing_raised do
+ r.info
+ end
+ end
+
+ def test_raises_on_protocol_errors
+ redis_mock(:ping => lambda { |*_| "foo" }) do |redis|
+ assert_raise(Redis::ProtocolError) do
+ redis.ping
+ end
+ end
+ end
+
+ def test_provides_a_meaningful_inspect
+ assert_equal "#<Redis client v#{Redis::VERSION} for redis://127.0.0.1:#{PORT}/15>", r.inspect
+ end
+
+ def test_redis_current
+ assert_equal "127.0.0.1", Redis.current.client.host
+ assert_equal 6379, Redis.current.client.port
+ assert_equal 0, Redis.current.client.db
+
+ Redis.current = Redis.new(OPTIONS.merge(:port => 6380, :db => 1))
+
+ t = Thread.new do
+ assert_equal "127.0.0.1", Redis.current.client.host
+ assert_equal 6380, Redis.current.client.port
+ assert_equal 1, Redis.current.client.db
+ end
+
+ t.join
+
+ assert_equal "127.0.0.1", Redis.current.client.host
+ assert_equal 6380, Redis.current.client.port
+ assert_equal 1, Redis.current.client.db
+ end
+
+ def test_redis_connected?
+ fresh_client = _new_client
+ assert !fresh_client.connected?
+
+ fresh_client.ping
+ assert fresh_client.connected?
+
+ fresh_client.quit
+ assert !fresh_client.connected?
+ end
+
+ def test_default_id_with_host_and_port
+ redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0))
+ assert_equal "redis://host:1234/0", redis.client.id
+ end
+
+ def test_default_id_with_host_and_port_and_explicit_scheme
+ redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0, :scheme => "foo"))
+ assert_equal "redis://host:1234/0", redis.client.id
+ end
+
+ def test_default_id_with_path
+ redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0))
+ assert_equal "redis:///tmp/redis.sock/0", redis.client.id
+ end
+
+ def test_default_id_with_path_and_explicit_scheme
+ redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0, :scheme => "foo"))
+ assert_equal "redis:///tmp/redis.sock/0", redis.client.id
+ end
+
+ def test_override_id
+ redis = Redis.new(OPTIONS.merge(:id => "test"))
+ assert_equal redis.client.id, "test"
+ end
+
+ def test_timeout
+ assert_nothing_raised do
+ Redis.new(OPTIONS.merge(:timeout => 0))
+ end
+ end
+
+ def test_id_inside_multi
+ redis = Redis.new(OPTIONS)
+ id = nil
+
+ redis.multi do
+ id = redis.id
+ end
+
+ assert_equal id, "redis://127.0.0.1:6381/15"
+ end
+
+ driver(:ruby) do
+ def test_tcp_keepalive
+ keepalive = {:time => 20, :intvl => 10, :probes => 5}
+
+ redis = Redis.new(OPTIONS.merge(:tcp_keepalive => keepalive))
+ redis.ping
+
+ connection = redis.client.connection
+ actual_keepalive = connection.get_tcp_keepalive
+
+ [:time, :intvl, :probes].each do |key|
+ if actual_keepalive.has_key?(key)
+ assert_equal actual_keepalive[key], keepalive[key]
+ end
+ end
+ end
+ end
+
+ def test_time
+ target_version "2.5.4" do
+ # Test that the difference between the time that Ruby reports and the time
+ # that Redis reports is minimal (prevents the test from being racy).
+ rv = r.time
+
+ redis_usec = rv[0] * 1_000_000 + rv[1]
+ ruby_usec = Integer(Time.now.to_f * 1_000_000)
+
+ assert 500_000 > (ruby_usec - redis_usec).abs
+ end
+ end
+
+ def test_connection_timeout
+ opts = OPTIONS.merge(:host => "10.255.255.254", :connect_timeout => 0.1, :timeout => 5.0)
+ start_time = Time.now
+ assert_raise Redis::CannotConnectError do
+ Redis.new(opts).ping
+ end
+ assert (Time.now - start_time) <= opts[:timeout]
+ end
+
+ def close_on_ping(seq, options = {})
+ $request = 0
+
+ command = lambda do
+ idx = $request
+ $request += 1
+
+ rv = "+%d" % idx
+ rv = nil if seq.include?(idx)
+ rv
+ end
+
+ redis_mock({:ping => command}, {:timeout => 0.1}.merge(options)) do |redis|
+ yield(redis)
+ end
+ end
+
+ def test_retry_by_default
+ close_on_ping([0]) do |redis|
+ assert_equal "1", redis.ping
+ end
+ end
+
+ def test_retry_when_wrapped_in_with_reconnect_true
+ close_on_ping([0]) do |redis|
+ redis.with_reconnect(true) do
+ assert_equal "1", redis.ping
+ end
+ end
+ end
+
+ def test_dont_retry_when_wrapped_in_with_reconnect_false
+ close_on_ping([0]) do |redis|
+ assert_raise Redis::ConnectionError do
+ redis.with_reconnect(false) do
+ redis.ping
+ end
+ end
+ end
+ end
+
+ def test_dont_retry_when_wrapped_in_without_reconnect
+ close_on_ping([0]) do |redis|
+ assert_raise Redis::ConnectionError do
+ redis.without_reconnect do
+ redis.ping
+ end
+ end
+ end
+ end
+
+ def test_retry_only_once_when_read_raises_econnreset
+ close_on_ping([0, 1]) do |redis|
+ assert_raise Redis::ConnectionError do
+ redis.ping
+ end
+
+ assert !redis.client.connected?
+ end
+ end
+
+ def test_retry_with_custom_reconnect_attempts
+ close_on_ping([0, 1], :reconnect_attempts => 2) do |redis|
+ assert_equal "2", redis.ping
+ end
+ end
+
+ def test_retry_with_custom_reconnect_attempts_can_still_fail
+ close_on_ping([0, 1, 2], :reconnect_attempts => 2) do |redis|
+ assert_raise Redis::ConnectionError do
+ redis.ping
+ end
+
+ assert !redis.client.connected?
+ end
+ end
+
+ def test_don_t_retry_when_second_read_in_pipeline_raises_econnreset
+ close_on_ping([1]) do |redis|
+ assert_raise Redis::ConnectionError do
+ redis.pipelined do
+ redis.ping
+ redis.ping # Second #read times out
+ end
+ end
+
+ assert !redis.client.connected?
+ end
+ end
+
+ def close_on_connection(seq)
+ $n = 0
+
+ read_command = lambda do |session|
+ Array.new(session.gets[1..-3].to_i) do
+ bytes = session.gets[1..-3].to_i
+ arg = session.read(bytes)
+ session.read(2) # Discard \r\n
+ arg
+ end
+ end
+
+ handler = lambda do |session|
+ n = $n
+ $n += 1
+
+ select = read_command.call(session)
+ if select[0].downcase == "select"
+ session.write("+OK\r\n")
+ else
+ raise "Expected SELECT"
+ end
+
+ if !seq.include?(n)
+ while read_command.call(session)
+ session.write("+#{n}\r\n")
+ end
+ end
+ end
+
+ redis_mock_with_handler(handler) do |redis|
+ yield(redis)
+ end
+ end
+
+ def test_retry_on_write_error_by_default
+ close_on_connection([0]) do |redis|
+ assert_equal "1", redis.client.call(["x" * 128 * 1024])
+ end
+ end
+
+ def test_retry_on_write_error_when_wrapped_in_with_reconnect_true
+ close_on_connection([0]) do |redis|
+ redis.with_reconnect(true) do
+ assert_equal "1", redis.client.call(["x" * 128 * 1024])
+ end
+ end
+ end
+
+ def test_dont_retry_on_write_error_when_wrapped_in_with_reconnect_false
+ close_on_connection([0]) do |redis|
+ assert_raise Redis::ConnectionError do
+ redis.with_reconnect(false) do
+ redis.client.call(["x" * 128 * 1024])
+ end
+ end
+ end
+ end
+
+ def test_dont_retry_on_write_error_when_wrapped_in_without_reconnect
+ close_on_connection([0]) do |redis|
+ assert_raise Redis::ConnectionError do
+ redis.without_reconnect do
+ redis.client.call(["x" * 128 * 1024])
+ end
+ end
+ end
+ end
+
+ def test_connecting_to_unix_domain_socket
+ assert_nothing_raised do
+ Redis.new(OPTIONS.merge(:path => "./test/db/redis.sock")).ping
+ end
+ end
+
+ driver(:ruby, :hiredis) do
+ def test_bubble_timeout_without_retrying
+ serv = TCPServer.new(6380)
+
+ redis = Redis.new(:port => 6380, :timeout => 0.1)
+
+ assert_raise(Redis::TimeoutError) do
+ redis.ping
+ end
+
+ ensure
+ serv.close if serv
+ end
+ end
+
+ def test_client_options
+ redis = Redis.new(OPTIONS.merge(:host => "host", :port => 1234, :db => 1, :scheme => "foo"))
+
+ assert_equal "host", redis.client.options[:host]
+ assert_equal 1234, redis.client.options[:port]
+ assert_equal 1, redis.client.options[:db]
+ assert_equal "foo", redis.client.options[:scheme]
+ end
+
+ def test_does_not_change_self_client_options
+ redis = Redis.new(OPTIONS.merge(:host => "host", :port => 1234, :db => 1, :scheme => "foo"))
+ options = redis.client.options
+
+ options[:host] << "new_host"
+ options[:scheme] << "bar"
+ options.merge!(:db => 0)
+
+ assert_equal "host", redis.client.options[:host]
+ assert_equal 1, redis.client.options[:db]
+ assert_equal "foo", redis.client.options[:scheme]
+ end
+
+ def test_resolves_localhost
+ assert_nothing_raised do
+ Redis.new(OPTIONS.merge(:host => 'localhost')).ping
+ end
+ end
+
+ class << self
+ def af_family_supported(af)
+ hosts = {
+ Socket::AF_INET => "127.0.0.1",
+ Socket::AF_INET6 => "::1",
+ }
+
+ begin
+ s = Socket.new(af, Socket::SOCK_STREAM, 0)
+ begin
+ tries = 5
+ begin
+ sa = Socket.pack_sockaddr_in(1024 + Random.rand(63076), hosts[af])
+ s.bind(sa)
+ rescue Errno::EADDRINUSE
+ tries -= 1
+ retry if tries > 0
+
+ raise
+ end
+ yield
+ rescue Errno::EADDRNOTAVAIL
+ ensure
+ s.close
+ end
+ rescue Errno::ESOCKTNOSUPPORT
+ end
+ end
+ end
+
+ def af_test(host)
+ commands = {
+ :ping => lambda { |*_| "+pong" },
+ }
+
+ redis_mock(commands, :host => host) do |redis|
+ assert_nothing_raised do
+ redis.ping
+ end
+ end
+ end
+
+ driver(:ruby) do
+ af_family_supported(Socket::AF_INET) do
+ def test_connect_ipv4
+ af_test("127.0.0.1")
+ end
+ end
+ end
+
+ driver(:ruby) do
+ af_family_supported(Socket::AF_INET6) do
+ def test_connect_ipv6
+ af_test("::1")
+ end
+ end
+ end
+
+ def test_can_be_duped_to_create_a_new_connection
+ clients = r.info["connected_clients"].to_i
+
+ r2 = r.dup
+ r2.ping
+
+ assert_equal clients + 1, r.info["connected_clients"].to_i
+ end
+end
diff --git a/lib/vendor/redis/test/lint/blocking_commands.rb b/lib/vendor/redis/test/lint/blocking_commands.rb
new file mode 100644
index 0000000..531e8d9
--- /dev/null
+++ b/lib/vendor/redis/test/lint/blocking_commands.rb
@@ -0,0 +1,150 @@
+module Lint
+
+ module BlockingCommands
+
+ def setup
+ super
+
+ r.rpush("{zap}foo", "s1")
+ r.rpush("{zap}foo", "s2")
+ r.rpush("{zap}bar", "s1")
+ r.rpush("{zap}bar", "s2")
+ end
+
+ def to_protocol(obj)
+ case obj
+ when String
+ "$#{obj.length}\r\n#{obj}\r\n"
+ when Array
+ "*#{obj.length}\r\n" + obj.map { |e| to_protocol(e) }.join
+ else
+ fail
+ end
+ end
+
+ def mock(options = {}, &blk)
+ commands = {
+ :blpop => lambda do |*args|
+ sleep options[:delay] if options.has_key?(:delay)
+ to_protocol([args.first, args.last])
+ end,
+ :brpop => lambda do |*args|
+ sleep options[:delay] if options.has_key?(:delay)
+ to_protocol([args.first, args.last])
+ end,
+ :brpoplpush => lambda do |*args|
+ sleep options[:delay] if options.has_key?(:delay)
+ to_protocol(args.last)
+ end
+ }
+
+ redis_mock(commands, &blk)
+ end
+
+ def test_blpop
+ assert_equal ["{zap}foo", "s1"], r.blpop("{zap}foo")
+ assert_equal ["{zap}foo", "s2"], r.blpop(["{zap}foo"])
+ assert_equal ["{zap}bar", "s1"], r.blpop(["{zap}bar", "{zap}foo"])
+ assert_equal ["{zap}bar", "s2"], r.blpop(["{zap}foo", "{zap}bar"])
+ end
+
+ def test_blpop_timeout
+ mock do |r|
+ assert_equal ["{zap}foo", "0"], r.blpop("{zap}foo")
+ assert_equal ["{zap}foo", "1"], r.blpop("{zap}foo", :timeout => 1)
+ end
+ end
+
+ def test_blpop_with_old_prototype
+ assert_equal ["{zap}foo", "s1"], r.blpop("{zap}foo", 0)
+ assert_equal ["{zap}foo", "s2"], r.blpop("{zap}foo", 0)
+ assert_equal ["{zap}bar", "s1"], r.blpop("{zap}bar", "{zap}foo", 0)
+ assert_equal ["{zap}bar", "s2"], r.blpop("{zap}foo", "{zap}bar", 0)
+ end
+
+ def test_blpop_timeout_with_old_prototype
+ mock do |r|
+ assert_equal ["{zap}foo", "0"], r.blpop("{zap}foo", 0)
+ assert_equal ["{zap}foo", "1"], r.blpop("{zap}foo", 1)
+ end
+ end
+
+ def test_brpop
+ assert_equal ["{zap}foo", "s2"], r.brpop("{zap}foo")
+ assert_equal ["{zap}foo", "s1"], r.brpop(["{zap}foo"])
+ assert_equal ["{zap}bar", "s2"], r.brpop(["{zap}bar", "{zap}foo"])
+ assert_equal ["{zap}bar", "s1"], r.brpop(["{zap}foo", "{zap}bar"])
+ end
+
+ def test_brpop_timeout
+ mock do |r|
+ assert_equal ["{zap}foo", "0"], r.brpop("{zap}foo")
+ assert_equal ["{zap}foo", "1"], r.brpop("{zap}foo", :timeout => 1)
+ end
+ end
+
+ def test_brpop_with_old_prototype
+ assert_equal ["{zap}foo", "s2"], r.brpop("{zap}foo", 0)
+ assert_equal ["{zap}foo", "s1"], r.brpop("{zap}foo", 0)
+ assert_equal ["{zap}bar", "s2"], r.brpop("{zap}bar", "{zap}foo", 0)
+ assert_equal ["{zap}bar", "s1"], r.brpop("{zap}foo", "{zap}bar", 0)
+ end
+
+ def test_brpop_timeout_with_old_prototype
+ mock do |r|
+ assert_equal ["{zap}foo", "0"], r.brpop("{zap}foo", 0)
+ assert_equal ["{zap}foo", "1"], r.brpop("{zap}foo", 1)
+ end
+ end
+
+ def test_brpoplpush
+ assert_equal "s2", r.brpoplpush("{zap}foo", "{zap}qux")
+ assert_equal ["s2"], r.lrange("{zap}qux", 0, -1)
+ end
+
+ def test_brpoplpush_timeout
+ mock do |r|
+ assert_equal "0", r.brpoplpush("{zap}foo", "{zap}bar")
+ assert_equal "1", r.brpoplpush("{zap}foo", "{zap}bar", :timeout => 1)
+ end
+ end
+
+ def test_brpoplpush_with_old_prototype
+ assert_equal "s2", r.brpoplpush("{zap}foo", "{zap}qux", 0)
+ assert_equal ["s2"], r.lrange("{zap}qux", 0, -1)
+ end
+
+ def test_brpoplpush_timeout_with_old_prototype
+ mock do |r|
+ assert_equal "0", r.brpoplpush("{zap}foo", "{zap}bar", 0)
+ assert_equal "1", r.brpoplpush("{zap}foo", "{zap}bar", 1)
+ end
+ end
+
+ driver(:ruby, :hiredis) do
+ def test_blpop_socket_timeout
+ mock(:delay => 1 + OPTIONS[:timeout] * 2) do |r|
+ assert_raises(Redis::TimeoutError) do
+ r.blpop("{zap}foo", :timeout => 1)
+ end
+ end
+ end
+
+ def test_brpop_socket_timeout
+ mock(:delay => 1 + OPTIONS[:timeout] * 2) do |r|
+ assert_raises(Redis::TimeoutError) do
+ r.brpop("{zap}foo", :timeout => 1)
+ end
+ end
+ end
+
+ def test_brpoplpush_socket_timeout
+ mock(:delay => 1 + OPTIONS[:timeout] * 2) do |r|
+ assert_raises(Redis::TimeoutError) do
+ r.brpoplpush("{zap}foo", "{zap}bar", :timeout => 1)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/lint/hashes.rb b/lib/vendor/redis/test/lint/hashes.rb
new file mode 100644
index 0000000..649e667
--- /dev/null
+++ b/lib/vendor/redis/test/lint/hashes.rb
@@ -0,0 +1,162 @@
+module Lint
+
+ module Hashes
+
+ def test_hset_and_hget
+ r.hset("foo", "f1", "s1")
+
+ assert_equal "s1", r.hget("foo", "f1")
+ end
+
+ def test_hsetnx
+ r.hset("foo", "f1", "s1")
+ r.hsetnx("foo", "f1", "s2")
+
+ assert_equal "s1", r.hget("foo", "f1")
+
+ r.del("foo")
+ r.hsetnx("foo", "f1", "s2")
+
+ assert_equal "s2", r.hget("foo", "f1")
+ end
+
+ def test_hdel
+ r.hset("foo", "f1", "s1")
+
+ assert_equal "s1", r.hget("foo", "f1")
+
+ assert_equal 1, r.hdel("foo", "f1")
+
+ assert_equal nil, r.hget("foo", "f1")
+ end
+
+ def test_variadic_hdel
+ target_version "2.3.9" do
+ r.hset("foo", "f1", "s1")
+ r.hset("foo", "f2", "s2")
+
+ assert_equal "s1", r.hget("foo", "f1")
+ assert_equal "s2", r.hget("foo", "f2")
+
+ assert_equal 2, r.hdel("foo", ["f1", "f2"])
+
+ assert_equal nil, r.hget("foo", "f1")
+ assert_equal nil, r.hget("foo", "f2")
+ end
+ end
+
+ def test_hexists
+ assert_equal false, r.hexists("foo", "f1")
+
+ r.hset("foo", "f1", "s1")
+
+ assert r.hexists("foo", "f1")
+ end
+
+ def test_hlen
+ assert_equal 0, r.hlen("foo")
+
+ r.hset("foo", "f1", "s1")
+
+ assert_equal 1, r.hlen("foo")
+
+ r.hset("foo", "f2", "s2")
+
+ assert_equal 2, r.hlen("foo")
+ end
+
+ def test_hkeys
+ assert_equal [], r.hkeys("foo")
+
+ r.hset("foo", "f1", "s1")
+ r.hset("foo", "f2", "s2")
+
+ assert_equal ["f1", "f2"], r.hkeys("foo")
+ end
+
+ def test_hvals
+ assert_equal [], r.hvals("foo")
+
+ r.hset("foo", "f1", "s1")
+ r.hset("foo", "f2", "s2")
+
+ assert_equal ["s1", "s2"], r.hvals("foo")
+ end
+
+ def test_hgetall
+ assert({} == r.hgetall("foo"))
+
+ r.hset("foo", "f1", "s1")
+ r.hset("foo", "f2", "s2")
+
+ assert({"f1" => "s1", "f2" => "s2"} == r.hgetall("foo"))
+ end
+
+ def test_hmset
+ r.hmset("hash", "foo1", "bar1", "foo2", "bar2")
+
+ assert_equal "bar1", r.hget("hash", "foo1")
+ assert_equal "bar2", r.hget("hash", "foo2")
+ end
+
+ def test_hmset_with_invalid_arguments
+ assert_raise(Redis::CommandError) do
+ r.hmset("hash", "foo1", "bar1", "foo2", "bar2", "foo3")
+ end
+ end
+
+ def test_mapped_hmset
+ r.mapped_hmset("foo", :f1 => "s1", :f2 => "s2")
+
+ assert_equal "s1", r.hget("foo", "f1")
+ assert_equal "s2", r.hget("foo", "f2")
+ end
+
+ def test_hmget
+ r.hset("foo", "f1", "s1")
+ r.hset("foo", "f2", "s2")
+ r.hset("foo", "f3", "s3")
+
+ assert_equal ["s2", "s3"], r.hmget("foo", "f2", "f3")
+ end
+
+ def test_hmget_mapped
+ r.hset("foo", "f1", "s1")
+ r.hset("foo", "f2", "s2")
+ r.hset("foo", "f3", "s3")
+
+ assert({"f1" => "s1"} == r.mapped_hmget("foo", "f1"))
+ assert({"f1" => "s1", "f2" => "s2"} == r.mapped_hmget("foo", "f1", "f2"))
+ end
+
+ def test_hincrby
+ r.hincrby("foo", "f1", 1)
+
+ assert_equal "1", r.hget("foo", "f1")
+
+ r.hincrby("foo", "f1", 2)
+
+ assert_equal "3", r.hget("foo", "f1")
+
+ r.hincrby("foo", "f1", -1)
+
+ assert_equal "2", r.hget("foo", "f1")
+ end
+
+ def test_hincrbyfloat
+ target_version "2.5.4" do
+ r.hincrbyfloat("foo", "f1", 1.23)
+
+ assert_equal "1.23", r.hget("foo", "f1")
+
+ r.hincrbyfloat("foo", "f1", 0.77)
+
+ assert_equal "2", r.hget("foo", "f1")
+
+ r.hincrbyfloat("foo", "f1", -0.1)
+
+ assert_equal "1.9", r.hget("foo", "f1")
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/lint/hyper_log_log.rb b/lib/vendor/redis/test/lint/hyper_log_log.rb
new file mode 100644
index 0000000..5472e22
--- /dev/null
+++ b/lib/vendor/redis/test/lint/hyper_log_log.rb
@@ -0,0 +1,60 @@
+module Lint
+
+ module HyperLogLog
+
+ def test_pfadd
+ target_version "2.8.9" do
+ assert_equal true, r.pfadd("foo", "s1")
+ assert_equal true, r.pfadd("foo", "s2")
+ assert_equal false, r.pfadd("foo", "s1")
+
+ assert_equal 2, r.pfcount("foo")
+ end
+ end
+
+ def test_variadic_pfadd
+ target_version "2.8.9" do
+ assert_equal true, r.pfadd("foo", ["s1", "s2"])
+ assert_equal true, r.pfadd("foo", ["s1", "s2", "s3"])
+
+ assert_equal 3, r.pfcount("foo")
+ end
+ end
+
+ def test_pfcount
+ target_version "2.8.9" do
+ assert_equal 0, r.pfcount("foo")
+
+ assert_equal true, r.pfadd("foo", "s1")
+
+ assert_equal 1, r.pfcount("foo")
+ end
+ end
+
+ def test_variadic_pfcount
+ target_version "2.8.9" do
+ assert_equal 0, r.pfcount(["{1}foo", "{1}bar"])
+
+ assert_equal true, r.pfadd("{1}foo", "s1")
+ assert_equal true, r.pfadd("{1}bar", "s1")
+ assert_equal true, r.pfadd("{1}bar", "s2")
+
+ assert_equal 2, r.pfcount("{1}foo", "{1}bar")
+ end
+ end
+
+ def test_variadic_pfcount_expanded
+ target_version "2.8.9" do
+ assert_equal 0, r.pfcount("{1}foo", "{1}bar")
+
+ assert_equal true, r.pfadd("{1}foo", "s1")
+ assert_equal true, r.pfadd("{1}bar", "s1")
+ assert_equal true, r.pfadd("{1}bar", "s2")
+
+ assert_equal 2, r.pfcount("{1}foo", "{1}bar")
+ end
+ end
+
+ end
+
+end
diff --git a/lib/vendor/redis/test/lint/lists.rb b/lib/vendor/redis/test/lint/lists.rb
new file mode 100644
index 0000000..3a230f6
--- /dev/null
+++ b/lib/vendor/redis/test/lint/lists.rb
@@ -0,0 +1,143 @@
+module Lint
+
+ module Lists
+
+ def test_lpush
+ r.lpush "foo", "s1"
+ r.lpush "foo", "s2"
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.lpop("foo")
+ end
+
+ def test_variadic_lpush
+ target_version "2.3.9" do # 2.4-rc6
+ assert_equal 3, r.lpush("foo", ["s1", "s2", "s3"])
+ assert_equal 3, r.llen("foo")
+ assert_equal "s3", r.lpop("foo")
+ end
+ end
+
+ def test_lpushx
+ r.lpushx "foo", "s1"
+ r.lpush "foo", "s2"
+ r.lpushx "foo", "s3"
+
+ assert_equal 2, r.llen("foo")
+ assert_equal ["s3", "s2"], r.lrange("foo", 0, -1)
+ end
+
+ def test_rpush
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.rpop("foo")
+ end
+
+ def test_variadic_rpush
+ target_version "2.3.9" do # 2.4-rc6
+ assert_equal 3, r.rpush("foo", ["s1", "s2", "s3"])
+ assert_equal 3, r.llen("foo")
+ assert_equal "s3", r.rpop("foo")
+ end
+ end
+
+ def test_rpushx
+ r.rpushx "foo", "s1"
+ r.rpush "foo", "s2"
+ r.rpushx "foo", "s3"
+
+ assert_equal 2, r.llen("foo")
+ assert_equal ["s2", "s3"], r.lrange("foo", 0, -1)
+ end
+
+ def test_llen
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal 2, r.llen("foo")
+ end
+
+ def test_lrange
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+ r.rpush "foo", "s3"
+
+ assert_equal ["s2", "s3"], r.lrange("foo", 1, -1)
+ assert_equal ["s1", "s2"], r.lrange("foo", 0, 1)
+
+ assert_equal [], r.lrange("bar", 0, -1)
+ end
+
+ def test_ltrim
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+ r.rpush "foo", "s3"
+
+ r.ltrim "foo", 0, 1
+
+ assert_equal 2, r.llen("foo")
+ assert_equal ["s1", "s2"], r.lrange("foo", 0, -1)
+ end
+
+ def test_lindex
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal "s1", r.lindex("foo", 0)
+ assert_equal "s2", r.lindex("foo", 1)
+ end
+
+ def test_lset
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal "s2", r.lindex("foo", 1)
+ assert r.lset("foo", 1, "s3")
+ assert_equal "s3", r.lindex("foo", 1)
+
+ assert_raise Redis::CommandError do
+ r.lset("foo", 4, "s3")
+ end
+ end
+
+ def test_lrem
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal 1, r.lrem("foo", 1, "s1")
+ assert_equal ["s2"], r.lrange("foo", 0, -1)
+ end
+
+ def test_lpop
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s1", r.lpop("foo")
+ assert_equal 1, r.llen("foo")
+ end
+
+ def test_rpop
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.rpop("foo")
+ assert_equal 1, r.llen("foo")
+ end
+
+ def test_linsert
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s3"
+ r.linsert "foo", :before, "s3", "s2"
+
+ assert_equal ["s1", "s2", "s3"], r.lrange("foo", 0, -1)
+
+ assert_raise(Redis::CommandError) do
+ r.linsert "foo", :anywhere, "s3", "s2"
+ end
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/lint/sets.rb b/lib/vendor/redis/test/lint/sets.rb
new file mode 100644
index 0000000..7342f13
--- /dev/null
+++ b/lib/vendor/redis/test/lint/sets.rb
@@ -0,0 +1,125 @@
+module Lint
+
+ module Sets
+
+ def test_sadd
+ assert_equal true, r.sadd("foo", "s1")
+ assert_equal true, r.sadd("foo", "s2")
+ assert_equal false, r.sadd("foo", "s1")
+
+ assert_equal ["s1", "s2"], r.smembers("foo").sort
+ end
+
+ def test_variadic_sadd
+ target_version "2.3.9" do # 2.4-rc6
+ assert_equal 2, r.sadd("foo", ["s1", "s2"])
+ assert_equal 1, r.sadd("foo", ["s1", "s2", "s3"])
+
+ assert_equal ["s1", "s2", "s3"], r.smembers("foo").sort
+ end
+ end
+
+ def test_srem
+ r.sadd("foo", "s1")
+ r.sadd("foo", "s2")
+
+ assert_equal true, r.srem("foo", "s1")
+ assert_equal false, r.srem("foo", "s3")
+
+ assert_equal ["s2"], r.smembers("foo")
+ end
+
+ def test_variadic_srem
+ target_version "2.3.9" do # 2.4-rc6
+ r.sadd("foo", "s1")
+ r.sadd("foo", "s2")
+ r.sadd("foo", "s3")
+
+ assert_equal 1, r.srem("foo", ["s1", "aaa"])
+ assert_equal 0, r.srem("foo", ["bbb", "ccc" "ddd"])
+ assert_equal 1, r.srem("foo", ["eee", "s3"])
+
+ assert_equal ["s2"], r.smembers("foo")
+ end
+ end
+
+ def test_spop
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+
+ assert ["s1", "s2"].include?(r.spop("foo"))
+ assert ["s1", "s2"].include?(r.spop("foo"))
+ assert_equal nil, r.spop("foo")
+ end
+
+ def test_scard
+ assert_equal 0, r.scard("foo")
+
+ r.sadd "foo", "s1"
+
+ assert_equal 1, r.scard("foo")
+
+ r.sadd "foo", "s2"
+
+ assert_equal 2, r.scard("foo")
+ end
+
+ def test_sismember
+ assert_equal false, r.sismember("foo", "s1")
+
+ r.sadd "foo", "s1"
+
+ assert_equal true, r.sismember("foo", "s1")
+ assert_equal false, r.sismember("foo", "s2")
+ end
+
+ def test_smembers
+ assert_equal [], r.smembers("foo")
+
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+
+ assert_equal ["s1", "s2"], r.smembers("foo").sort
+ end
+
+ def test_srandmember
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+
+ 4.times do
+ assert ["s1", "s2"].include?(r.srandmember("foo"))
+ end
+
+ assert_equal 2, r.scard("foo")
+ end
+
+ def test_srandmember_with_positive_count
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "foo", "s3"
+ r.sadd "foo", "s4"
+
+ 4.times do
+ assert !(["s1", "s2", "s3", "s4"] & r.srandmember("foo", 3)).empty?
+
+ assert_equal 3, r.srandmember("foo", 3).size
+ end
+
+ assert_equal 4, r.scard("foo")
+ end
+
+ def test_srandmember_with_negative_count
+ r.sadd "foo", "s1"
+ r.sadd "foo", "s2"
+ r.sadd "foo", "s3"
+ r.sadd "foo", "s4"
+
+ 4.times do
+ assert !(["s1", "s2", "s3", "s4"] & r.srandmember("foo", -6)).empty?
+ assert_equal 6, r.srandmember("foo", -6).size
+ end
+
+ assert_equal 4, r.scard("foo")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/lint/sorted_sets.rb b/lib/vendor/redis/test/lint/sorted_sets.rb
new file mode 100644
index 0000000..9dc11e2
--- /dev/null
+++ b/lib/vendor/redis/test/lint/sorted_sets.rb
@@ -0,0 +1,316 @@
+module Lint
+
+ module SortedSets
+
+ Infinity = 1.0/0.0
+
+ def test_zadd
+ assert_equal 0, r.zcard("foo")
+ assert_equal true, r.zadd("foo", 1, "s1")
+ assert_equal false, r.zadd("foo", 1, "s1")
+ assert_equal 1, r.zcard("foo")
+ r.del "foo"
+
+ target_version "3.0.2" do
+ # XX option
+ assert_equal 0, r.zcard("foo")
+ assert_equal false, r.zadd("foo", 1, "s1", :xx => true)
+ r.zadd("foo", 1, "s1")
+ assert_equal false, r.zadd("foo", 2, "s1", :xx => true)
+ assert_equal 2, r.zscore("foo", "s1")
+ r.del "foo"
+
+ # NX option
+ assert_equal 0, r.zcard("foo")
+ assert_equal true, r.zadd("foo", 1, "s1", :nx => true)
+ assert_equal false, r.zadd("foo", 2, "s1", :nx => true)
+ assert_equal 1, r.zscore("foo", "s1")
+ assert_equal 1, r.zcard("foo")
+ r.del "foo"
+
+ # CH option
+ assert_equal 0, r.zcard("foo")
+ assert_equal true, r.zadd("foo", 1, "s1", :ch => true)
+ assert_equal false, r.zadd("foo", 1, "s1", :ch => true)
+ assert_equal true, r.zadd("foo", 2, "s1", :ch => true)
+ assert_equal 1, r.zcard("foo")
+ r.del "foo"
+
+ # INCR option
+ assert_equal 1.0, r.zadd("foo", 1, "s1", :incr => true)
+ assert_equal 11.0, r.zadd("foo", 10, "s1", :incr => true)
+ assert_equal(-Infinity, r.zadd("bar", "-inf", "s1", :incr => true))
+ assert_equal(+Infinity, r.zadd("bar", "+inf", "s2", :incr => true))
+ r.del "foo", "bar"
+
+ # Incompatible options combination
+ assert_raise(Redis::CommandError) { r.zadd("foo", 1, "s1", :xx => true, :nx => true) }
+ end
+ end
+
+ def test_variadic_zadd
+ target_version "2.3.9" do # 2.4-rc6
+ # Non-nested array with pairs
+ assert_equal 0, r.zcard("foo")
+ assert_equal 2, r.zadd("foo", [1, "s1", 2, "s2"])
+ assert_equal 1, r.zadd("foo", [4, "s1", 5, "s2", 6, "s3"])
+ assert_equal 3, r.zcard("foo")
+ r.del "foo"
+
+ # Nested array with pairs
+ assert_equal 0, r.zcard("foo")
+ assert_equal 2, r.zadd("foo", [[1, "s1"], [2, "s2"]])
+ assert_equal 1, r.zadd("foo", [[4, "s1"], [5, "s2"], [6, "s3"]])
+ assert_equal 3, r.zcard("foo")
+ r.del "foo"
+
+ # Wrong number of arguments
+ assert_raise(Redis::CommandError) { r.zadd("foo", ["bar"]) }
+ assert_raise(Redis::CommandError) { r.zadd("foo", ["bar", "qux", "zap"]) }
+ end
+
+ target_version "3.0.2" do
+ # XX option
+ assert_equal 0, r.zcard("foo")
+ assert_equal 0, r.zadd("foo", [1, "s1", 2, "s2"], :xx => true)
+ r.zadd("foo", [1, "s1", 2, "s2"])
+ assert_equal 0, r.zadd("foo", [2, "s1", 3, "s2", 4, "s3"], :xx => true)
+ assert_equal 2, r.zscore("foo", "s1")
+ assert_equal 3, r.zscore("foo", "s2")
+ assert_equal nil, r.zscore("foo", "s3")
+ assert_equal 2, r.zcard("foo")
+ r.del "foo"
+
+ # NX option
+ assert_equal 0, r.zcard("foo")
+ assert_equal 2, r.zadd("foo", [1, "s1", 2, "s2"], :nx => true)
+ assert_equal 1, r.zadd("foo", [2, "s1", 3, "s2", 4, "s3"], :nx => true)
+ assert_equal 1, r.zscore("foo", "s1")
+ assert_equal 2, r.zscore("foo", "s2")
+ assert_equal 4, r.zscore("foo", "s3")
+ assert_equal 3, r.zcard("foo")
+ r.del "foo"
+
+ # CH option
+ assert_equal 0, r.zcard("foo")
+ assert_equal 2, r.zadd("foo", [1, "s1", 2, "s2"], :ch => true)
+ assert_equal 2, r.zadd("foo", [1, "s1", 3, "s2", 4, "s3"], :ch => true)
+ assert_equal 3, r.zcard("foo")
+ r.del "foo"
+
+ # INCR option
+ assert_equal 1.0, r.zadd("foo", [1, "s1"], :incr => true)
+ assert_equal 11.0, r.zadd("foo", [10, "s1"], :incr => true)
+ assert_equal(-Infinity, r.zadd("bar", ["-inf", "s1"], :incr => true))
+ assert_equal(+Infinity, r.zadd("bar", ["+inf", "s2"], :incr => true))
+ assert_raise(Redis::CommandError) { r.zadd("foo", [1, "s1", 2, "s2"], :incr => true) }
+ r.del "foo", "bar"
+
+ # Incompatible options combination
+ assert_raise(Redis::CommandError) { r.zadd("foo", [1, "s1"], :xx => true, :nx => true) }
+ end
+ end
+
+ def test_zrem
+ r.zadd("foo", 1, "s1")
+ r.zadd("foo", 2, "s2")
+
+ assert_equal 2, r.zcard("foo")
+ assert_equal true, r.zrem("foo", "s1")
+ assert_equal false, r.zrem("foo", "s1")
+ assert_equal 1, r.zcard("foo")
+ end
+
+ def test_variadic_zrem
+ target_version "2.3.9" do # 2.4-rc6
+ r.zadd("foo", 1, "s1")
+ r.zadd("foo", 2, "s2")
+ r.zadd("foo", 3, "s3")
+
+ assert_equal 3, r.zcard("foo")
+ assert_equal 1, r.zrem("foo", ["s1", "aaa"])
+ assert_equal 0, r.zrem("foo", ["bbb", "ccc" "ddd"])
+ assert_equal 1, r.zrem("foo", ["eee", "s3"])
+ assert_equal 1, r.zcard("foo")
+ end
+ end
+
+ def test_zincrby
+ rv = r.zincrby "foo", 1, "s1"
+ assert_equal 1.0, rv
+
+ rv = r.zincrby "foo", 10, "s1"
+ assert_equal 11.0, rv
+
+ rv = r.zincrby "bar", "-inf", "s1"
+ assert_equal(-Infinity, rv)
+
+ rv = r.zincrby "bar", "+inf", "s2"
+ assert_equal(+Infinity, rv)
+ end
+
+ def test_zrank
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal 2, r.zrank("foo", "s3")
+ end
+
+ def test_zrevrank
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal 0, r.zrevrank("foo", "s3")
+ end
+
+ def test_zrange
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal ["s1", "s2"], r.zrange("foo", 0, 1)
+ assert_equal [["s1", 1.0], ["s2", 2.0]], r.zrange("foo", 0, 1, :with_scores => true)
+ assert_equal [["s1", 1.0], ["s2", 2.0]], r.zrange("foo", 0, 1, :withscores => true)
+
+ r.zadd "bar", "-inf", "s1"
+ r.zadd "bar", "+inf", "s2"
+ assert_equal [["s1", -Infinity], ["s2", +Infinity]], r.zrange("bar", 0, 1, :with_scores => true)
+ assert_equal [["s1", -Infinity], ["s2", +Infinity]], r.zrange("bar", 0, 1, :withscores => true)
+ end
+
+ def test_zrevrange
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal ["s3", "s2"], r.zrevrange("foo", 0, 1)
+ assert_equal [["s3", 3.0], ["s2", 2.0]], r.zrevrange("foo", 0, 1, :with_scores => true)
+ assert_equal [["s3", 3.0], ["s2", 2.0]], r.zrevrange("foo", 0, 1, :withscores => true)
+
+ r.zadd "bar", "-inf", "s1"
+ r.zadd "bar", "+inf", "s2"
+ assert_equal [["s2", +Infinity], ["s1", -Infinity]], r.zrevrange("bar", 0, 1, :with_scores => true)
+ assert_equal [["s2", +Infinity], ["s1", -Infinity]], r.zrevrange("bar", 0, 1, :withscores => true)
+ end
+
+ def test_zrangebyscore
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal ["s2", "s3"], r.zrangebyscore("foo", 2, 3)
+ end
+
+ def test_zrevrangebyscore
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+
+ assert_equal ["s3", "s2"], r.zrevrangebyscore("foo", 3, 2)
+ end
+
+ def test_zrangebyscore_with_limit
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "foo", 4, "s4"
+
+ assert_equal ["s2"], r.zrangebyscore("foo", 2, 4, :limit => [0, 1])
+ assert_equal ["s3"], r.zrangebyscore("foo", 2, 4, :limit => [1, 1])
+ assert_equal ["s3", "s4"], r.zrangebyscore("foo", 2, 4, :limit => [1, 2])
+ end
+
+ def test_zrevrangebyscore_with_limit
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "foo", 4, "s4"
+
+ assert_equal ["s4"], r.zrevrangebyscore("foo", 4, 2, :limit => [0, 1])
+ assert_equal ["s3"], r.zrevrangebyscore("foo", 4, 2, :limit => [1, 1])
+ assert_equal ["s3", "s2"], r.zrevrangebyscore("foo", 4, 2, :limit => [1, 2])
+ end
+
+ def test_zrangebyscore_with_withscores
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "foo", 4, "s4"
+
+ assert_equal [["s2", 2.0]], r.zrangebyscore("foo", 2, 4, :limit => [0, 1], :with_scores => true)
+ assert_equal [["s3", 3.0]], r.zrangebyscore("foo", 2, 4, :limit => [1, 1], :with_scores => true)
+ assert_equal [["s2", 2.0]], r.zrangebyscore("foo", 2, 4, :limit => [0, 1], :withscores => true)
+ assert_equal [["s3", 3.0]], r.zrangebyscore("foo", 2, 4, :limit => [1, 1], :withscores => true)
+
+ r.zadd "bar", "-inf", "s1"
+ r.zadd "bar", "+inf", "s2"
+ assert_equal [["s1", -Infinity]], r.zrangebyscore("bar", -Infinity, +Infinity, :limit => [0, 1], :with_scores => true)
+ assert_equal [["s2", +Infinity]], r.zrangebyscore("bar", -Infinity, +Infinity, :limit => [1, 1], :with_scores => true)
+ assert_equal [["s1", -Infinity]], r.zrangebyscore("bar", -Infinity, +Infinity, :limit => [0, 1], :withscores => true)
+ assert_equal [["s2", +Infinity]], r.zrangebyscore("bar", -Infinity, +Infinity, :limit => [1, 1], :withscores => true)
+ end
+
+ def test_zrevrangebyscore_with_withscores
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "foo", 4, "s4"
+
+ assert_equal [["s4", 4.0]], r.zrevrangebyscore("foo", 4, 2, :limit => [0, 1], :with_scores => true)
+ assert_equal [["s3", 3.0]], r.zrevrangebyscore("foo", 4, 2, :limit => [1, 1], :with_scores => true)
+ assert_equal [["s4", 4.0]], r.zrevrangebyscore("foo", 4, 2, :limit => [0, 1], :withscores => true)
+ assert_equal [["s3", 3.0]], r.zrevrangebyscore("foo", 4, 2, :limit => [1, 1], :withscores => true)
+
+ r.zadd "bar", "-inf", "s1"
+ r.zadd "bar", "+inf", "s2"
+ assert_equal [["s2", +Infinity]], r.zrevrangebyscore("bar", +Infinity, -Infinity, :limit => [0, 1], :with_scores => true)
+ assert_equal [["s1", -Infinity]], r.zrevrangebyscore("bar", +Infinity, -Infinity, :limit => [1, 1], :with_scores => true)
+ assert_equal [["s2", +Infinity]], r.zrevrangebyscore("bar", +Infinity, -Infinity, :limit => [0, 1], :withscores => true)
+ assert_equal [["s1", -Infinity]], r.zrevrangebyscore("bar", +Infinity, -Infinity, :limit => [1, 1], :withscores => true)
+ end
+
+ def test_zcard
+ assert_equal 0, r.zcard("foo")
+
+ r.zadd "foo", 1, "s1"
+
+ assert_equal 1, r.zcard("foo")
+ end
+
+ def test_zscore
+ r.zadd "foo", 1, "s1"
+
+ assert_equal 1.0, r.zscore("foo", "s1")
+
+ assert_equal nil, r.zscore("foo", "s2")
+ assert_equal nil, r.zscore("bar", "s1")
+
+ r.zadd "bar", "-inf", "s1"
+ r.zadd "bar", "+inf", "s2"
+ assert_equal(-Infinity, r.zscore("bar", "s1"))
+ assert_equal(+Infinity, r.zscore("bar", "s2"))
+ end
+
+ def test_zremrangebyrank
+ r.zadd "foo", 10, "s1"
+ r.zadd "foo", 20, "s2"
+ r.zadd "foo", 30, "s3"
+ r.zadd "foo", 40, "s4"
+
+ assert_equal 3, r.zremrangebyrank("foo", 1, 3)
+ assert_equal ["s1"], r.zrange("foo", 0, -1)
+ end
+
+ def test_zremrangebyscore
+ r.zadd "foo", 1, "s1"
+ r.zadd "foo", 2, "s2"
+ r.zadd "foo", 3, "s3"
+ r.zadd "foo", 4, "s4"
+
+ assert_equal 3, r.zremrangebyscore("foo", 2, 4)
+ assert_equal ["s1"], r.zrange("foo", 0, -1)
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/lint/strings.rb b/lib/vendor/redis/test/lint/strings.rb
new file mode 100644
index 0000000..381df3c
--- /dev/null
+++ b/lib/vendor/redis/test/lint/strings.rb
@@ -0,0 +1,260 @@
+module Lint
+
+ module Strings
+
+ def test_set_and_get
+ r.set("foo", "s1")
+
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_set_and_get_with_brackets
+ r["foo"] = "s1"
+
+ assert_equal "s1", r["foo"]
+ end
+
+ def test_set_and_get_with_brackets_and_symbol
+ r[:foo] = "s1"
+
+ assert_equal "s1", r[:foo]
+ end
+
+ def test_set_and_get_with_newline_characters
+ r.set("foo", "1\n")
+
+ assert_equal "1\n", r.get("foo")
+ end
+
+ def test_set_and_get_with_non_string_value
+ value = ["a", "b"]
+
+ r.set("foo", value)
+
+ assert_equal value.to_s, r.get("foo")
+ end
+
+ def test_set_and_get_with_ascii_characters
+ if defined?(Encoding)
+ with_external_encoding("ASCII-8BIT") do
+ (0..255).each do |i|
+ str = "#{i.chr}---#{i.chr}"
+ r.set("foo", str)
+
+ assert_equal str, r.get("foo")
+ end
+ end
+ end
+ end
+
+ def test_set_with_ex
+ target_version "2.6.12" do
+ r.set("foo", "bar", :ex => 2)
+ assert_in_range 0..2, r.ttl("foo")
+ end
+ end
+
+ def test_set_with_px
+ target_version "2.6.12" do
+ r.set("foo", "bar", :px => 2000)
+ assert_in_range 0..2, r.ttl("foo")
+ end
+ end
+
+ def test_set_with_nx
+ target_version "2.6.12" do
+ r.set("foo", "qux", :nx => true)
+ assert !r.set("foo", "bar", :nx => true)
+ assert_equal "qux", r.get("foo")
+
+ r.del("foo")
+ assert r.set("foo", "bar", :nx => true)
+ assert_equal "bar", r.get("foo")
+ end
+ end
+
+ def test_set_with_xx
+ target_version "2.6.12" do
+ r.set("foo", "qux")
+ assert r.set("foo", "bar", :xx => true)
+ assert_equal "bar", r.get("foo")
+
+ r.del("foo")
+ assert !r.set("foo", "bar", :xx => true)
+ end
+ end
+
+ def test_setex
+ assert r.setex("foo", 1, "bar")
+ assert_equal "bar", r.get("foo")
+ assert [0, 1].include? r.ttl("foo")
+ end
+
+ def test_setex_with_non_string_value
+ value = ["b", "a", "r"]
+
+ assert r.setex("foo", 1, value)
+ assert_equal value.to_s, r.get("foo")
+ assert [0, 1].include? r.ttl("foo")
+ end
+
+ def test_psetex
+ target_version "2.5.4" do
+ assert r.psetex("foo", 1000, "bar")
+ assert_equal "bar", r.get("foo")
+ assert [0, 1].include? r.ttl("foo")
+ end
+ end
+
+ def test_psetex_with_non_string_value
+ target_version "2.5.4" do
+ value = ["b", "a", "r"]
+
+ assert r.psetex("foo", 1000, value)
+ assert_equal value.to_s, r.get("foo")
+ assert [0, 1].include? r.ttl("foo")
+ end
+ end
+
+ def test_getset
+ r.set("foo", "bar")
+
+ assert_equal "bar", r.getset("foo", "baz")
+ assert_equal "baz", r.get("foo")
+ end
+
+ def test_getset_with_non_string_value
+ r.set("foo", "zap")
+
+ value = ["b", "a", "r"]
+
+ assert_equal "zap", r.getset("foo", value)
+ assert_equal value.to_s, r.get("foo")
+ end
+
+ def test_setnx
+ r.set("foo", "qux")
+ assert !r.setnx("foo", "bar")
+ assert_equal "qux", r.get("foo")
+
+ r.del("foo")
+ assert r.setnx("foo", "bar")
+ assert_equal "bar", r.get("foo")
+ end
+
+ def test_setnx_with_non_string_value
+ value = ["b", "a", "r"]
+
+ r.set("foo", "qux")
+ assert !r.setnx("foo", value)
+ assert_equal "qux", r.get("foo")
+
+ r.del("foo")
+ assert r.setnx("foo", value)
+ assert_equal value.to_s, r.get("foo")
+ end
+
+ def test_incr
+ assert_equal 1, r.incr("foo")
+ assert_equal 2, r.incr("foo")
+ assert_equal 3, r.incr("foo")
+ end
+
+ def test_incrby
+ assert_equal 1, r.incrby("foo", 1)
+ assert_equal 3, r.incrby("foo", 2)
+ assert_equal 6, r.incrby("foo", 3)
+ end
+
+ def test_incrbyfloat
+ target_version "2.5.4" do
+ assert_equal 1.23, r.incrbyfloat("foo", 1.23)
+ assert_equal 2 , r.incrbyfloat("foo", 0.77)
+ assert_equal 1.9 , r.incrbyfloat("foo", -0.1)
+ end
+ end
+
+ def test_decr
+ r.set("foo", 3)
+
+ assert_equal 2, r.decr("foo")
+ assert_equal 1, r.decr("foo")
+ assert_equal 0, r.decr("foo")
+ end
+
+ def test_decrby
+ r.set("foo", 6)
+
+ assert_equal 3, r.decrby("foo", 3)
+ assert_equal 1, r.decrby("foo", 2)
+ assert_equal 0, r.decrby("foo", 1)
+ end
+
+ def test_append
+ r.set "foo", "s"
+ r.append "foo", "1"
+
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_getbit
+ r.set("foo", "a")
+
+ assert_equal 1, r.getbit("foo", 1)
+ assert_equal 1, r.getbit("foo", 2)
+ assert_equal 0, r.getbit("foo", 3)
+ assert_equal 0, r.getbit("foo", 4)
+ assert_equal 0, r.getbit("foo", 5)
+ assert_equal 0, r.getbit("foo", 6)
+ assert_equal 1, r.getbit("foo", 7)
+ end
+
+ def test_setbit
+ r.set("foo", "a")
+
+ r.setbit("foo", 6, 1)
+
+ assert_equal "c", r.get("foo")
+ end
+
+ def test_bitcount
+ target_version "2.5.10" do
+ r.set("foo", "abcde")
+
+ assert_equal 10, r.bitcount("foo", 1, 3)
+ assert_equal 17, r.bitcount("foo", 0, -1)
+ end
+ end
+
+ def test_getrange
+ r.set("foo", "abcde")
+
+ assert_equal "bcd", r.getrange("foo", 1, 3)
+ assert_equal "abcde", r.getrange("foo", 0, -1)
+ end
+
+ def test_setrange
+ r.set("foo", "abcde")
+
+ r.setrange("foo", 1, "bar")
+
+ assert_equal "abare", r.get("foo")
+ end
+
+ def test_setrange_with_non_string_value
+ r.set("foo", "abcde")
+
+ value = ["b", "a", "r"]
+
+ r.setrange("foo", 2, value)
+
+ assert_equal "ab#{value.to_s}", r.get("foo")
+ end
+
+ def test_strlen
+ r.set "foo", "lorem"
+
+ assert_equal 5, r.strlen("foo")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/lint/value_types.rb b/lib/vendor/redis/test/lint/value_types.rb
new file mode 100644
index 0000000..c4deb23
--- /dev/null
+++ b/lib/vendor/redis/test/lint/value_types.rb
@@ -0,0 +1,122 @@
+module Lint
+
+ module ValueTypes
+
+ def test_exists
+ assert_equal false, r.exists("foo")
+
+ r.set("foo", "s1")
+
+ assert_equal true, r.exists("foo")
+ end
+
+ def test_type
+ assert_equal "none", r.type("foo")
+
+ r.set("foo", "s1")
+
+ assert_equal "string", r.type("foo")
+ end
+
+ def test_keys
+ r.set("f", "s1")
+ r.set("fo", "s2")
+ r.set("foo", "s3")
+
+ assert_equal ["f","fo", "foo"], r.keys("f*").sort
+ end
+
+ def test_expire
+ r.set("foo", "s1")
+ assert r.expire("foo", 2)
+ assert_in_range 0..2, r.ttl("foo")
+ end
+
+ def test_pexpire
+ target_version "2.5.4" do
+ r.set("foo", "s1")
+ assert r.pexpire("foo", 2000)
+ assert_in_range 0..2, r.ttl("foo")
+ end
+ end
+
+ def test_expireat
+ r.set("foo", "s1")
+ assert r.expireat("foo", (Time.now + 2).to_i)
+ assert_in_range 0..2, r.ttl("foo")
+ end
+
+ def test_pexpireat
+ target_version "2.5.4" do
+ r.set("foo", "s1")
+ assert r.pexpireat("foo", (Time.now + 2).to_i * 1_000)
+ assert_in_range 0..2, r.ttl("foo")
+ end
+ end
+
+ def test_persist
+ r.set("foo", "s1")
+ r.expire("foo", 1)
+ r.persist("foo")
+
+ assert(-1 == r.ttl("foo"))
+ end
+
+ def test_ttl
+ r.set("foo", "s1")
+ r.expire("foo", 2)
+ assert_in_range 0..2, r.ttl("foo")
+ end
+
+ def test_pttl
+ target_version "2.5.4" do
+ r.set("foo", "s1")
+ r.expire("foo", 2)
+ assert_in_range 1..2000, r.pttl("foo")
+ end
+ end
+
+ def test_dump_and_restore
+ target_version "2.5.7" do
+ r.set("foo", "a")
+ v = r.dump("foo")
+ r.del("foo")
+
+ assert r.restore("foo", 1000, v)
+ assert_equal "a", r.get("foo")
+ assert [0, 1].include? r.ttl("foo")
+
+ r.rpush("bar", ["b", "c", "d"])
+ w = r.dump("bar")
+ r.del("bar")
+
+ assert r.restore("bar", 1000, w)
+ assert_equal ["b", "c", "d"], r.lrange("bar", 0, -1)
+ assert [0, 1].include? r.ttl("bar")
+ end
+ end
+
+ def test_move
+ r.select 14
+ r.flushdb
+
+ r.set "bar", "s3"
+
+ r.select 15
+
+ r.set "foo", "s1"
+ r.set "bar", "s2"
+
+ assert r.move("foo", 14)
+ assert_equal nil, r.get("foo")
+
+ assert !r.move("bar", 14)
+ assert_equal "s2", r.get("bar")
+
+ r.select 14
+
+ assert_equal "s1", r.get("foo")
+ assert_equal "s3", r.get("bar")
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/persistence_control_commands_test.rb b/lib/vendor/redis/test/persistence_control_commands_test.rb
new file mode 100644
index 0000000..2816571
--- /dev/null
+++ b/lib/vendor/redis/test/persistence_control_commands_test.rb
@@ -0,0 +1,26 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestPersistenceControlCommands < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_save
+ redis_mock(:save => lambda { "+SAVE" }) do |redis|
+ assert_equal "SAVE", redis.save
+ end
+ end
+
+ def test_bgsave
+ redis_mock(:bgsave => lambda { "+BGSAVE" }) do |redis|
+ assert_equal "BGSAVE", redis.bgsave
+ end
+ end
+
+ def test_lastsave
+ redis_mock(:lastsave => lambda { "+LASTSAVE" }) do |redis|
+ assert_equal "LASTSAVE", redis.lastsave
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/pipelining_commands_test.rb b/lib/vendor/redis/test/pipelining_commands_test.rb
new file mode 100644
index 0000000..82cd92f
--- /dev/null
+++ b/lib/vendor/redis/test/pipelining_commands_test.rb
@@ -0,0 +1,242 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestPipeliningCommands < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_bulk_commands
+ r.pipelined do
+ r.lpush "foo", "s1"
+ r.lpush "foo", "s2"
+ end
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.lpop("foo")
+ assert_equal "s1", r.lpop("foo")
+ end
+
+ def test_multi_bulk_commands
+ r.pipelined do
+ r.mset("foo", "s1", "bar", "s2")
+ r.mset("baz", "s3", "qux", "s4")
+ end
+
+ assert_equal "s1", r.get("foo")
+ assert_equal "s2", r.get("bar")
+ assert_equal "s3", r.get("baz")
+ assert_equal "s4", r.get("qux")
+ end
+
+ def test_bulk_and_multi_bulk_commands_mixed
+ r.pipelined do
+ r.lpush "foo", "s1"
+ r.lpush "foo", "s2"
+ r.mset("baz", "s3", "qux", "s4")
+ end
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.lpop("foo")
+ assert_equal "s1", r.lpop("foo")
+ assert_equal "s3", r.get("baz")
+ assert_equal "s4", r.get("qux")
+ end
+
+ def test_multi_bulk_and_bulk_commands_mixed
+ r.pipelined do
+ r.mset("baz", "s3", "qux", "s4")
+ r.lpush "foo", "s1"
+ r.lpush "foo", "s2"
+ end
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.lpop("foo")
+ assert_equal "s1", r.lpop("foo")
+ assert_equal "s3", r.get("baz")
+ assert_equal "s4", r.get("qux")
+ end
+
+ def test_pipelined_with_an_empty_block
+ assert_nothing_raised do
+ r.pipelined do
+ end
+ end
+
+ assert_equal 0, r.dbsize
+ end
+
+ def test_returning_the_result_of_a_pipeline
+ result = r.pipelined do
+ r.set "foo", "bar"
+ r.get "foo"
+ r.get "bar"
+ end
+
+ assert_equal ["OK", "bar", nil], result
+ end
+
+ def test_assignment_of_results_inside_the_block
+ r.pipelined do
+ @first = r.sadd("foo", 1)
+ @second = r.sadd("foo", 1)
+ end
+
+ assert_equal true, @first.value
+ assert_equal false, @second.value
+ end
+
+ # Although we could support accessing the values in these futures,
+ # it doesn't make a lot of sense.
+ def test_assignment_of_results_inside_the_block_with_errors
+ assert_raise(Redis::CommandError) do
+ r.pipelined do
+ r.doesnt_exist
+ @first = r.sadd("foo", 1)
+ @second = r.sadd("foo", 1)
+ end
+ end
+
+ assert_raise(Redis::FutureNotReady) { @first.value }
+ assert_raise(Redis::FutureNotReady) { @second.value }
+ end
+
+ def test_assignment_of_results_inside_a_nested_block
+ r.pipelined do
+ @first = r.sadd("foo", 1)
+
+ r.pipelined do
+ @second = r.sadd("foo", 1)
+ end
+ end
+
+ assert_equal true, @first.value
+ assert_equal false, @second.value
+ end
+
+ def test_futures_raise_when_confused_with_something_else
+ r.pipelined do
+ @result = r.sadd("foo", 1)
+ end
+
+ assert_raise(NoMethodError) { @result.to_s }
+ end
+
+ def test_futures_raise_when_trying_to_access_their_values_too_early
+ r.pipelined do
+ assert_raise(Redis::FutureNotReady) do
+ r.sadd("foo", 1).value
+ end
+ end
+ end
+
+ def test_futures_can_be_identified
+ r.pipelined do
+ @result = r.sadd("foo", 1)
+ end
+
+ assert_equal true, @result.is_a?(Redis::Future)
+ if defined?(::BasicObject)
+ assert_equal true, @result.is_a?(::BasicObject)
+ end
+ assert_equal Redis::Future, @result.class
+ end
+
+ def test_returning_the_result_of_an_empty_pipeline
+ result = r.pipelined do
+ end
+
+ assert_equal [], result
+ end
+
+ def test_nesting_pipeline_blocks
+ r.pipelined do
+ r.set("foo", "s1")
+ r.pipelined do
+ r.set("bar", "s2")
+ end
+ end
+
+ assert_equal "s1", r.get("foo")
+ assert_equal "s2", r.get("bar")
+ end
+
+ def test_info_in_a_pipeline_returns_hash
+ result = r.pipelined do
+ r.info
+ end
+
+ assert result.first.kind_of?(Hash)
+ end
+
+ def test_config_get_in_a_pipeline_returns_hash
+ result = r.pipelined do
+ r.config(:get, "*")
+ end
+
+ assert result.first.kind_of?(Hash)
+ end
+
+ def test_hgetall_in_a_pipeline_returns_hash
+ r.hmset("hash", "field", "value")
+ result = r.pipelined do
+ r.hgetall("hash")
+ end
+
+ assert_equal result.first, { "field" => "value" }
+ end
+
+ def test_keys_in_a_pipeline
+ r.set("key", "value")
+ result = r.pipelined do
+ r.keys("*")
+ end
+
+ assert_equal ["key"], result.first
+ end
+
+ def test_pipeline_yields_a_connection
+ r.pipelined do |p|
+ p.set("foo", "bar")
+ end
+
+ assert_equal "bar", r.get("foo")
+ end
+
+ def test_pipeline_select
+ r.select 1
+ r.set("db", "1")
+
+ r.pipelined do |p|
+ p.select 2
+ p.set("db", "2")
+ end
+
+ r.select 1
+ assert_equal "1", r.get("db")
+
+ r.select 2
+ assert_equal "2", r.get("db")
+ end
+
+ def test_pipeline_select_client_db
+ r.select 1
+ r.pipelined do |p2|
+ p2.select 2
+ end
+
+ assert_equal 2, r.client.db
+ end
+
+ def test_nested_pipeline_select_client_db
+ r.select 1
+ r.pipelined do |p2|
+ p2.select 2
+ p2.pipelined do |p3|
+ p3.select 3
+ end
+ end
+
+ assert_equal 3, r.client.db
+ end
+end
diff --git a/lib/vendor/redis/test/publish_subscribe_test.rb b/lib/vendor/redis/test/publish_subscribe_test.rb
new file mode 100644
index 0000000..e607e62
--- /dev/null
+++ b/lib/vendor/redis/test/publish_subscribe_test.rb
@@ -0,0 +1,282 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestPublishSubscribe < Test::Unit::TestCase
+
+ include Helper::Client
+
+ class TestError < StandardError
+ end
+
+ def test_subscribe_and_unsubscribe
+ @subscribed = false
+ @unsubscribed = false
+
+ wire = Wire.new do
+ r.subscribe("foo") do |on|
+ on.subscribe do |channel, total|
+ @subscribed = true
+ @t1 = total
+ end
+
+ on.message do |channel, message|
+ if message == "s1"
+ r.unsubscribe
+ @message = message
+ end
+ end
+
+ on.unsubscribe do |channel, total|
+ @unsubscribed = true
+ @t2 = total
+ end
+ end
+ end
+
+ # Wait until the subscription is active before publishing
+ Wire.pass while !@subscribed
+
+ Redis.new(OPTIONS).publish("foo", "s1")
+
+ wire.join
+
+ assert @subscribed
+ assert_equal 1, @t1
+ assert @unsubscribed
+ assert_equal 0, @t2
+ assert_equal "s1", @message
+ end
+
+ def test_psubscribe_and_punsubscribe
+ @subscribed = false
+ @unsubscribed = false
+
+ wire = Wire.new do
+ r.psubscribe("f*") do |on|
+ on.psubscribe do |pattern, total|
+ @subscribed = true
+ @t1 = total
+ end
+
+ on.pmessage do |pattern, channel, message|
+ if message == "s1"
+ r.punsubscribe
+ @message = message
+ end
+ end
+
+ on.punsubscribe do |pattern, total|
+ @unsubscribed = true
+ @t2 = total
+ end
+ end
+ end
+
+ # Wait until the subscription is active before publishing
+ Wire.pass while !@subscribed
+
+ Redis.new(OPTIONS).publish("foo", "s1")
+
+ wire.join
+
+ assert @subscribed
+ assert_equal 1, @t1
+ assert @unsubscribed
+ assert_equal 0, @t2
+ assert_equal "s1", @message
+ end
+
+ def test_pubsub_with_numpat_subcommand
+ target_version("2.8.0") do
+ @subscribed = false
+ wire = Wire.new do
+ r.psubscribe("f*") do |on|
+ on.psubscribe { |channel, total| @subscribed = true }
+ on.pmessage { |pattern, channel, message| r.punsubscribe }
+ end
+ end
+ Wire.pass while !@subscribed
+ redis = Redis.new(OPTIONS)
+ numpat_result = redis.pubsub(:numpat)
+
+ redis.publish("foo", "s1")
+ wire.join
+
+ assert_equal redis.pubsub(:numpat), 0
+ assert_equal numpat_result, 1
+ end
+ end
+
+
+ def test_pubsub_with_channels_and_numsub_subcommnads
+ target_version("2.8.0") do
+ @subscribed = false
+ wire = Wire.new do
+ r.subscribe("foo") do |on|
+ on.subscribe { |channel, total| @subscribed = true }
+ on.message { |channel, message| r.unsubscribe }
+ end
+ end
+ Wire.pass while !@subscribed
+ redis = Redis.new(OPTIONS)
+ channels_result = redis.pubsub(:channels)
+ numsub_result = redis.pubsub(:numsub, 'foo', 'boo')
+
+ redis.publish("foo", "s1")
+ wire.join
+
+ assert_equal channels_result, ['foo']
+ assert_equal numsub_result, ['foo', 1, 'boo', 0]
+ end
+ end
+
+ def test_subscribe_connection_usable_after_raise
+ @subscribed = false
+
+ wire = Wire.new do
+ begin
+ r.subscribe("foo") do |on|
+ on.subscribe do |channel, total|
+ @subscribed = true
+ end
+
+ on.message do |channel, message|
+ raise TestError
+ end
+ end
+ rescue TestError
+ end
+ end
+
+ # Wait until the subscription is active before publishing
+ Wire.pass while !@subscribed
+
+ Redis.new(OPTIONS).publish("foo", "s1")
+
+ wire.join
+
+ assert_equal "PONG", r.ping
+ end
+
+ def test_psubscribe_connection_usable_after_raise
+ @subscribed = false
+
+ wire = Wire.new do
+ begin
+ r.psubscribe("f*") do |on|
+ on.psubscribe do |pattern, total|
+ @subscribed = true
+ end
+
+ on.pmessage do |pattern, channel, message|
+ raise TestError
+ end
+ end
+ rescue TestError
+ end
+ end
+
+ # Wait until the subscription is active before publishing
+ Wire.pass while !@subscribed
+
+ Redis.new(OPTIONS).publish("foo", "s1")
+
+ wire.join
+
+ assert_equal "PONG", r.ping
+ end
+
+ def test_subscribe_within_subscribe
+ @channels = []
+
+ wire = Wire.new do
+ r.subscribe("foo") do |on|
+ on.subscribe do |channel, total|
+ @channels << channel
+
+ r.subscribe("bar") if channel == "foo"
+ r.unsubscribe if channel == "bar"
+ end
+ end
+ end
+
+ wire.join
+
+ assert_equal ["foo", "bar"], @channels
+ end
+
+ def test_other_commands_within_a_subscribe
+ assert_raise Redis::CommandError do
+ r.subscribe("foo") do |on|
+ on.subscribe do |channel, total|
+ r.set("bar", "s2")
+ end
+ end
+ end
+ end
+
+ def test_subscribe_without_a_block
+ assert_raise LocalJumpError do
+ r.subscribe("foo")
+ end
+ end
+
+ def test_unsubscribe_without_a_subscribe
+ assert_raise RuntimeError do
+ r.unsubscribe
+ end
+
+ assert_raise RuntimeError do
+ r.punsubscribe
+ end
+ end
+
+ def test_subscribe_past_a_timeout
+ # For some reason, a thread here doesn't reproduce the issue.
+ sleep = %{sleep #{OPTIONS[:timeout] * 2}}
+ publish = %{ruby -rsocket -e 't=TCPSocket.new("127.0.0.1",#{OPTIONS[:port]});t.write("publish foo bar\\r\\n");t.read(4);t.close'}
+ cmd = [sleep, publish].join("; ")
+
+ IO.popen(cmd, "r+") do |pipe|
+ received = false
+
+ r.subscribe "foo" do |on|
+ on.message do |channel, message|
+ received = true
+ r.unsubscribe
+ end
+ end
+
+ assert received
+ end
+ end
+
+ def test_subscribe_with_timeout
+ received = false
+
+ assert_raise Redis::TimeoutError do
+ r.subscribe_with_timeout(1, "foo") do |on|
+ on.message do |channel, message|
+ received = true
+ end
+ end
+ end
+
+ assert !received
+ end
+
+ def test_psubscribe_with_timeout
+ received = false
+
+ assert_raise Redis::TimeoutError do
+ r.psubscribe_with_timeout(1, "f*") do |on|
+ on.message do |channel, message|
+ received = true
+ end
+ end
+ end
+
+ assert !received
+ end
+end
diff --git a/lib/vendor/redis/test/remote_server_control_commands_test.rb b/lib/vendor/redis/test/remote_server_control_commands_test.rb
new file mode 100644
index 0000000..b5cbd45
--- /dev/null
+++ b/lib/vendor/redis/test/remote_server_control_commands_test.rb
@@ -0,0 +1,118 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestRemoteServerControlCommands < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_info
+ keys = [
+ "redis_version",
+ "uptime_in_seconds",
+ "uptime_in_days",
+ "connected_clients",
+ "used_memory",
+ "total_connections_received",
+ "total_commands_processed",
+ ]
+
+ info = r.info
+
+ keys.each do |k|
+ msg = "expected #info to include #{k}"
+ assert info.keys.include?(k), msg
+ end
+ end
+
+ def test_info_commandstats
+ target_version "2.5.7" do
+ r.config(:resetstat)
+ r.ping
+
+ result = r.info(:commandstats)
+ assert_equal "1", result["ping"]["calls"]
+ end
+ end
+
+ def test_monitor_redis_lt_2_5_0
+ return unless version < "2.5.0"
+
+ log = []
+
+ wire = Wire.new do
+ Redis.new(OPTIONS).monitor do |line|
+ log << line
+ break if log.size == 3
+ end
+ end
+
+ Wire.pass while log.empty? # Faster than sleep
+
+ r.set "foo", "s1"
+
+ wire.join
+
+ assert log[-1][%q{(db 15) "set" "foo" "s1"}]
+ end
+
+ def test_monitor_redis_gte_2_5_0
+ return unless version >= "2.5.0"
+
+ log = []
+
+ wire = Wire.new do
+ Redis.new(OPTIONS).monitor do |line|
+ log << line
+ break if line =~ /set/
+ end
+ end
+
+ Wire.pass while log.empty? # Faster than sleep
+
+ r.set "foo", "s1"
+
+ wire.join
+
+ assert log[-1] =~ /\b15\b.* "set" "foo" "s1"/
+ end
+
+ def test_monitor_returns_value_for_break
+ result = r.monitor do |line|
+ break line
+ end
+
+ assert_equal "OK", result
+ end
+
+ def test_echo
+ assert_equal "foo bar baz\n", r.echo("foo bar baz\n")
+ end
+
+ def test_debug
+ r.set "foo", "s1"
+
+ assert r.debug(:object, "foo").kind_of?(String)
+ end
+
+ def test_object
+ r.lpush "list", "value"
+
+ assert_equal 1, r.object(:refcount, "list")
+ encoding = r.object(:encoding, "list")
+ assert "ziplist" == encoding || "quicklist" == encoding, "Wrong encoding for list"
+ assert r.object(:idletime, "list").kind_of?(Fixnum)
+ end
+
+ def test_sync
+ redis_mock(:sync => lambda { "+OK" }) do |redis|
+ assert_equal "OK", redis.sync
+ end
+ end
+
+ def test_slowlog
+ r.slowlog(:reset)
+ result = r.slowlog(:len)
+ assert_equal 0, result
+ end
+end
diff --git a/lib/vendor/redis/test/scanning_test.rb b/lib/vendor/redis/test/scanning_test.rb
new file mode 100644
index 0000000..9a4cf7d
--- /dev/null
+++ b/lib/vendor/redis/test/scanning_test.rb
@@ -0,0 +1,413 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+unless defined?(Enumerator)
+ Enumerator = Enumerable::Enumerator
+end
+
+class TestScanning < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_scan_basic
+ target_version "2.7.105" do
+ r.debug :populate, 1000
+
+ cursor = 0
+ all_keys = []
+ loop {
+ cursor, keys = r.scan cursor
+ all_keys += keys
+ break if cursor == "0"
+ }
+
+ assert_equal 1000, all_keys.uniq.size
+ end
+ end
+
+ def test_scan_count
+ target_version "2.7.105" do
+ r.debug :populate, 1000
+
+ cursor = 0
+ all_keys = []
+ loop {
+ cursor, keys = r.scan cursor, :count => 5
+ all_keys += keys
+ break if cursor == "0"
+ }
+
+ assert_equal 1000, all_keys.uniq.size
+ end
+ end
+
+ def test_scan_match
+ target_version "2.7.105" do
+ r.debug :populate, 1000
+
+ cursor = 0
+ all_keys = []
+ loop {
+ cursor, keys = r.scan cursor, :match => "key:1??"
+ all_keys += keys
+ break if cursor == "0"
+ }
+
+ assert_equal 100, all_keys.uniq.size
+ end
+ end
+
+ def test_scan_each_enumerator
+ target_version "2.7.105" do
+
+ r.debug :populate, 1000
+
+ scan_enumerator = r.scan_each
+ assert_equal true, scan_enumerator.is_a?(::Enumerator)
+
+ keys_from_scan = scan_enumerator.to_a.uniq
+ all_keys = r.keys "*"
+
+ assert all_keys.sort == keys_from_scan.sort
+ end
+ end
+
+ def test_scan_each_enumerator_match
+ target_version "2.7.105" do
+
+ r.debug :populate, 1000
+
+ keys_from_scan = r.scan_each(:match => "key:1??").to_a.uniq
+ all_keys = r.keys "key:1??"
+
+ assert all_keys.sort == keys_from_scan.sort
+ end
+ end
+
+ def test_scan_each_block
+ target_version "2.7.105" do
+
+ r.debug :populate, 100
+
+ keys_from_scan = []
+ r.scan_each {|key|
+ keys_from_scan << key
+ }
+
+ all_keys = r.keys "*"
+
+ assert all_keys.sort == keys_from_scan.uniq.sort
+ end
+ end
+
+ def test_scan_each_block_match
+ target_version "2.7.105" do
+
+ r.debug :populate, 100
+
+ keys_from_scan = []
+ r.scan_each(:match => "key:1?") {|key|
+ keys_from_scan << key
+ }
+
+ all_keys = r.keys "key:1?"
+
+ assert all_keys.sort == keys_from_scan.uniq.sort
+ end
+ end
+
+ def test_sscan_with_encoding
+ target_version "2.7.105" do
+ [:intset, :hashtable].each do |enc|
+ r.del "set"
+
+ prefix = ""
+ prefix = "ele:" if enc == :hashtable
+
+ elements = []
+ 100.times { |j| elements << "#{prefix}#{j}" }
+
+ r.sadd "set", elements
+
+ assert_equal enc.to_s, r.object("encoding", "set")
+
+ cursor = 0
+ all_keys = []
+ loop {
+ cursor, keys = r.sscan "set", cursor
+ all_keys += keys
+ break if cursor == "0"
+ }
+
+ assert_equal 100, all_keys.uniq.size
+ end
+ end
+ end
+
+ def test_sscan_each_enumerator
+ target_version "2.7.105" do
+ elements = []
+ 100.times { |j| elements << "ele:#{j}" }
+ r.sadd "set", elements
+
+ scan_enumerator = r.sscan_each("set")
+ assert_equal true, scan_enumerator.is_a?(::Enumerator)
+
+ keys_from_scan = scan_enumerator.to_a.uniq
+ all_keys = r.smembers("set")
+
+ assert all_keys.sort == keys_from_scan.sort
+ end
+ end
+
+ def test_sscan_each_enumerator_match
+ target_version "2.7.105" do
+ elements = []
+ 100.times { |j| elements << "ele:#{j}" }
+ r.sadd "set", elements
+
+ keys_from_scan = r.sscan_each("set", :match => "ele:1?").to_a.uniq
+
+ all_keys = r.smembers("set").grep(/^ele:1.$/)
+
+ assert all_keys.sort == keys_from_scan.sort
+ end
+ end
+
+ def test_sscan_each_enumerator_block
+ target_version "2.7.105" do
+ elements = []
+ 100.times { |j| elements << "ele:#{j}" }
+ r.sadd "set", elements
+
+ keys_from_scan = []
+ r.sscan_each("set") do |key|
+ keys_from_scan << key
+ end
+
+ all_keys = r.smembers("set")
+
+ assert all_keys.sort == keys_from_scan.uniq.sort
+ end
+ end
+
+ def test_sscan_each_enumerator_block_match
+ target_version "2.7.105" do
+ elements = []
+ 100.times { |j| elements << "ele:#{j}" }
+ r.sadd "set", elements
+
+ keys_from_scan = []
+ r.sscan_each("set", :match => "ele:1?") do |key|
+ keys_from_scan << key
+ end
+
+ all_keys = r.smembers("set").grep(/^ele:1.$/)
+
+ assert all_keys.sort == keys_from_scan.uniq.sort
+ end
+ end
+
+ def test_hscan_with_encoding
+ target_version "2.7.105" do
+ [:ziplist, :hashtable].each do |enc|
+ r.del "set"
+
+ count = 1000
+ count = 30 if enc == :ziplist
+
+ elements = []
+ count.times { |j| elements << "key:#{j}" << j.to_s }
+
+ r.hmset "hash", *elements
+
+ assert_equal enc.to_s, r.object("encoding", "hash")
+
+ cursor = 0
+ all_key_values = []
+ loop {
+ cursor, key_values = r.hscan "hash", cursor
+ all_key_values.concat key_values
+ break if cursor == "0"
+ }
+
+ keys2 = []
+ all_key_values.each do |k, v|
+ assert_equal "key:#{v}", k
+ keys2 << k
+ end
+
+ assert_equal count, keys2.uniq.size
+ end
+ end
+ end
+
+ def test_hscan_each_enumerator
+ target_version "2.7.105" do
+ count = 1000
+ elements = []
+ count.times { |j| elements << "key:#{j}" << j.to_s }
+ r.hmset "hash", *elements
+
+ scan_enumerator = r.hscan_each("hash")
+ assert_equal true, scan_enumerator.is_a?(::Enumerator)
+
+ keys_from_scan = scan_enumerator.to_a.uniq
+ all_keys = r.hgetall("hash").to_a
+
+ assert all_keys.sort == keys_from_scan.sort
+ end
+ end
+
+ def test_hscan_each_enumerator_match
+ target_version "2.7.105" do
+ count = 100
+ elements = []
+ count.times { |j| elements << "key:#{j}" << j.to_s }
+ r.hmset "hash", *elements
+
+ keys_from_scan = r.hscan_each("hash", :match => "key:1?").to_a.uniq
+ all_keys = r.hgetall("hash").to_a.select{|k,v| k =~ /^key:1.$/}
+
+ assert all_keys.sort == keys_from_scan.sort
+ end
+ end
+
+ def test_hscan_each_block
+ target_version "2.7.105" do
+ count = 1000
+ elements = []
+ count.times { |j| elements << "key:#{j}" << j.to_s }
+ r.hmset "hash", *elements
+
+ keys_from_scan = []
+ r.hscan_each("hash") do |field, value|
+ keys_from_scan << [field, value]
+ end
+ all_keys = r.hgetall("hash").to_a
+
+ assert all_keys.sort == keys_from_scan.uniq.sort
+ end
+ end
+
+ def test_hscan_each_block_match
+ target_version "2.7.105" do
+ count = 1000
+ elements = []
+ count.times { |j| elements << "key:#{j}" << j.to_s }
+ r.hmset "hash", *elements
+
+ keys_from_scan = []
+ r.hscan_each("hash", :match => "key:1?") do |field, value|
+ keys_from_scan << [field, value]
+ end
+ all_keys = r.hgetall("hash").to_a.select{|k,v| k =~ /^key:1.$/}
+
+ assert all_keys.sort == keys_from_scan.uniq.sort
+ end
+ end
+
+ def test_zscan_with_encoding
+ target_version "2.7.105" do
+ [:ziplist, :skiplist].each do |enc|
+ r.del "zset"
+
+ count = 1000
+ count = 30 if enc == :ziplist
+
+ elements = []
+ count.times { |j| elements << j << "key:#{j}" }
+
+ r.zadd "zset", elements
+
+ assert_equal enc.to_s, r.object("encoding", "zset")
+
+ cursor = 0
+ all_key_scores = []
+ loop {
+ cursor, key_scores = r.zscan "zset", cursor
+ all_key_scores.concat key_scores
+ break if cursor == "0"
+ }
+
+ keys2 = []
+ all_key_scores.each do |k, v|
+ assert_equal true, v.is_a?(Float)
+ assert_equal "key:#{Integer(v)}", k
+ keys2 << k
+ end
+
+ assert_equal count, keys2.uniq.size
+ end
+ end
+ end
+
+ def test_zscan_each_enumerator
+ target_version "2.7.105" do
+ count = 1000
+ elements = []
+ count.times { |j| elements << j << "key:#{j}" }
+ r.zadd "zset", elements
+
+ scan_enumerator = r.zscan_each "zset"
+ assert_equal true, scan_enumerator.is_a?(::Enumerator)
+
+ scores_from_scan = scan_enumerator.to_a.uniq
+ member_scores = r.zrange("zset", 0, -1, :with_scores => true)
+
+ assert member_scores.sort == scores_from_scan.sort
+ end
+ end
+
+ def test_zscan_each_enumerator_match
+ target_version "2.7.105" do
+ count = 1000
+ elements = []
+ count.times { |j| elements << j << "key:#{j}" }
+ r.zadd "zset", elements
+
+ scores_from_scan = r.zscan_each("zset", :match => "key:1??").to_a.uniq
+ member_scores = r.zrange("zset", 0, -1, :with_scores => true)
+ filtered_members = member_scores.select{|k,s| k =~ /^key:1..$/}
+
+ assert filtered_members.sort == scores_from_scan.sort
+ end
+ end
+
+ def test_zscan_each_block
+ target_version "2.7.105" do
+ count = 1000
+ elements = []
+ count.times { |j| elements << j << "key:#{j}" }
+ r.zadd "zset", elements
+
+ scores_from_scan = []
+ r.zscan_each("zset") do |member, score|
+ scores_from_scan << [member, score]
+ end
+ member_scores = r.zrange("zset", 0, -1, :with_scores => true)
+
+ assert member_scores.sort == scores_from_scan.sort
+ end
+ end
+
+ def test_zscan_each_block_match
+ target_version "2.7.105" do
+ count = 1000
+ elements = []
+ count.times { |j| elements << j << "key:#{j}" }
+ r.zadd "zset", elements
+
+ scores_from_scan = []
+ r.zscan_each("zset", :match => "key:1??") do |member, score|
+ scores_from_scan << [member, score]
+ end
+ member_scores = r.zrange("zset", 0, -1, :with_scores => true)
+ filtered_members = member_scores.select{|k,s| k =~ /^key:1..$/}
+
+ assert filtered_members.sort == scores_from_scan.sort
+ end
+ end
+
+end
diff --git a/lib/vendor/redis/test/scripting_test.rb b/lib/vendor/redis/test/scripting_test.rb
new file mode 100644
index 0000000..82d0d89
--- /dev/null
+++ b/lib/vendor/redis/test/scripting_test.rb
@@ -0,0 +1,78 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestScripting < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def to_sha(script)
+ r.script(:load, script)
+ end
+
+ def test_script_exists
+ target_version "2.5.9" do # 2.6-rc1
+ a = to_sha("return 1")
+ b = a.succ
+
+ assert_equal true, r.script(:exists, a)
+ assert_equal false, r.script(:exists, b)
+ assert_equal [true], r.script(:exists, [a])
+ assert_equal [false], r.script(:exists, [b])
+ assert_equal [true, false], r.script(:exists, [a, b])
+ end
+ end
+
+ def test_script_flush
+ target_version "2.5.9" do # 2.6-rc1
+ sha = to_sha("return 1")
+ assert r.script(:exists, sha)
+ assert_equal "OK", r.script(:flush)
+ assert !r.script(:exists, sha)
+ end
+ end
+
+ def test_script_kill
+ target_version "2.5.9" do # 2.6-rc1
+ redis_mock(:script => lambda { |arg| "+#{arg.upcase}" }) do |redis|
+ assert_equal "KILL", redis.script(:kill)
+ end
+ end
+ end
+
+ def test_eval
+ target_version "2.5.9" do # 2.6-rc1
+ assert_equal 0, r.eval("return #KEYS")
+ assert_equal 0, r.eval("return #ARGV")
+ assert_equal ["k1", "k2"], r.eval("return KEYS", ["k1", "k2"])
+ assert_equal ["a1", "a2"], r.eval("return ARGV", [], ["a1", "a2"])
+ end
+ end
+
+ def test_eval_with_options_hash
+ target_version "2.5.9" do # 2.6-rc1
+ assert_equal 0, r.eval("return #KEYS", {})
+ assert_equal 0, r.eval("return #ARGV", {})
+ assert_equal ["k1", "k2"], r.eval("return KEYS", { :keys => ["k1", "k2"] })
+ assert_equal ["a1", "a2"], r.eval("return ARGV", { :argv => ["a1", "a2"] })
+ end
+ end
+
+ def test_evalsha
+ target_version "2.5.9" do # 2.6-rc1
+ assert_equal 0, r.evalsha(to_sha("return #KEYS"))
+ assert_equal 0, r.evalsha(to_sha("return #ARGV"))
+ assert_equal ["k1", "k2"], r.evalsha(to_sha("return KEYS"), ["k1", "k2"])
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), [], ["a1", "a2"])
+ end
+ end
+
+ def test_evalsha_with_options_hash
+ target_version "2.5.9" do # 2.6-rc1
+ assert_equal 0, r.evalsha(to_sha("return #KEYS"), {})
+ assert_equal 0, r.evalsha(to_sha("return #ARGV"), {})
+ assert_equal ["k1", "k2"], r.evalsha(to_sha("return KEYS"), { :keys => ["k1", "k2"] })
+ assert_equal ["a1", "a2"], r.evalsha(to_sha("return ARGV"), { :argv => ["a1", "a2"] })
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/sentinel_command_test.rb b/lib/vendor/redis/test/sentinel_command_test.rb
new file mode 100644
index 0000000..d61fa07
--- /dev/null
+++ b/lib/vendor/redis/test/sentinel_command_test.rb
@@ -0,0 +1,80 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class SentinalCommandsTest < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_sentinel_command_master
+
+ handler = lambda do |id|
+ {
+ :sentinel => lambda do |command, *args|
+ ["name", "master1", "ip", "127.0.0.1"]
+ end
+ }
+ end
+
+ RedisMock.start(handler.call(:s1)) do |port|
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
+
+ result = redis.sentinel('master', 'master1')
+ assert_equal result, { "name" => "master1", "ip" => "127.0.0.1" }
+ end
+ end
+
+ def test_sentinel_command_masters
+
+ handler = lambda do |id|
+ {
+ :sentinel => lambda do |command, *args|
+ [%w[name master1 ip 127.0.0.1 port 6381], %w[name master1 ip 127.0.0.1 port 6382]]
+ end
+ }
+ end
+
+ RedisMock.start(handler.call(:s1)) do |port|
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
+
+ result = redis.sentinel('masters')
+ assert_equal result[0], { "name" => "master1", "ip" => "127.0.0.1", "port" => "6381" }
+ assert_equal result[1], { "name" => "master1", "ip" => "127.0.0.1", "port" => "6382" }
+ end
+ end
+
+ def test_sentinel_command_get_master_by_name
+
+ handler = lambda do |id|
+ {
+ :sentinel => lambda do |command, *args|
+ ["127.0.0.1", "6381"]
+ end
+ }
+ end
+
+ RedisMock.start(handler.call(:s1)) do |port|
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
+
+ result = redis.sentinel('get-master-addr-by-name', 'master1')
+ assert_equal result, ["127.0.0.1", "6381"]
+ end
+ end
+
+ def test_sentinel_command_ckquorum
+ handler = lambda do |id|
+ {
+ :sentinel => lambda do |command, *args|
+ "+OK 2 usable Sentinels. Quorum and failover authorization can be reached"
+ end
+ }
+ end
+
+ RedisMock.start(handler.call(:s1)) do |port|
+ redis = Redis.new(:host => "127.0.0.1", :port => port)
+
+ result = redis.sentinel('ckquorum', 'master1')
+ assert_equal result, "OK 2 usable Sentinels. Quorum and failover authorization can be reached"
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/sentinel_test.rb b/lib/vendor/redis/test/sentinel_test.rb
new file mode 100644
index 0000000..1eff251
--- /dev/null
+++ b/lib/vendor/redis/test/sentinel_test.rb
@@ -0,0 +1,255 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class SentinalTest < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_sentinel_connection
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
+ {:host => "127.0.0.1", :port => 26382}]
+
+ commands = {
+ :s1 => [],
+ :s2 => [],
+ }
+
+ handler = lambda do |id|
+ {
+ :sentinel => lambda do |command, *args|
+ commands[id] << [command, *args]
+ ["127.0.0.1", "6381"]
+ end
+ }
+ end
+
+ RedisMock.start(handler.call(:s1)) do |s1_port|
+ RedisMock.start(handler.call(:s2)) do |s2_port|
+ sentinels[0][:port] = s1_port
+ sentinels[1][:port] = s2_port
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
+
+ assert redis.ping
+ end
+ end
+
+ assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
+ assert_equal commands[:s2], []
+ end
+
+ def test_sentinel_failover
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
+ {:host => "127.0.0.1", :port => 26382}]
+
+ commands = {
+ :s1 => [],
+ :s2 => [],
+ }
+
+ s1 = {
+ :sentinel => lambda do |command, *args|
+ commands[:s1] << [command, *args]
+ "$-1" # Nil
+ end
+ }
+
+ s2 = {
+ :sentinel => lambda do |command, *args|
+ commands[:s2] << [command, *args]
+ ["127.0.0.1", "6381"]
+ end
+ }
+
+ RedisMock.start(s1) do |s1_port|
+ RedisMock.start(s2) do |s2_port|
+ sentinels[0][:port] = s1_port
+ sentinels[1][:port] = s2_port
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
+
+ assert redis.ping
+ end
+ end
+
+ assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
+ assert_equal commands[:s2], [%w[get-master-addr-by-name master1]]
+ end
+
+ def test_sentinel_failover_prioritize_healthy_sentinel
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
+ {:host => "127.0.0.1", :port => 26382}]
+
+ commands = {
+ :s1 => [],
+ :s2 => [],
+ }
+
+ s1 = {
+ :sentinel => lambda do |command, *args|
+ commands[:s1] << [command, *args]
+ "$-1" # Nil
+ end
+ }
+
+ s2 = {
+ :sentinel => lambda do |command, *args|
+ commands[:s2] << [command, *args]
+ ["127.0.0.1", "6381"]
+ end
+ }
+
+ RedisMock.start(s1) do |s1_port|
+ RedisMock.start(s2) do |s2_port|
+ sentinels[0][:port] = s1_port
+ sentinels[1][:port] = s2_port
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
+
+ assert redis.ping
+
+ redis.quit
+
+ assert redis.ping
+ end
+ end
+
+ assert_equal commands[:s1], [%w[get-master-addr-by-name master1]]
+ assert_equal commands[:s2], [%w[get-master-addr-by-name master1], %w[get-master-addr-by-name master1]]
+ end
+
+ def test_sentinel_with_non_sentinel_options
+ sentinels = [{:host => "127.0.0.1", :port => 26381}]
+
+ commands = {
+ :s1 => [],
+ :m1 => []
+ }
+
+ sentinel = lambda do |port|
+ {
+ :auth => lambda do |pass|
+ commands[:s1] << ["auth", pass]
+ "-ERR unknown command 'auth'"
+ end,
+ :select => lambda do |db|
+ commands[:s1] << ["select", db]
+ "-ERR unknown command 'select'"
+ end,
+ :sentinel => lambda do |command, *args|
+ commands[:s1] << [command, *args]
+ ["127.0.0.1", port.to_s]
+ end
+ }
+ end
+
+ master = {
+ :auth => lambda do |pass|
+ commands[:m1] << ["auth", pass]
+ "+OK"
+ end,
+ :role => lambda do
+ commands[:m1] << ["role"]
+ ["master"]
+ end
+ }
+
+ RedisMock.start(master) do |master_port|
+ RedisMock.start(sentinel.call(master_port)) do |sen_port|
+ sentinels[0][:port] = sen_port
+ redis = Redis.new(:url => "redis://:foo@master1/15", :sentinels => sentinels, :role => :master)
+
+ assert redis.ping
+ end
+ end
+
+ assert_equal [%w[get-master-addr-by-name master1]], commands[:s1]
+ assert_equal [%w[auth foo], %w[role]], commands[:m1]
+ end
+
+ def test_sentinel_role_mismatch
+ sentinels = [{:host => "127.0.0.1", :port => 26381}]
+
+ sentinel = lambda do |port|
+ {
+ :sentinel => lambda do |command, *args|
+ ["127.0.0.1", port.to_s]
+ end
+ }
+ end
+
+ master = {
+ :role => lambda do
+ ["slave"]
+ end
+ }
+
+ ex = assert_raise(Redis::ConnectionError) do
+ RedisMock.start(master) do |master_port|
+ RedisMock.start(sentinel.call(master_port)) do |sen_port|
+ sentinels[0][:port] = sen_port
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master)
+
+ assert redis.ping
+ end
+ end
+ end
+
+ assert_match(/Instance role mismatch/, ex.message)
+ end
+
+ def test_sentinel_retries
+ sentinels = [{:host => "127.0.0.1", :port => 26381},
+ {:host => "127.0.0.1", :port => 26382}]
+
+ connections = []
+
+ handler = lambda do |id, port|
+ {
+ :sentinel => lambda do |command, *args|
+ connections << id
+
+ if connections.count(id) < 2
+ :close
+ else
+ ["127.0.0.1", port.to_s]
+ end
+ end
+ }
+ end
+
+ master = {
+ :role => lambda do
+ ["master"]
+ end
+ }
+
+ RedisMock.start(master) do |master_port|
+ RedisMock.start(handler.call(:s1, master_port)) do |s1_port|
+ RedisMock.start(handler.call(:s2, master_port)) do |s2_port|
+ sentinels[0][:port] = s1_port
+ sentinels[1][:port] = s2_port
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master, :reconnect_attempts => 1)
+
+ assert redis.ping
+ end
+ end
+ end
+
+ assert_equal [:s1, :s2, :s1], connections
+
+ connections.clear
+
+ ex = assert_raise(Redis::CannotConnectError) do
+ RedisMock.start(master) do |master_port|
+ RedisMock.start(handler.call(:s1, master_port)) do |s1_port|
+ RedisMock.start(handler.call(:s2, master_port)) do |s2_port|
+ redis = Redis.new(:url => "redis://master1", :sentinels => sentinels, :role => :master, :reconnect_attempts => 0)
+
+ assert redis.ping
+ end
+ end
+ end
+ end
+
+ assert_match(/No sentinels available/, ex.message)
+ end
+end
diff --git a/lib/vendor/redis/test/sorting_test.rb b/lib/vendor/redis/test/sorting_test.rb
new file mode 100644
index 0000000..e8aec56
--- /dev/null
+++ b/lib/vendor/redis/test/sorting_test.rb
@@ -0,0 +1,59 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestSorting < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_sort
+ r.set("foo:1", "s1")
+ r.set("foo:2", "s2")
+
+ r.rpush("bar", "1")
+ r.rpush("bar", "2")
+
+ assert_equal ["s1"], r.sort("bar", :get => "foo:*", :limit => [0, 1])
+ assert_equal ["s2"], r.sort("bar", :get => "foo:*", :limit => [0, 1], :order => "desc alpha")
+ end
+
+ def test_sort_with_an_array_of_gets
+ r.set("foo:1:a", "s1a")
+ r.set("foo:1:b", "s1b")
+
+ r.set("foo:2:a", "s2a")
+ r.set("foo:2:b", "s2b")
+
+ r.rpush("bar", "1")
+ r.rpush("bar", "2")
+
+ assert_equal [["s1a", "s1b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1])
+ assert_equal [["s2a", "s2b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :limit => [0, 1], :order => "desc alpha")
+ assert_equal [["s1a", "s1b"], ["s2a", "s2b"]], r.sort("bar", :get => ["foo:*:a", "foo:*:b"])
+ end
+
+ def test_sort_with_store
+ r.set("foo:1", "s1")
+ r.set("foo:2", "s2")
+
+ r.rpush("bar", "1")
+ r.rpush("bar", "2")
+
+ r.sort("bar", :get => "foo:*", :store => "baz")
+ assert_equal ["s1", "s2"], r.lrange("baz", 0, -1)
+ end
+
+ def test_sort_with_an_array_of_gets_and_with_store
+ r.set("foo:1:a", "s1a")
+ r.set("foo:1:b", "s1b")
+
+ r.set("foo:2:a", "s2a")
+ r.set("foo:2:b", "s2b")
+
+ r.rpush("bar", "1")
+ r.rpush("bar", "2")
+
+ r.sort("bar", :get => ["foo:*:a", "foo:*:b"], :store => 'baz')
+ assert_equal ["s1a", "s1b", "s2a", "s2b"], r.lrange("baz", 0, -1)
+ end
+end
diff --git a/lib/vendor/redis/test/ssl_test.rb b/lib/vendor/redis/test/ssl_test.rb
new file mode 100644
index 0000000..3800dd8
--- /dev/null
+++ b/lib/vendor/redis/test/ssl_test.rb
@@ -0,0 +1,66 @@
+# encoding: UTF-8
+
+if RUBY_VERSION >= "1.9.3"
+ require File.expand_path("helper", File.dirname(__FILE__))
+
+ class SslTest < Test::Unit::TestCase
+
+ include Helper::Client
+
+ driver(:ruby) do
+
+ def test_verified_ssl_connection
+ RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("trusted")) do |port|
+ redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
+ assert_equal redis.ping, "PONG"
+ end
+ end
+
+ def test_unverified_ssl_connection
+ assert_raise(OpenSSL::SSL::SSLError) do
+ RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("untrusted")) do |port|
+ redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
+ redis.ping
+ end
+ end
+ end
+
+ end
+
+ driver(:hiredis, :synchrony) do
+
+ def test_ssl_not_implemented_exception
+ assert_raise(NotImplementedError) do
+ RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("trusted")) do |port|
+ redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
+ redis.ping
+ end
+ end
+ end
+
+ end
+
+ private
+
+ def ssl_server_opts(prefix)
+ ssl_cert = File.join(cert_path, "#{prefix}-cert.crt")
+ ssl_key = File.join(cert_path, "#{prefix}-cert.key")
+
+ {
+ :ssl => true,
+ :ssl_params => {
+ :cert => OpenSSL::X509::Certificate.new(File.read(ssl_cert)),
+ :key => OpenSSL::PKey::RSA.new(File.read(ssl_key))
+ }
+ }
+ end
+
+ def ssl_ca_file
+ File.join(cert_path, "trusted-ca.crt")
+ end
+
+ def cert_path
+ File.expand_path("../support/ssl/", __FILE__)
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/support/connection/hiredis.rb b/lib/vendor/redis/test/support/connection/hiredis.rb
new file mode 100644
index 0000000..f2ccbca
--- /dev/null
+++ b/lib/vendor/redis/test/support/connection/hiredis.rb
@@ -0,0 +1 @@
+require "support/wire/thread"
diff --git a/lib/vendor/redis/test/support/connection/ruby.rb b/lib/vendor/redis/test/support/connection/ruby.rb
new file mode 100644
index 0000000..f2ccbca
--- /dev/null
+++ b/lib/vendor/redis/test/support/connection/ruby.rb
@@ -0,0 +1 @@
+require "support/wire/thread"
diff --git a/lib/vendor/redis/test/support/connection/synchrony.rb b/lib/vendor/redis/test/support/connection/synchrony.rb
new file mode 100644
index 0000000..80acb0a
--- /dev/null
+++ b/lib/vendor/redis/test/support/connection/synchrony.rb
@@ -0,0 +1,17 @@
+require "support/wire/synchrony"
+
+module Helper
+ def around
+ rv = nil
+
+ EM.synchrony do
+ begin
+ rv = yield
+ ensure
+ EM.stop
+ end
+ end
+
+ rv
+ end
+end
diff --git a/lib/vendor/redis/test/support/redis_mock.rb b/lib/vendor/redis/test/support/redis_mock.rb
new file mode 100644
index 0000000..8034df6
--- /dev/null
+++ b/lib/vendor/redis/test/support/redis_mock.rb
@@ -0,0 +1,130 @@
+require "socket"
+
+module RedisMock
+ class Server
+ def initialize(options = {}, &block)
+ tcp_server = TCPServer.new(options[:host] || "127.0.0.1", 0)
+ tcp_server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
+
+ if options[:ssl]
+ ctx = OpenSSL::SSL::SSLContext.new
+
+ ssl_params = options.fetch(:ssl_params, {})
+ ctx.set_params(ssl_params) unless ssl_params.empty?
+
+ @server = OpenSSL::SSL::SSLServer.new(tcp_server, ctx)
+ else
+ @server = tcp_server
+ end
+ end
+
+ def port
+ @server.addr[1]
+ end
+
+ def start(&block)
+ @thread = Thread.new { run(&block) }
+ end
+
+ def shutdown
+ @thread.kill
+ end
+
+ def run
+ begin
+ loop do
+ session = @server.accept
+
+ begin
+ return if yield(session) == :exit
+ ensure
+ session.close
+ end
+ end
+ rescue => ex
+ $stderr.puts "Error running mock server: #{ex.message}"
+ $stderr.puts ex.backtrace
+ retry
+ ensure
+ @server.close
+ end
+ end
+ end
+
+ # Starts a mock Redis server in a thread.
+ #
+ # The server will use the lambda handler passed as argument to handle
+ # connections. For example:
+ #
+ # handler = lambda { |session| session.close }
+ # RedisMock.start_with_handler(handler) do
+ # # Every connection will be closed immediately
+ # end
+ #
+ def self.start_with_handler(blk, options = {})
+ server = Server.new(options)
+ port = server.port
+
+ begin
+ server.start(&blk)
+ yield(port)
+ ensure
+ server.shutdown
+ end
+ end
+
+ # Starts a mock Redis server in a thread.
+ #
+ # The server will reply with a `+OK` to all commands, but you can
+ # customize it by providing a hash. For example:
+ #
+ # RedisMock.start(:ping => lambda { "+PONG" }) do |port|
+ # assert_equal "PONG", Redis.new(:port => port).ping
+ # end
+ #
+ def self.start(commands, options = {}, &blk)
+ handler = lambda do |session|
+ while line = session.gets
+ argv = Array.new(line[1..-3].to_i) do
+ bytes = session.gets[1..-3].to_i
+ arg = session.read(bytes)
+ session.read(2) # Discard \r\n
+ arg
+ end
+
+ command = argv.shift
+ blk = commands[command.to_sym]
+ blk ||= lambda { |*_| "+OK" }
+
+ response = blk.call(*argv)
+
+ # Convert a nil response to :close
+ response ||= :close
+
+ if response == :exit
+ break :exit
+ elsif response == :close
+ break :close
+ elsif response.is_a?(Array)
+ session.write("*%d\r\n" % response.size)
+
+ response.each do |resp|
+ if resp.is_a?(Array)
+ session.write("*%d\r\n" % resp.size)
+ resp.each do |r|
+ session.write("$%d\r\n%s\r\n" % [r.length, r])
+ end
+ else
+ session.write("$%d\r\n%s\r\n" % [resp.length, resp])
+ end
+ end
+ else
+ session.write(response)
+ session.write("\r\n") unless response.end_with?("\r\n")
+ end
+ end
+ end
+
+ start_with_handler(handler, options, &blk)
+ end
+end
diff --git a/lib/vendor/redis/test/support/ssl/gen_certs.sh b/lib/vendor/redis/test/support/ssl/gen_certs.sh
new file mode 100755
index 0000000..074d3e6
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/gen_certs.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+get_subject() {
+ if [ "$1" = "trusted" ]
+ then
+ echo "/C=IT/ST=Sicily/L=Catania/O=Redis/OU=Security/CN=127.0.0.1"
+ else
+ echo "/C=XX/ST=Untrusted/L=Evilville/O=Evil Hacker/OU=Attack Department/CN=127.0.0.1"
+ fi
+}
+
+# Generate two CAs: one to be considered trusted, and one that's untrusted
+for type in trusted untrusted; do
+ rm -rf ./demoCA
+ mkdir -p ./demoCA
+ mkdir -p ./demoCA/certs
+ mkdir -p ./demoCA/crl
+ mkdir -p ./demoCA/newcerts
+ mkdir -p ./demoCA/private
+ touch ./demoCA/index.txt
+
+ openssl genrsa -out ${type}-ca.key 2048
+ openssl req -new -x509 -days 12500 -key ${type}-ca.key -out ${type}-ca.crt -subj "$(get_subject $type)"
+ openssl x509 -in ${type}-ca.crt -noout -next_serial -out ./demoCA/serial
+
+ openssl req -newkey rsa:2048 -keyout ${type}-cert.key -nodes -out ${type}-cert.req -subj "$(get_subject $type)"
+ openssl ca -days 12500 -cert ${type}-ca.crt -keyfile ${type}-ca.key -out ${type}-cert.crt -infiles ${type}-cert.req
+ rm ${type}-cert.req
+done
+
+rm -rf ./demoCA
diff --git a/lib/vendor/redis/test/support/ssl/trusted-ca.crt b/lib/vendor/redis/test/support/ssl/trusted-ca.crt
new file mode 100644
index 0000000..f1e1b97
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/trusted-ca.crt
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEIDCCAwigAwIBAgIJAM7kyjC89Qj/MA0GCSqGSIb3DQEBCwUAMGcxCzAJBgNV
+BAYTAklUMQ8wDQYDVQQIEwZTaWNpbHkxEDAOBgNVBAcTB0NhdGFuaWExDjAMBgNV
+BAoTBVJlZGlzMREwDwYDVQQLEwhTZWN1cml0eTESMBAGA1UEAxMJMTI3LjAuMC4x
+MCAXDTE2MDQwMjAzMzQ0MVoYDzIwNTAwNjIzMDMzNDQxWjBnMQswCQYDVQQGEwJJ
+VDEPMA0GA1UECBMGU2ljaWx5MRAwDgYDVQQHEwdDYXRhbmlhMQ4wDAYDVQQKEwVS
+ZWRpczERMA8GA1UECxMIU2VjdXJpdHkxEjAQBgNVBAMTCTEyNy4wLjAuMTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMeibFqEG38mtN9DSXy6NZdd7AjH
+4/D+VdDzlbJlI5IBACCV9p6P2j5PFlFvkHFE6vr6biMaLXNAmUHYfDzeT95LODHH
+t+8HlR51cNYrnt9B3eiVwEnJ7+axuDHg6nUgLXeKeog+vEqreZwLnFibxt2qpFze
+xzyKJ37Pm+iAey5glCc/v7ECYQ4sWVVV+ciC+sAwmZDfZXCBQtRRokJ6ikqQDwWV
+DugGcV46feTpu79OmkLLM8PI3E7ow2F/3iv67gmdlO5m9wX1ahWzJKUapBTxgf4X
+QG0s60WbC9iJIvgXRGW7wWSsqSVJkfLYllDTPgfpLyl1+FR3A4awrsPiMVUCAwEA
+AaOBzDCByTAdBgNVHQ4EFgQU+YG9kJR3Vy31d7QVyxRAYyKTK18wgZkGA1UdIwSB
+kTCBjoAU+YG9kJR3Vy31d7QVyxRAYyKTK1+ha6RpMGcxCzAJBgNVBAYTAklUMQ8w
+DQYDVQQIEwZTaWNpbHkxEDAOBgNVBAcTB0NhdGFuaWExDjAMBgNVBAoTBVJlZGlz
+MREwDwYDVQQLEwhTZWN1cml0eTESMBAGA1UEAxMJMTI3LjAuMC4xggkAzuTKMLz1
+CP8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAeFKB7DUixmxbdvNw
+n/mNoHK+OOZXmfxZDCo0v2gcQ4WXUiCqL6MagrImCvkEz5RL6Fk2ZflEV2iGQ5Ds
+CmF2n47ISpqG29bfI5R1rcbfqK/5tazUIhQu12ThNmkEh7hCuW/0LqJrnmxpuRLy
+le9e3svCC96lwjFczzU/utWurKt7S7Di3C4P+AXAJJuszDMLMCBLaB/3j24cNpOx
+zzeZo02x4rpsD2+MMfRDWMWezVEyk63KnI0kt3JGnepsKCFc48ZOk09LwFk3Rfaq
+zuKSgEJJw1mfsdBfysM0HQw20yyjSdoTEfQq3bXctTNi+pEOgW6x7TMsnngYYLXV
+9XTrpg==
+-----END CERTIFICATE-----
diff --git a/lib/vendor/redis/test/support/ssl/trusted-ca.key b/lib/vendor/redis/test/support/ssl/trusted-ca.key
new file mode 100644
index 0000000..2c30610
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/trusted-ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEAx6JsWoQbfya030NJfLo1l13sCMfj8P5V0POVsmUjkgEAIJX2
+no/aPk8WUW+QcUTq+vpuIxotc0CZQdh8PN5P3ks4Mce37weVHnVw1iue30Hd6JXA
+Scnv5rG4MeDqdSAtd4p6iD68Sqt5nAucWJvG3aqkXN7HPIonfs+b6IB7LmCUJz+/
+sQJhDixZVVX5yIL6wDCZkN9lcIFC1FGiQnqKSpAPBZUO6AZxXjp95Om7v06aQssz
+w8jcTujDYX/eK/ruCZ2U7mb3BfVqFbMkpRqkFPGB/hdAbSzrRZsL2Iki+BdEZbvB
+ZKypJUmR8tiWUNM+B+kvKXX4VHcDhrCuw+IxVQIDAQABAoIBAQCzbGHiQJXOA+XQ
+O9OSjHGaJ8n6Yl2VvaE3eZXzjj8X/Fo271GGVVgbZE10x8aUZxKim+3dEqwCx+52
+ZbHTqyMxcX2CEDRaWwBFLdxKQU467iIZ5m26ZAp/1v7rpXBT8KWsqQNT7L6ihdd4
+zl6orOlhVPsAlSGQYcL5kHJZ1w/fL0phEbwdISd3PYhGHXMNmqfXorzJYHDQA4R+
+yR7WpP1dmnUeEKrHc9FFcBZ75BGlWjdCPZMFKc7IndZumarhBpWH9yZMUxrUIo4V
+SCweRUFdD5H1lMZ0YiIAE25wKNEQ2iGd3Jfr8Vj1KFSHC9I2FJA3aFRRUgTwxx/W
+h0mJy1ZJAoGBAPYsSSlwQdxZjnyZiVkNSD4MoLdof//nRxeKGejq6AiXDvcsLyJy
+0MKk4YBFw2249TWm/KBbMAFiBE7d8uPtP5pPfjNVPX6VltH3AhSZ7Ugbpo6C3NFA
+GpzFVtNaWgCVDloDVdmsY7ssDFuAIih0paklPAqnLY+Ua9m1BiEPrB+bAoGBAM+a
+i+0NMR4AyKpuo1exdd+7BIHw5HNPwGmR1ggdGWduH0zsOhEawQKKFv1X9xKAcXxW
+PyeD56/Tmn7fkWvuE8dOu9E6em0vgmxhYyn4nyLAFYF5uKXYo78MpIEThdpl1ldT
+iHwG/25vunaBUHhwbHPUD+F989tmRuCjoFkuA5nPAoGAaqPIlcDhZvkMtoE0dHVC
+hE6oGIuWV17y9wmGK9YG6iG2A/EKAhxGvur6HL0b6Z4j6zgJW9Xkt9SkFR4kqAQQ
+d2JUQxx75SgcC5y7M/1yQrhnsHiT+7mPTbZW5HvRXUs0yl2DhSYeleiA+epJ4ciW
+Mu3EUsEVBYvAJLE8lHnbkF0CgYEAhyxpz3+3a4G3JsHDOWYjCfoLhVAEb9CNyC9c
+3QuVbvMVDlEBvgFdivm+3lZYWYOoYP0HQgNw59svzUxks5Hg7vUk9abN8CnvEgKX
+PszTUR0g450NzW6xr8PbmO/NR9bnKRUK2Tb1OkMldePdMY6CDykU7g3EqiZ+H+Zq
+kaaUUaECgYEAmk5W+S94q5jLemnfAChC5lva/0/aHdhtaoH4Lo+j0haMsdiy8/ZE
+sh+3gQ8pqwaCAwnKxAcppt/FNZ7tHRsH3oyY6biypn3WppQj+BA41nuzbspOKJhR
+ZDXKFCItbzUjyi23Dx4P4DgMivkpV+e88RMIuBnv4yjl5iOLq+vf4Rg=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/vendor/redis/test/support/ssl/trusted-cert.crt b/lib/vendor/redis/test/support/ssl/trusted-cert.crt
new file mode 100644
index 0000000..8a26c1a
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/trusted-cert.crt
@@ -0,0 +1,81 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 14908262977180600576 (0xcee4ca30bcf50900)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=IT, ST=Sicily, L=Catania, O=Redis, OU=Security, CN=127.0.0.1
+ Validity
+ Not Before: Apr 2 03:34:42 2016 GMT
+ Not After : Jun 23 03:34:42 2050 GMT
+ Subject: C=IT, ST=Sicily, O=Redis, OU=Security, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:ab:bf:ac:ef:dc:99:35:fa:07:3f:d5:33:86:f1:
+ 7d:9e:57:8b:d5:c1:10:04:0c:35:95:7c:61:ff:05:
+ a6:f9:ef:71:5c:c5:83:68:a2:ad:5d:0f:a5:2b:b4:
+ 76:9f:36:8f:df:75:fb:d6:48:00:c0:f0:68:56:f6:
+ 49:84:4d:4e:e1:ca:dd:24:9f:2f:5e:7c:35:26:57:
+ d6:d5:95:d1:3f:40:32:22:43:2c:8c:b7:8c:89:56:
+ 7c:d0:94:e5:f7:cf:4a:51:3f:60:b2:fe:1f:3b:38:
+ d6:47:5d:2e:4f:38:75:d9:9b:c8:0f:d1:fd:91:5a:
+ 07:c3:94:95:1f:7b:f1:ae:dc:a1:83:e2:6b:78:05:
+ 34:b3:8b:87:86:31:9f:cc:8b:15:cd:18:2e:06:36:
+ ca:f8:29:f8:6e:93:60:78:ec:8a:e8:a6:94:ad:24:
+ a8:e3:d4:ac:42:da:52:0f:34:e8:d0:10:e5:53:db:
+ f8:3a:56:48:10:33:df:80:70:1c:72:5e:1f:c3:11:
+ bb:3b:b9:6b:0a:e0:82:eb:67:d4:8f:5c:30:d3:cf:
+ 17:6d:86:01:0e:ae:43:c1:d8:c0:5e:99:ef:fa:60:
+ 0a:f2:62:68:62:8b:05:f3:8b:b1:34:d8:70:78:35:
+ 74:76:c2:46:13:a3:1f:5d:7b:3b:49:20:1e:98:54:
+ 63:77
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 81:DE:C0:39:F9:8A:57:50:DB:B1:6A:B3:D0:5F:E9:2C:87:5A:1E:3D
+ X509v3 Authority Key Identifier:
+ keyid:F9:81:BD:90:94:77:57:2D:F5:77:B4:15:CB:14:40:63:22:93:2B:5F
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a3:0a:d7:22:5a:bc:cc:f6:ed:2f:f2:9f:dd:e0:46:02:73:14:
+ dd:a7:f5:39:b9:16:19:16:36:b6:22:5c:66:14:c0:d3:ac:55:
+ fc:52:2d:c3:b2:70:5f:cf:3d:23:71:78:e9:31:88:65:2c:2e:
+ 4a:09:6e:4b:97:bb:4d:38:87:d8:25:ed:bb:ed:62:19:08:50:
+ f2:40:cc:39:ee:f9:a8:3a:5d:2b:e7:34:eb:8a:74:c7:c9:bc:
+ 88:9b:9b:ca:5b:11:20:ca:53:b2:0b:20:49:fc:b9:f7:ec:03:
+ c9:5d:c1:24:75:27:f8:7c:70:dc:6a:2c:98:48:93:5f:7f:7e:
+ 94:a1:cf:79:b3:24:e3:de:9e:f0:0f:d8:d6:3e:c9:52:30:31:
+ 87:90:c2:d2:23:be:d8:7a:e9:e6:bb:4b:00:75:30:49:4b:98:
+ d5:f6:7d:b5:83:b5:57:85:20:98:00:51:55:c3:a2:81:ec:6c:
+ 11:91:33:60:14:7b:d2:01:ee:5b:bf:5b:68:f5:e0:4e:45:0a:
+ 68:cd:33:4f:29:72:fa:fe:6a:19:b6:84:70:90:a4:d5:7a:04:
+ 2e:da:5b:98:4f:e4:aa:a6:c4:68:aa:5c:8c:a5:5e:df:20:94:
+ 22:f7:37:45:71:a4:bc:72:34:ee:42:cf:9d:0f:fb:4a:39:d1:
+ 8e:41:f3:3f
+-----BEGIN CERTIFICATE-----
+MIIDvDCCAqSgAwIBAgIJAM7kyjC89QkAMA0GCSqGSIb3DQEBBQUAMGcxCzAJBgNV
+BAYTAklUMQ8wDQYDVQQIEwZTaWNpbHkxEDAOBgNVBAcTB0NhdGFuaWExDjAMBgNV
+BAoTBVJlZGlzMREwDwYDVQQLEwhTZWN1cml0eTESMBAGA1UEAxMJMTI3LjAuMC4x
+MCAXDTE2MDQwMjAzMzQ0MloYDzIwNTAwNjIzMDMzNDQyWjBVMQswCQYDVQQGEwJJ
+VDEPMA0GA1UECBMGU2ljaWx5MQ4wDAYDVQQKEwVSZWRpczERMA8GA1UECxMIU2Vj
+dXJpdHkxEjAQBgNVBAMTCTEyNy4wLjAuMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAKu/rO/cmTX6Bz/VM4bxfZ5Xi9XBEAQMNZV8Yf8FpvnvcVzFg2ii
+rV0PpSu0dp82j991+9ZIAMDwaFb2SYRNTuHK3SSfL158NSZX1tWV0T9AMiJDLIy3
+jIlWfNCU5ffPSlE/YLL+Hzs41kddLk84ddmbyA/R/ZFaB8OUlR978a7coYPia3gF
+NLOLh4Yxn8yLFc0YLgY2yvgp+G6TYHjsiuimlK0kqOPUrELaUg806NAQ5VPb+DpW
+SBAz34BwHHJeH8MRuzu5awrggutn1I9cMNPPF22GAQ6uQ8HYwF6Z7/pgCvJiaGKL
+BfOLsTTYcHg1dHbCRhOjH117O0kgHphUY3cCAwEAAaN7MHkwCQYDVR0TBAIwADAs
+BglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYD
+VR0OBBYEFIHewDn5ildQ27Fqs9Bf6SyHWh49MB8GA1UdIwQYMBaAFPmBvZCUd1ct
+9Xe0FcsUQGMikytfMA0GCSqGSIb3DQEBBQUAA4IBAQCjCtciWrzM9u0v8p/d4EYC
+cxTdp/U5uRYZFja2IlxmFMDTrFX8Ui3DsnBfzz0jcXjpMYhlLC5KCW5Ll7tNOIfY
+Je277WIZCFDyQMw57vmoOl0r5zTrinTHybyIm5vKWxEgylOyCyBJ/Ln37APJXcEk
+dSf4fHDcaiyYSJNff36Uoc95syTj3p7wD9jWPslSMDGHkMLSI77Yeunmu0sAdTBJ
+S5jV9n21g7VXhSCYAFFVw6KB7GwRkTNgFHvSAe5bv1to9eBORQpozTNPKXL6/moZ
+toRwkKTVegQu2luYT+SqpsRoqlyMpV7fIJQi9zdFcaS8cjTuQs+dD/tKOdGOQfM/
+-----END CERTIFICATE-----
diff --git a/lib/vendor/redis/test/support/ssl/trusted-cert.key b/lib/vendor/redis/test/support/ssl/trusted-cert.key
new file mode 100644
index 0000000..7dee292
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/trusted-cert.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrv6zv3Jk1+gc/
+1TOG8X2eV4vVwRAEDDWVfGH/Bab573FcxYNooq1dD6UrtHafNo/fdfvWSADA8GhW
+9kmETU7hyt0kny9efDUmV9bVldE/QDIiQyyMt4yJVnzQlOX3z0pRP2Cy/h87ONZH
+XS5POHXZm8gP0f2RWgfDlJUfe/Gu3KGD4mt4BTSzi4eGMZ/MixXNGC4GNsr4Kfhu
+k2B47IroppStJKjj1KxC2lIPNOjQEOVT2/g6VkgQM9+AcBxyXh/DEbs7uWsK4ILr
+Z9SPXDDTzxdthgEOrkPB2MBeme/6YAryYmhiiwXzi7E02HB4NXR2wkYTox9deztJ
+IB6YVGN3AgMBAAECggEASmOxIgtoiQqMzUcpFE/Q2x6MQL9okng/VUoUoALwudzO
+OyKJsm6TrHU0U2PM5VUap+1QcRWqzebTKqduXFGn0wCtHEmemMwvsTXmpYhIo57I
+mDKEP0bZJjtBwI5dtSIhzGMpHR4YpOwPU8W2YzXPRbvFwaRwsd5O8pWOqZ5jphrQ
+DtkLNz4hIFsMihPeYFpuAjsZ2cMIGPtlY2qbfjyno7hd7LxNzL/2vMlDw5MHHtw4
+snxLN92KomC6rSUUydNDyemyMpg8iRwm7gmYzVoZf6aTbI2RdFcv2KZfpUWYdB+I
+yU8ZV1Sch7VQ+xLVy74SuY8AZ2Rq4S3M+EmEa5ghoQKBgQDfgOIyStulfYn6UC1A
+OYwcGoSOaVNfPE/m9BZN58xK0+XnEqQECMsyg/GYS65Zri4+KJYPxqv6f9ljLTGE
+0PxiA7wq+QWnv4EM+3aGShxwyVlmgJZyyBfJtAMr1iDm4JsidTT5GMdfxRICPGZY
+WVggcz/cVu39OxRrumREuTWAzwKBgQDEuGheZv68cYt5EkzOWxeFQyt1bzXn1CJg
+IXnIFZIekJhVGpBG+zMAYri9+hSheiDrwfIcSfTq9LxF6JNUvaU4qMrkwvW21jKs
+n7ofcA+VYq2mggoIuuvKVqXemLHorC0U/zCMnM6rycaa9sB5tsF+Js93uvf1TEJt
+veV0yCeM2QKBgF1M0iAoe7SDyXuCyMEMxN5ee4NvmGwjIz/IGR+Aahm6hziE4Y8F
+lL2LsujefvPU8FzmWG5Rgy1Y/YiXLxrAmvrXkE9oEOJL4TVoK7w3Z9P1Waqedy+H
+M9bxnHlKNAXtMRWbU/fATko+XBwu1pJ/CXjSY5A5gbO6W/X0ozLFFf6lAoGABRZ7
+5I0nY3pQUCZQBDpI5nJxSk1BCKjs5q2W97zPFalJt1HDj4JptEXZX1h7dh2xgkd2
+2pJzGiyQPgKg5N0uy8NZ1AbS0hLCJsLOzodYb9Wohhjw537mIEqTaalrWIgzdkqP
+V+OqWLkUQOfG3J8EbB3W2dLlHNwHD82MhLO0iikCgYEAvdK5LmpUdZXMVtiOMZO5
+t3M0vwi6vPhW7DV1QET51x/U+SyH4rvZGeqRl+gcKrZ8SreOlyICvsPgVmvLCrHE
+gJLPWJIzI8Mg6u91/KpiVmRahnJjOn3oHNuLSqFjn9lIhmA5dN7zQDXzPdYrWPNR
+u1QX+JLhlP33ejgdkdLsNiM=
+-----END PRIVATE KEY-----
diff --git a/lib/vendor/redis/test/support/ssl/untrusted-ca.crt b/lib/vendor/redis/test/support/ssl/untrusted-ca.crt
new file mode 100644
index 0000000..3495dcb
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/untrusted-ca.crt
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEXDCCA0SgAwIBAgIJAIgFm03l5AJkMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNV
+BAYTAlhYMRIwEAYDVQQIEwlVbnRydXN0ZWQxEjAQBgNVBAcTCUV2aWx2aWxsZTEU
+MBIGA1UEChMLRXZpbCBIYWNrZXIxGjAYBgNVBAsTEUF0dGFjayBEZXBhcnRtZW50
+MRIwEAYDVQQDEwkxMjcuMC4wLjEwIBcNMTYwNDAyMDMzNDUxWhgPMjA1MDA2MjMw
+MzM0NTFaMHsxCzAJBgNVBAYTAlhYMRIwEAYDVQQIEwlVbnRydXN0ZWQxEjAQBgNV
+BAcTCUV2aWx2aWxsZTEUMBIGA1UEChMLRXZpbCBIYWNrZXIxGjAYBgNVBAsTEUF0
+dGFjayBEZXBhcnRtZW50MRIwEAYDVQQDEwkxMjcuMC4wLjEwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCtXxcEGUrGqAqlBK94B8IwSkB8OnLYC/c/6Tde
+WG46pzmZWZvffi0akcKKubRNcfoavOuqLuNnpQDkIlnJ37K/LZk8Q5+aMoUGBiQ2
+jSN1707sFqH3eTFvXOUzlDEcsBa7Y7RuaI8SXg1UGsnnCcj6H3BW2xKcXPN6/s30
+vhNw2CPqtXm4NOD3Zb5FkB9epAEejRg0OPn5DJ3mESVp/H2EqkptMZ+6cOk2/CMc
+e8AAfcxBGwKuOMXNODszTNxN+OuGCHOxx8+vR/eV35tonISwbkmO9WI6DC+pWT2s
+PvDhuQtqsrVofCP/pireb5Ce/7bP/FsZcNSMMfV5dponcYrrAgMBAAGjgeAwgd0w
+HQYDVR0OBBYEFLeDNvKpJKmuyPsamax2AZTijdkwMIGtBgNVHSMEgaUwgaKAFLeD
+NvKpJKmuyPsamax2AZTijdkwoX+kfTB7MQswCQYDVQQGEwJYWDESMBAGA1UECBMJ
+VW50cnVzdGVkMRIwEAYDVQQHEwlFdmlsdmlsbGUxFDASBgNVBAoTC0V2aWwgSGFj
+a2VyMRowGAYDVQQLExFBdHRhY2sgRGVwYXJ0bWVudDESMBAGA1UEAxMJMTI3LjAu
+MC4xggkAiAWbTeXkAmQwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA
+FWYTrxi/h7PYIpp09QsbDiGdC7gmp04HTx82NvBaUFaLk8ygz4DUz5u7QyTDdAga
+yWviHghuyZ6vv5Ubaj7XLOzLM6rYsQjkVq5ltwP+9V/U/b5jOHvZdYqdatVXUXxR
+SO+e3QYiMpM4Vs/NNXhpUp6apD7VcoB2LgK3vGDJ526PBJjgw24311t8O7kDTwkt
+AwX56/KTolMI+k9rT8Ee6aucT6gBNf0judhNkPVo+6CYgjmEVRrN/xaFCUNSpv5E
+O6uIcxSSX6a5iOZ/EH+GyHb6kDmztn/Hes+UN9+gMuAK7+LgsD2mYbxn9Pnaerrs
+2nER8XurylLxi0GLvNWNdQ==
+-----END CERTIFICATE-----
diff --git a/lib/vendor/redis/test/support/ssl/untrusted-ca.key b/lib/vendor/redis/test/support/ssl/untrusted-ca.key
new file mode 100644
index 0000000..3e30218
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/untrusted-ca.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEArV8XBBlKxqgKpQSveAfCMEpAfDpy2Av3P+k3XlhuOqc5mVmb
+334tGpHCirm0TXH6Grzrqi7jZ6UA5CJZyd+yvy2ZPEOfmjKFBgYkNo0jde9O7Bah
+93kxb1zlM5QxHLAWu2O0bmiPEl4NVBrJ5wnI+h9wVtsSnFzzev7N9L4TcNgj6rV5
+uDTg92W+RZAfXqQBHo0YNDj5+Qyd5hElafx9hKpKbTGfunDpNvwjHHvAAH3MQRsC
+rjjFzTg7M0zcTfjrhghzscfPr0f3ld+baJyEsG5JjvViOgwvqVk9rD7w4bkLarK1
+aHwj/6Yq3m+Qnv+2z/xbGXDUjDH1eXaaJ3GK6wIDAQABAoIBAQCeC0QxAVlwNTnW
+6qmGsxPr75RPavzMREQ1p8VIpTZ/E3hneg+lMiGtydhdnCJoQxGrFDOFJU86aWmh
+jkrpw5nvu4KoNEEnUQyAzFJwxELiPLBmec9WiM1u5nEujtYif8eJNcACsiBSrxhZ
+Zj5N9laW5NgE5ZpWnkl7AxL/G9MfFvifr9KtyDcs+wnYD6ffz/bRwS54veMccj/q
+SkVQRL7FM4NJczG0TTp+LT/1R3s8YVv9GHnJ6K7Gol3E0PbFS1HztDuMVonhWiac
+9Rjt7w0rNgeH6ZbCMXrUd+8I8amazA78p1ky0Mh8d6UUVFU1jjtyxlgDh06IPsnE
++exeAClxAoGBAOMZ7LEFr3VcFwym7RvgckeQhd6Rmz8Bh7kGfB9pDsHFprGJ0rm4
+XgNETJXOky5wUCPZmMBN1iAU/ehyyXqPykXiKjAQLxQNHR9/Z6P58PsHs2Uw8VZa
+XdZwlBME5+/yl5DiirO5rCt804DdCQgSu7denudwWbbtzAsodSKj5zEJAoGBAMNu
+21hZnsvhtZlvQVHZ4sQttrv9e5VpWWHkDPRN3sdLZPfK/+3L0BmUrGotgNWpTZwR
+8YvKRT2Xn1unzpKlkHtIVuHU7khOj+tYHLIx3rezVanw9QzbIANMel6STlUr3jwX
+fjnibgkJixxHTOBs8/zm219Q1sNTos9GUOAZQb1TAoGALwGFsVpo59TI3JCMkXGS
+led/HgNra84oRo7mECZRrJ/5kdPiLxjPNMPlSji41CrhG5qFeIBj6r4NlBh2RY0P
+pAldDBe9dtwEBCn9zL4GOB9u7WoE+ge4VpN0wr8INu0ynAWYCf1LerDaolid7vLZ
+sem+4E6r8yYjTsfv/tyIFOkCgYEAlCZobxxZLbNn5+2X9cWXiyIgYXgyBDy9fmDT
+lSum0yuLWfDwfELB+XJkFYVzIgVbCRHtKwxl2uAi9OdLyI1r7pkTC9VP4U50+XJt
+JoR5koaHTPGVwm4mYXnLVf/RE+3SZXllvdmxknZCl2hRldviRfh3mlT8yUuQo1Jp
+oshitnMCgYAXTQLA7B5YLmhCG8HRM/h/xM55ObdDX1SIWUbk3p1uxak1W0abfvpi
+FPBy2riOdSDA6Uv7V8y1j4tENGVMyyMsEpFdLDX4Lkbh9niOoPeHCWdO0boPk0Fw
+aPXtT7gdTPWJulKOxtLuGqBjZZ77TO49uqWlEMaerulWyjhRm8zzvA==
+-----END RSA PRIVATE KEY-----
diff --git a/lib/vendor/redis/test/support/ssl/untrusted-cert.crt b/lib/vendor/redis/test/support/ssl/untrusted-cert.crt
new file mode 100644
index 0000000..127f8e4
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/untrusted-cert.crt
@@ -0,0 +1,82 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 9801410922913464933 (0x88059b4de5e40265)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=XX, ST=Untrusted, L=Evilville, O=Evil Hacker, OU=Attack Department, CN=127.0.0.1
+ Validity
+ Not Before: Apr 2 03:34:51 2016 GMT
+ Not After : Jun 23 03:34:51 2050 GMT
+ Subject: C=XX, ST=Untrusted, O=Evil Hacker, OU=Attack Department, CN=127.0.0.1
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:9a:73:e7:45:fc:d3:b5:4a:bd:bd:ad:30:e5:24:
+ 74:38:01:89:8f:a9:90:bf:3c:4a:bf:d1:f1:5e:db:
+ c8:aa:26:59:e6:ec:b3:a0:0f:4d:74:59:dd:c9:27:
+ 2f:e1:48:7d:30:d9:59:06:2f:29:f0:d1:25:33:79:
+ 5f:58:9d:d7:54:c8:a7:aa:1a:84:00:a2:85:63:32:
+ cc:ef:73:7d:b0:26:c6:95:f1:86:16:68:38:63:57:
+ 09:0d:6f:6a:70:e8:75:3b:72:b4:b1:4d:01:0e:01:
+ 0e:bf:bf:6a:8c:88:fe:0d:cb:88:43:1b:da:ed:0c:
+ 88:25:33:f7:b9:b1:fc:32:b8:94:c9:20:7c:ac:49:
+ e4:c1:58:93:69:0e:41:e3:df:96:e3:47:11:14:8c:
+ e4:4b:b6:56:df:6f:5e:d2:48:dc:a1:8a:98:cc:4b:
+ 02:89:95:ea:f6:de:a5:3a:9c:06:7c:f0:7c:09:6f:
+ 27:11:f2:b1:1b:47:6b:a3:ea:d6:ee:a1:65:91:84:
+ cf:2e:81:d3:55:4a:e8:01:4e:72:41:ac:92:e0:7d:
+ 7c:fe:85:f0:2e:f1:ee:4a:80:f9:4e:5a:b4:95:6c:
+ bb:fe:ff:46:58:4a:7b:fc:a0:63:59:5d:01:5b:63:
+ 06:5c:94:83:30:27:81:f0:1a:13:89:5a:5a:a2:e2:
+ 0f:eb
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ Netscape Comment:
+ OpenSSL Generated Certificate
+ X509v3 Subject Key Identifier:
+ 1B:71:91:99:43:12:0F:D3:59:FC:00:EF:99:F3:42:CF:41:FD:40:1D
+ X509v3 Authority Key Identifier:
+ keyid:B7:83:36:F2:A9:24:A9:AE:C8:FB:1A:99:AC:76:01:94:E2:8D:D9:30
+
+ Signature Algorithm: sha1WithRSAEncryption
+ a4:cd:88:c3:19:b7:cd:7e:7a:e7:85:1f:fb:3e:31:0b:ff:9d:
+ 6f:b1:a2:72:56:4a:b1:ec:6c:f3:99:bd:65:08:0a:e9:47:1d:
+ 79:55:5b:29:b1:d4:85:69:85:65:3f:30:37:a1:0e:76:d2:1f:
+ b0:76:2a:23:75:c9:05:a4:89:cf:c1:68:42:16:46:d6:c9:a8:
+ e5:06:5b:52:45:d4:41:5d:f3:c7:00:d1:ca:cc:3e:4c:63:e6:
+ 7a:fe:ce:20:a4:df:e3:7c:e3:75:6e:f7:18:84:1c:9b:56:ce:
+ 55:fb:04:b9:de:11:6e:7d:5d:47:de:a9:ed:3e:79:48:a5:4f:
+ 32:d5:96:8d:ea:e2:a6:8a:c2:e9:f5:b0:8d:da:ef:71:96:60:
+ b0:7e:c3:3d:e9:37:91:27:bf:ae:5c:e8:c0:9a:6f:c8:38:62:
+ 90:d0:49:c1:7f:28:13:da:29:bb:5b:d1:72:6f:23:7c:a0:87:
+ 44:96:47:53:0e:0d:1d:74:d9:26:6b:b3:01:24:9c:5e:c8:f4:
+ 11:fe:35:14:6c:ec:e7:42:5f:32:56:f0:9d:8d:11:02:21:07:
+ cc:ce:7b:f0:e9:bc:83:c8:93:b0:8c:a7:e9:b1:c2:12:6b:30:
+ 2b:75:dc:61:b8:d4:87:6b:07:2d:75:b0:7a:18:6e:19:7f:04:
+ 78:c6:c7:b7
+-----BEGIN CERTIFICATE-----
+MIID4jCCAsqgAwIBAgIJAIgFm03l5AJlMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNV
+BAYTAlhYMRIwEAYDVQQIEwlVbnRydXN0ZWQxEjAQBgNVBAcTCUV2aWx2aWxsZTEU
+MBIGA1UEChMLRXZpbCBIYWNrZXIxGjAYBgNVBAsTEUF0dGFjayBEZXBhcnRtZW50
+MRIwEAYDVQQDEwkxMjcuMC4wLjEwIBcNMTYwNDAyMDMzNDUxWhgPMjA1MDA2MjMw
+MzM0NTFaMGcxCzAJBgNVBAYTAlhYMRIwEAYDVQQIEwlVbnRydXN0ZWQxFDASBgNV
+BAoTC0V2aWwgSGFja2VyMRowGAYDVQQLExFBdHRhY2sgRGVwYXJ0bWVudDESMBAG
+A1UEAxMJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+mnPnRfzTtUq9va0w5SR0OAGJj6mQvzxKv9HxXtvIqiZZ5uyzoA9NdFndyScv4Uh9
+MNlZBi8p8NElM3lfWJ3XVMinqhqEAKKFYzLM73N9sCbGlfGGFmg4Y1cJDW9qcOh1
+O3K0sU0BDgEOv79qjIj+DcuIQxva7QyIJTP3ubH8MriUySB8rEnkwViTaQ5B49+W
+40cRFIzkS7ZW329e0kjcoYqYzEsCiZXq9t6lOpwGfPB8CW8nEfKxG0dro+rW7qFl
+kYTPLoHTVUroAU5yQayS4H18/oXwLvHuSoD5Tlq0lWy7/v9GWEp7/KBjWV0BW2MG
+XJSDMCeB8BoTiVpaouIP6wIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIB
+DQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUG3GR
+mUMSD9NZ/ADvmfNCz0H9QB0wHwYDVR0jBBgwFoAUt4M28qkkqa7I+xqZrHYBlOKN
+2TAwDQYJKoZIhvcNAQEFBQADggEBAKTNiMMZt81+eueFH/s+MQv/nW+xonJWSrHs
+bPOZvWUICulHHXlVWymx1IVphWU/MDehDnbSH7B2KiN1yQWkic/BaEIWRtbJqOUG
+W1JF1EFd88cA0crMPkxj5nr+ziCk3+N843Vu9xiEHJtWzlX7BLneEW59XUfeqe0+
+eUilTzLVlo3q4qaKwun1sI3a73GWYLB+wz3pN5Env65c6MCab8g4YpDQScF/KBPa
+Kbtb0XJvI3ygh0SWR1MODR102SZrswEknF7I9BH+NRRs7OdCXzJW8J2NEQIhB8zO
+e/DpvIPIk7CMp+mxwhJrMCt13GG41IdrBy11sHoYbhl/BHjGx7c=
+-----END CERTIFICATE-----
diff --git a/lib/vendor/redis/test/support/ssl/untrusted-cert.key b/lib/vendor/redis/test/support/ssl/untrusted-cert.key
new file mode 100644
index 0000000..a52934a
--- /dev/null
+++ b/lib/vendor/redis/test/support/ssl/untrusted-cert.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCac+dF/NO1Sr29
+rTDlJHQ4AYmPqZC/PEq/0fFe28iqJlnm7LOgD010Wd3JJy/hSH0w2VkGLynw0SUz
+eV9YnddUyKeqGoQAooVjMszvc32wJsaV8YYWaDhjVwkNb2pw6HU7crSxTQEOAQ6/
+v2qMiP4Ny4hDG9rtDIglM/e5sfwyuJTJIHysSeTBWJNpDkHj35bjRxEUjORLtlbf
+b17SSNyhipjMSwKJler23qU6nAZ88HwJbycR8rEbR2uj6tbuoWWRhM8ugdNVSugB
+TnJBrJLgfXz+hfAu8e5KgPlOWrSVbLv+/0ZYSnv8oGNZXQFbYwZclIMwJ4HwGhOJ
+Wlqi4g/rAgMBAAECggEAPX3fmfGqqAb1u8p0KQZ2bsXN6rBrvHdYmz4OhuGh5nwW
+VuXuLc9p2uTcc/VyDpM5pHUkCF5GqGXcFb5Aw5sz28F3XzXnUAlkabYT+VFVvQfz
+EEd0Rv9/U62XIQ42pnUmF2D3p48s2FJ7eMPQu9reqsdZnL4+TxoqKgWinv/JlLdh
+zBxjgVgaDMsvVc4cuuT6bcI3DUe2F9ALBKfaCxZoOUSsmgieuXog00Bzv0NmZoUD
+WsAX0syzUlwjVmCr8J4I0IByYAbn1S/ozU141Z+H+VUyuEpYw0zDqDNrlmdYclc8
+neoq8Xj9Cx1zHdF5H3aT9SLUGxdHPJpED9wQNx2toQKBgQDJcgJEG39u3h3mW/At
+f8jl8evar5nUOOn5AIxVGFAWx4ZvoboxHSRlS6UkF0AImlH4L7xQESb9BMzOrObN
+PBNQrccH+fz1o1fHDhob7EvyMMwzmDCPpQnN/6KXRzapu2MDFvlMkEMITTN7J0En
+c9BOxo06Q4DKXGVCiWmbIwXihQKBgQDER/KfaWRZWOA2mQ26giJVjUX4+s1GeQM0
+V4AIo1KS6fDzh68RsAQpMTx/N8aHEcxf+2qGIOTCvFY3Nxqe5aw/Xiz47MPlYulM
+OecovSO96nidhyv2Zux+HpvI85tcWTyORi+RWho51gTOLth6BJ4uvSsaooWmO0Va
+GoIxKcaLrwKBgH/guuWHWy8DG5H3fRE1FFA8cc+iN5HMC2NBYNRIGddME+BblznE
+WS1ghtXRWJnddPmLPAzLxqdJ28W7ZsyUPWKy3i0HGfjJF1jKb/KX32JAbfC2xOT7
+DK1TgWBtGZtH1EPK2rkqvxLPB0Y/lhG4aF0Jl++LmH9dhf5mAr8zzXGNAoGBAKEi
+l7H66aDX76mi2LxmnR0yz2DpNKBINDNCKh/tRJrLZz3mA/k3URMoEow2E8tK90dM
+tVTLqEGeMAFAQaB02IVlIPJyHRgxrWkgl/6/15nP5ZkdISA1uqyHIElGhCK6N5Zt
+VBu1ppYYdvV1S85QADRKpBpHlgSz3+lqnbsSmqaNAoGAacA3XSIzTHj6+06VzEHN
+aO2LJbBxl6Eduj6eTEgcZBlQOX6cVvaleTAT2g2xM0aGMV4StmdijUdktjBQVLpH
+8PBTqlfVuLXEXQd+qWMpUJwEkh/pdmf9EPoLSfp3zQLaNI/kCg3jQtR4n6/68hfi
+5Q6L0mN+SoB+jRNPDSV7JWA=
+-----END PRIVATE KEY-----
diff --git a/lib/vendor/redis/test/support/wire/synchrony.rb b/lib/vendor/redis/test/support/wire/synchrony.rb
new file mode 100644
index 0000000..f27d448
--- /dev/null
+++ b/lib/vendor/redis/test/support/wire/synchrony.rb
@@ -0,0 +1,24 @@
+class Wire < Fiber
+ # We cannot run this fiber explicitly because EM schedules it. Resuming the
+ # current fiber on the next tick to let the reactor do work.
+ def self.pass
+ f = Fiber.current
+ EM.next_tick { f.resume }
+ Fiber.yield
+ end
+
+ def self.sleep(sec)
+ EM::Synchrony.sleep(sec)
+ end
+
+ def initialize(&blk)
+ super
+
+ # Schedule run in next tick
+ EM.next_tick { resume }
+ end
+
+ def join
+ self.class.pass while alive?
+ end
+end
diff --git a/lib/vendor/redis/test/support/wire/thread.rb b/lib/vendor/redis/test/support/wire/thread.rb
new file mode 100644
index 0000000..aa5a67c
--- /dev/null
+++ b/lib/vendor/redis/test/support/wire/thread.rb
@@ -0,0 +1,5 @@
+class Wire < Thread
+ def self.sleep(sec)
+ Kernel.sleep(sec)
+ end
+end
diff --git a/lib/vendor/redis/test/synchrony_driver.rb b/lib/vendor/redis/test/synchrony_driver.rb
new file mode 100644
index 0000000..82b13a7
--- /dev/null
+++ b/lib/vendor/redis/test/synchrony_driver.rb
@@ -0,0 +1,88 @@
+# encoding: UTF-8
+
+require 'em-synchrony'
+require 'em-synchrony/connection_pool'
+
+require 'redis'
+require 'redis/connection/synchrony'
+
+
+require File.expand_path("./helper", File.dirname(__FILE__))
+
+PORT = 6381
+OPTIONS = {:port => PORT, :db => 15}
+
+#
+# if running under Eventmachine + Synchrony (Ruby 1.9+), then
+# we can simulate the blocking API while performing the network
+# IO via the EM reactor.
+#
+
+EM.synchrony do
+ r = Redis.new OPTIONS
+ r.flushdb
+
+ r.rpush "foo", "s1"
+ r.rpush "foo", "s2"
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.rpop("foo")
+
+ r.set("foo", "bar")
+
+ assert_equal "bar", r.getset("foo", "baz")
+ assert_equal "baz", r.get("foo")
+
+ r.set("foo", "a")
+
+ assert_equal 1, r.getbit("foo", 1)
+ assert_equal 1, r.getbit("foo", 2)
+ assert_equal 0, r.getbit("foo", 3)
+ assert_equal 0, r.getbit("foo", 4)
+ assert_equal 0, r.getbit("foo", 5)
+ assert_equal 0, r.getbit("foo", 6)
+ assert_equal 1, r.getbit("foo", 7)
+
+ r.flushdb
+
+ # command pipelining
+ r.pipelined do
+ r.lpush "foo", "s1"
+ r.lpush "foo", "s2"
+ end
+
+ assert_equal 2, r.llen("foo")
+ assert_equal "s2", r.lpop("foo")
+ assert_equal "s1", r.lpop("foo")
+
+ assert_equal "OK", r.client.call(:quit)
+ assert_equal "PONG", r.ping
+
+
+ rpool = EM::Synchrony::ConnectionPool.new(size: 5) { Redis.new OPTIONS }
+
+ result = rpool.watch 'foo' do |rd|
+ assert_kind_of Redis, rd
+
+ rd.set "foo", "s1"
+ rd.multi do |multi|
+ multi.set "foo", "s2"
+ end
+ end
+
+ assert_equal nil, result
+ assert_equal "s1", rpool.get("foo")
+
+ result = rpool.watch "foo" do |rd|
+ assert_kind_of Redis, rd
+
+ rd.multi do |multi|
+ multi.set "foo", "s3"
+ end
+ end
+
+ assert_equal ["OK"], result
+ assert_equal "s3", rpool.get("foo")
+
+ EM.stop
+end
diff --git a/lib/vendor/redis/test/test.conf.erb b/lib/vendor/redis/test/test.conf.erb
new file mode 100644
index 0000000..051cb80
--- /dev/null
+++ b/lib/vendor/redis/test/test.conf.erb
@@ -0,0 +1,9 @@
+dir <%= REDIS_DIR %>
+pidfile <%= REDIS_PID %>
+port 6381
+unixsocket <%= REDIS_SOCKET %>
+timeout 300
+loglevel debug
+logfile <%= REDIS_LOG %>
+databases 16
+daemonize yes
diff --git a/lib/vendor/redis/test/thread_safety_test.rb b/lib/vendor/redis/test/thread_safety_test.rb
new file mode 100644
index 0000000..f03ad77
--- /dev/null
+++ b/lib/vendor/redis/test/thread_safety_test.rb
@@ -0,0 +1,62 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestThreadSafety < Test::Unit::TestCase
+
+ include Helper::Client
+
+ driver(:ruby, :hiredis) do
+ def test_thread_safety
+ redis = Redis.new(OPTIONS)
+ redis.set "foo", 1
+ redis.set "bar", 2
+
+ sample = 100
+
+ t1 = Thread.new do
+ $foos = Array.new(sample) { redis.get "foo" }
+ end
+
+ t2 = Thread.new do
+ $bars = Array.new(sample) { redis.get "bar" }
+ end
+
+ t1.join
+ t2.join
+
+ assert_equal ["1"], $foos.uniq
+ assert_equal ["2"], $bars.uniq
+ end
+
+ def test_thread_safety_queue_commit
+ redis = Redis.new(OPTIONS)
+ redis.set "foo", 1
+ redis.set "bar", 2
+
+ sample = 100
+
+ t1 = Thread.new do
+ sample.times do
+ r.queue("get", "foo")
+ end
+
+ $foos = r.commit
+ end
+
+ t2 = Thread.new do
+ sample.times do
+ r.queue("get", "bar")
+ end
+
+ $bars = r.commit
+ end
+
+ t1.join
+ t2.join
+
+ assert_equal ["1"], $foos.uniq
+ assert_equal ["2"], $bars.uniq
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/transactions_test.rb b/lib/vendor/redis/test/transactions_test.rb
new file mode 100644
index 0000000..3f588b2
--- /dev/null
+++ b/lib/vendor/redis/test/transactions_test.rb
@@ -0,0 +1,264 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestTransactions < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_multi_discard
+ r.multi
+
+ assert_equal "QUEUED", r.set("foo", "1")
+ assert_equal "QUEUED", r.get("foo")
+
+ r.discard
+
+ assert_equal nil, r.get("foo")
+ end
+
+ def test_multi_exec_with_a_block
+ r.multi do |multi|
+ multi.set "foo", "s1"
+ end
+
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_multi_exec_with_a_block_doesn_t_return_replies_for_multi_and_exec
+ r1, r2, nothing_else = r.multi do |multi|
+ multi.set "foo", "s1"
+ multi.get "foo"
+ end
+
+ assert_equal "OK", r1
+ assert_equal "s1", r2
+ assert_equal nil, nothing_else
+ end
+
+ def test_assignment_inside_multi_exec_block
+ r.multi do |m|
+ @first = m.sadd("foo", 1)
+ @second = m.sadd("foo", 1)
+ end
+
+ assert_equal true, @first.value
+ assert_equal false, @second.value
+ end
+
+ # Although we could support accessing the values in these futures,
+ # it doesn't make a lot of sense.
+ def test_assignment_inside_multi_exec_block_with_delayed_command_errors
+ assert_raise(Redis::CommandError) do
+ r.multi do |m|
+ @first = m.set("foo", "s1")
+ @second = m.incr("foo") # not an integer
+ @third = m.lpush("foo", "value") # wrong kind of value
+ end
+ end
+
+ assert_equal "OK", @first.value
+ assert_raise(Redis::CommandError) { @second.value }
+ assert_raise(Redis::FutureNotReady) { @third.value }
+ end
+
+ def test_assignment_inside_multi_exec_block_with_immediate_command_errors
+ assert_raise(Redis::CommandError) do
+ r.multi do |m|
+ m.doesnt_exist
+ @first = m.sadd("foo", 1)
+ @second = m.sadd("foo", 1)
+ end
+ end
+
+ assert_raise(Redis::FutureNotReady) { @first.value }
+ assert_raise(Redis::FutureNotReady) { @second.value }
+ end
+
+ def test_raise_immediate_errors_in_multi_exec
+ assert_raise(RuntimeError) do
+ r.multi do |multi|
+ multi.set "bar", "s2"
+ raise "Some error"
+ multi.set "baz", "s3"
+ end
+ end
+
+ assert_equal nil, r.get("bar")
+ assert_equal nil, r.get("baz")
+ end
+
+ def test_transformed_replies_as_return_values_for_multi_exec_block
+ info, _ = r.multi do |m|
+ r.info
+ end
+
+ assert info.kind_of?(Hash)
+ end
+
+ def test_transformed_replies_inside_multi_exec_block
+ r.multi do |m|
+ @info = r.info
+ end
+
+ assert @info.value.kind_of?(Hash)
+ end
+
+ def test_raise_command_errors_in_multi_exec
+ assert_raise(Redis::CommandError) do
+ r.multi do |m|
+ m.set("foo", "s1")
+ m.incr("foo") # not an integer
+ m.lpush("foo", "value") # wrong kind of value
+ end
+ end
+
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_raise_command_errors_when_accessing_futures_after_multi_exec
+ begin
+ r.multi do |m|
+ m.set("foo", "s1")
+ @counter = m.incr("foo") # not an integer
+ end
+ rescue Exception
+ # Not gonna deal with it
+ end
+
+ # We should test for Redis::Error here, but hiredis doesn't yet do
+ # custom error classes.
+ err = nil
+ begin
+ @counter.value
+ rescue => err
+ end
+
+ assert err.kind_of?(RuntimeError)
+ end
+
+ def test_multi_with_a_block_yielding_the_client
+ r.multi do |multi|
+ multi.set "foo", "s1"
+ end
+
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_raise_command_error_when_exec_fails
+ redis_mock(:exec => lambda { |*_| "-ERROR" }) do |redis|
+ assert_raise(Redis::CommandError) do
+ redis.multi do |m|
+ m.set "foo", "s1"
+ end
+ end
+ end
+ end
+
+ def test_watch
+ res = r.watch "foo"
+
+ assert_equal "OK", res
+ end
+
+ def test_watch_with_an_unmodified_key
+ r.watch "foo"
+ r.multi do |multi|
+ multi.set "foo", "s1"
+ end
+
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_watch_with_an_unmodified_key_passed_as_array
+ r.watch ["foo", "bar"]
+ r.multi do |multi|
+ multi.set "foo", "s1"
+ end
+
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_watch_with_a_modified_key
+ r.watch "foo"
+ r.set "foo", "s1"
+ res = r.multi do |multi|
+ multi.set "foo", "s2"
+ end
+
+ assert_equal nil, res
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_watch_with_a_modified_key_passed_as_array
+ r.watch ["foo", "bar"]
+ r.set "foo", "s1"
+ res = r.multi do |multi|
+ multi.set "foo", "s2"
+ end
+
+ assert_equal nil, res
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_watch_with_a_block_and_an_unmodified_key
+ result = r.watch "foo" do |rd|
+
+ assert_same r, rd
+
+ rd.multi do |multi|
+ multi.set "foo", "s1"
+ end
+ end
+
+ assert_equal ["OK"], result
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_watch_with_a_block_and_a_modified_key
+ result = r.watch "foo" do |rd|
+
+ assert_same r, rd
+
+ rd.set "foo", "s1"
+ rd.multi do |multi|
+ multi.set "foo", "s2"
+ end
+ end
+
+ assert_equal nil, result
+ assert_equal "s1", r.get("foo")
+ end
+
+ def test_watch_with_a_block_that_raises_an_exception
+ r.set("foo", "s1")
+
+ begin
+ r.watch "foo" do
+ raise "test"
+ end
+ rescue RuntimeError
+ end
+
+ r.set("foo", "s2")
+
+ # If the watch was still set from within the block above, this multi/exec
+ # would fail. This proves that raising an exception above unwatches.
+ r.multi do |multi|
+ multi.set "foo", "s3"
+ end
+
+ assert_equal "s3", r.get("foo")
+ end
+
+ def test_unwatch_with_a_modified_key
+ r.watch "foo"
+ r.set "foo", "s1"
+ r.unwatch
+ r.multi do |multi|
+ multi.set "foo", "s2"
+ end
+
+ assert_equal "s2", r.get("foo")
+ end
+end
diff --git a/lib/vendor/redis/test/unknown_commands_test.rb b/lib/vendor/redis/test/unknown_commands_test.rb
new file mode 100644
index 0000000..0d1ca58
--- /dev/null
+++ b/lib/vendor/redis/test/unknown_commands_test.rb
@@ -0,0 +1,14 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestUnknownCommands < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_should_try_to_work
+ assert_raise Redis::CommandError do
+ r.not_yet_implemented_command
+ end
+ end
+end
diff --git a/lib/vendor/redis/test/url_param_test.rb b/lib/vendor/redis/test/url_param_test.rb
new file mode 100644
index 0000000..468bbf1
--- /dev/null
+++ b/lib/vendor/redis/test/url_param_test.rb
@@ -0,0 +1,138 @@
+# encoding: UTF-8
+
+require File.expand_path("helper", File.dirname(__FILE__))
+
+class TestUrlParam < Test::Unit::TestCase
+
+ include Helper::Client
+
+ def test_url_defaults_to_______________
+ redis = Redis.new
+
+ assert_equal "127.0.0.1", redis.client.host
+ assert_equal 6379, redis.client.port
+ assert_equal 0, redis.client.db
+ assert_equal nil, redis.client.password
+ end
+
+ def test_allows_to_pass_in_a_url
+ redis = Redis.new :url => "redis://:secr3t@foo.com:999/2"
+
+ assert_equal "foo.com", redis.client.host
+ assert_equal 999, redis.client.port
+ assert_equal 2, redis.client.db
+ assert_equal "secr3t", redis.client.password
+ end
+
+ def test_allows_to_pass_in_a_url_with_string_key
+ redis = Redis.new "url" => "redis://:secr3t@foo.com:999/2"
+
+ assert_equal "foo.com", redis.client.host
+ assert_equal 999, redis.client.port
+ assert_equal 2, redis.client.db
+ assert_equal "secr3t", redis.client.password
+ end
+
+ def test_unescape_password_from_url
+ redis = Redis.new :url => "redis://:secr3t%3A@foo.com:999/2"
+
+ assert_equal "secr3t:", redis.client.password
+ end
+
+ def test_unescape_password_from_url_with_string_key
+ redis = Redis.new "url" => "redis://:secr3t%3A@foo.com:999/2"
+
+ assert_equal "secr3t:", redis.client.password
+ end
+
+ def test_does_not_unescape_password_when_explicitly_passed
+ redis = Redis.new :url => "redis://:secr3t%3A@foo.com:999/2", :password => "secr3t%3A"
+
+ assert_equal "secr3t%3A", redis.client.password
+ end
+
+ def test_does_not_unescape_password_when_explicitly_passed_with_string_key
+ redis = Redis.new :url => "redis://:secr3t%3A@foo.com:999/2", "password" => "secr3t%3A"
+
+ assert_equal "secr3t%3A", redis.client.password
+ end
+
+ def test_override_url_if_path_option_is_passed
+ redis = Redis.new :url => "redis://:secr3t@foo.com/foo:999/2", :path => "/tmp/redis.sock"
+
+ assert_equal "/tmp/redis.sock", redis.client.path
+ assert_equal nil, redis.client.host
+ assert_equal nil, redis.client.port
+ end
+
+ def test_override_url_if_path_option_is_passed_with_string_key
+ redis = Redis.new :url => "redis://:secr3t@foo.com/foo:999/2", "path" => "/tmp/redis.sock"
+
+ assert_equal "/tmp/redis.sock", redis.client.path
+ assert_equal nil, redis.client.host
+ assert_equal nil, redis.client.port
+ end
+
+ def test_overrides_url_if_another_connection_option_is_passed
+ redis = Redis.new :url => "redis://:secr3t@foo.com:999/2", :port => 1000
+
+ assert_equal "foo.com", redis.client.host
+ assert_equal 1000, redis.client.port
+ assert_equal 2, redis.client.db
+ assert_equal "secr3t", redis.client.password
+ end
+
+ def test_overrides_url_if_another_connection_option_is_passed_with_string_key
+ redis = Redis.new :url => "redis://:secr3t@foo.com:999/2", "port" => 1000
+
+ assert_equal "foo.com", redis.client.host
+ assert_equal 1000, redis.client.port
+ assert_equal 2, redis.client.db
+ assert_equal "secr3t", redis.client.password
+ end
+
+ def test_does_not_overrides_url_if_a_nil_option_is_passed
+ redis = Redis.new :url => "redis://:secr3t@foo.com:999/2", :port => nil
+
+ assert_equal "foo.com", redis.client.host
+ assert_equal 999, redis.client.port
+ assert_equal 2, redis.client.db
+ assert_equal "secr3t", redis.client.password
+ end
+
+ def test_does_not_overrides_url_if_a_nil_option_is_passed_with_string_key
+ redis = Redis.new :url => "redis://:secr3t@foo.com:999/2", "port" => nil
+
+ assert_equal "foo.com", redis.client.host
+ assert_equal 999, redis.client.port
+ assert_equal 2, redis.client.db
+ assert_equal "secr3t", redis.client.password
+ end
+
+ def test_does_not_modify_the_passed_options
+ options = { :url => "redis://:secr3t@foo.com:999/2" }
+
+ Redis.new(options)
+
+ assert({ :url => "redis://:secr3t@foo.com:999/2" } == options)
+ end
+
+ def test_uses_redis_url_over_default_if_available
+ ENV["REDIS_URL"] = "redis://:secr3t@foo.com:999/2"
+
+ redis = Redis.new
+
+ assert_equal "foo.com", redis.client.host
+ assert_equal 999, redis.client.port
+ assert_equal 2, redis.client.db
+ assert_equal "secr3t", redis.client.password
+
+ ENV.delete("REDIS_URL")
+ end
+
+ def test_defaults_to_localhost
+ redis = Redis.new(:url => "redis:///")
+
+ assert_equal "127.0.0.1", redis.client.host
+ end
+end
diff --git a/support/vendor-gem.rb b/support/vendor-gem.rb
new file mode 100755
index 0000000..4115a56
--- /dev/null
+++ b/support/vendor-gem.rb
@@ -0,0 +1,38 @@
+#!/usr/bin/env ruby
+require 'fileutils'
+require 'open3'
+
+VENDOR_PREFIX = 'lib/vendor'
+
+def main(gem, version, vendor_dir)
+ if !vendor_dir.start_with?(VENDOR_PREFIX)
+ abort "Invalid vendor_dir: must start with #{VENDOR_PREFIX}: #{vendor_dir.inspect}"
+ end
+
+ FileUtils.rm_rf(vendor_dir)
+
+ # Use clear-sources to force https://rubygems.org as the source
+ if !system(*%W[gem fetch #{gem} -v #{version} --clear-sources -s https://rubygems.org])
+ abort "Failed to fetch gem"
+ end
+
+ FileUtils.mkdir_p(vendor_dir)
+
+ # A .gem file is a tar file containing a data.tar.gz file.
+ statuses = Open3.pipeline(
+ %W[tar -xOf - data.tar.gz],
+ %W[tar -zxf -],
+ in: "#{gem}-#{version}.gem",
+ chdir: vendor_dir
+ )
+
+ if !statuses.all?(&:success?)
+ abort "Failed to extract gem"
+ end
+end
+
+if ARGV.count != 3
+ abort "Usage: #{$PROGNAME} GEM VERSION VENDOR_DIR"
+end
+
+main(*ARGV)