redis 2.0.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. data/.gitignore +8 -0
  2. data/CHANGELOG.md +49 -0
  3. data/README.md +208 -0
  4. data/Rakefile +183 -73
  5. data/TODO.md +4 -0
  6. data/benchmarking/logging.rb +62 -0
  7. data/benchmarking/pipeline.rb +51 -0
  8. data/benchmarking/speed.rb +21 -0
  9. data/benchmarking/suite.rb +24 -0
  10. data/benchmarking/thread_safety.rb +38 -0
  11. data/benchmarking/worker.rb +71 -0
  12. data/examples/basic.rb +15 -0
  13. data/examples/dist_redis.rb +43 -0
  14. data/examples/incr-decr.rb +17 -0
  15. data/examples/list.rb +26 -0
  16. data/examples/pubsub.rb +31 -0
  17. data/examples/sets.rb +36 -0
  18. data/examples/unicorn/config.ru +3 -0
  19. data/examples/unicorn/unicorn.rb +20 -0
  20. data/lib/redis/client.rb +149 -165
  21. data/lib/redis/connection/command_helper.rb +45 -0
  22. data/lib/redis/connection/hiredis.rb +49 -0
  23. data/lib/redis/connection/registry.rb +12 -0
  24. data/lib/redis/connection/ruby.rb +135 -0
  25. data/lib/redis/connection/synchrony.rb +129 -0
  26. data/lib/redis/connection.rb +9 -0
  27. data/lib/redis/distributed.rb +182 -7
  28. data/lib/redis/pipeline.rb +22 -1
  29. data/lib/redis/subscribe.rb +19 -4
  30. data/lib/redis/version.rb +3 -0
  31. data/lib/redis.rb +715 -155
  32. data/redis.gemspec +24 -0
  33. data/test/commands_on_hashes_test.rb +32 -0
  34. data/test/commands_on_lists_test.rb +60 -0
  35. data/test/commands_on_sets_test.rb +78 -0
  36. data/test/commands_on_sorted_sets_test.rb +109 -0
  37. data/test/commands_on_strings_test.rb +80 -0
  38. data/test/commands_on_value_types_test.rb +88 -0
  39. data/test/connection_handling_test.rb +88 -0
  40. data/test/db/.gitignore +1 -0
  41. data/test/distributed_blocking_commands_test.rb +53 -0
  42. data/test/distributed_commands_on_hashes_test.rb +12 -0
  43. data/test/distributed_commands_on_lists_test.rb +24 -0
  44. data/test/distributed_commands_on_sets_test.rb +85 -0
  45. data/test/distributed_commands_on_strings_test.rb +50 -0
  46. data/test/distributed_commands_on_value_types_test.rb +73 -0
  47. data/test/distributed_commands_requiring_clustering_test.rb +148 -0
  48. data/test/distributed_connection_handling_test.rb +25 -0
  49. data/test/distributed_internals_test.rb +27 -0
  50. data/test/distributed_key_tags_test.rb +53 -0
  51. data/test/distributed_persistence_control_commands_test.rb +24 -0
  52. data/test/distributed_publish_subscribe_test.rb +101 -0
  53. data/test/distributed_remote_server_control_commands_test.rb +43 -0
  54. data/test/distributed_sorting_test.rb +21 -0
  55. data/test/distributed_test.rb +59 -0
  56. data/test/distributed_transactions_test.rb +34 -0
  57. data/test/encoding_test.rb +16 -0
  58. data/test/error_replies_test.rb +53 -0
  59. data/test/helper.rb +145 -0
  60. data/test/internals_test.rb +160 -0
  61. data/test/lint/hashes.rb +114 -0
  62. data/test/lint/internals.rb +37 -0
  63. data/test/lint/lists.rb +93 -0
  64. data/test/lint/sets.rb +66 -0
  65. data/test/lint/sorted_sets.rb +167 -0
  66. data/test/lint/strings.rb +137 -0
  67. data/test/lint/value_types.rb +84 -0
  68. data/test/persistence_control_commands_test.rb +22 -0
  69. data/test/pipelining_commands_test.rb +123 -0
  70. data/test/publish_subscribe_test.rb +158 -0
  71. data/test/redis_mock.rb +80 -0
  72. data/test/remote_server_control_commands_test.rb +82 -0
  73. data/test/sorting_test.rb +44 -0
  74. data/test/synchrony_driver.rb +57 -0
  75. data/test/test.conf +8 -0
  76. data/test/thread_safety_test.rb +30 -0
  77. data/test/transactions_test.rb +100 -0
  78. data/test/unknown_commands_test.rb +14 -0
  79. data/test/url_param_test.rb +60 -0
  80. metadata +130 -22
  81. data/README.markdown +0 -120
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ test "handle multiple servers" do
12
+ @r = Redis::Distributed.new ["redis://localhost:6379/15", *NODES]
13
+
14
+ 100.times do |idx|
15
+ @r.set(idx.to_s, "foo#{idx}")
16
+ end
17
+
18
+ 100.times do |idx|
19
+ assert "foo#{idx}" == @r.get(idx.to_s)
20
+ end
21
+
22
+ assert "0" == @r.keys("*").sort.first
23
+ assert "string" == @r.type("1")
24
+ end
25
+
26
+ test "add nodes" do
27
+ logger = Logger.new("/dev/null")
28
+
29
+ @r = Redis::Distributed.new NODES, :logger => logger, :timeout => 10
30
+
31
+ assert "127.0.0.1" == @r.nodes[0].client.host
32
+ assert 6379 == @r.nodes[0].client.port
33
+ assert 15 == @r.nodes[0].client.db
34
+ assert 10 == @r.nodes[0].client.timeout
35
+ assert logger == @r.nodes[0].client.logger
36
+
37
+ @r.add_node("redis://localhost:6380/14")
38
+
39
+ assert "localhost" == @r.nodes[1].client.host
40
+ assert 6380 == @r.nodes[1].client.port
41
+ assert 14 == @r.nodes[1].client.db
42
+ assert 10 == @r.nodes[1].client.timeout
43
+ assert logger == @r.nodes[1].client.logger
44
+ end
45
+
46
+ test "Pipelining commands cannot be distributed" do |r|
47
+ assert_raise Redis::Distributed::CannotDistribute do
48
+ r.pipelined do
49
+ r.lpush "foo", "s1"
50
+ r.lpush "foo", "s2"
51
+ end
52
+ end
53
+ end
54
+
55
+ test "Unknown commands does not work by default" do |r|
56
+ assert_raise NoMethodError do
57
+ r.not_yet_implemented_command
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "redis/distributed"
5
+
6
+ setup do
7
+ log = StringIO.new
8
+ init Redis::Distributed.new(NODES, :logger => ::Logger.new(log))
9
+ end
10
+
11
+ test "MULTI/DISCARD" do |r|
12
+ @foo = nil
13
+
14
+ assert_raise Redis::Distributed::CannotDistribute do
15
+ r.multi { @foo = 1 }
16
+ end
17
+
18
+ assert nil == @foo
19
+
20
+ assert_raise Redis::Distributed::CannotDistribute do
21
+ r.discard
22
+ end
23
+ end
24
+
25
+ test "WATCH/UNWATCH" do |r|
26
+ assert_raise Redis::Distributed::CannotDistribute do
27
+ r.watch("foo")
28
+ end
29
+
30
+ assert_raise Redis::Distributed::CannotDistribute do
31
+ r.unwatch
32
+ end
33
+ end
34
+
@@ -0,0 +1,16 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ setup do
6
+ init Redis.new(OPTIONS)
7
+ end
8
+
9
+ test "returns properly encoded strings" do |r|
10
+ with_external_encoding("UTF-8") do
11
+ r.set "foo", "שלום"
12
+
13
+ assert "Shalom שלום" == "Shalom " + r.get("foo")
14
+ end
15
+ end if defined?(Encoding)
16
+
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ setup do
6
+ init Redis.new(OPTIONS)
7
+ end
8
+
9
+ # Every test shouldn't disconnect from the server. Also, when error replies are
10
+ # in play, the protocol should never get into an invalid state where there are
11
+ # pending replies in the connection. Calling INFO after every test ensures that
12
+ # the protocol is still in a valid state.
13
+ def test_with_reconnection_check(title)
14
+ test(title) do |r|
15
+ before = r.info["total_connections_received"]
16
+ yield(r)
17
+ after = r.info["total_connections_received"]
18
+ assert before == after
19
+ end
20
+ end
21
+
22
+ test_with_reconnection_check "Error reply for single command" do |r|
23
+ begin
24
+ r.unknown_command
25
+ rescue => ex
26
+ ensure
27
+ assert ex.message =~ /unknown command/i
28
+ end
29
+ end
30
+
31
+ test_with_reconnection_check "Raise first error reply in pipeline" do |r|
32
+ begin
33
+ r.pipelined do
34
+ r.set("foo", "s1")
35
+ r.incr("foo") # not an integer
36
+ r.lpush("foo", "value") # wrong kind of value
37
+ end
38
+ rescue => ex
39
+ ensure
40
+ assert ex.message =~ /not an integer/i
41
+ end
42
+ end
43
+
44
+ test_with_reconnection_check "Recover from raise in #call_loop" do |r|
45
+ begin
46
+ r.client.call_loop([:invalid_monitor]) do
47
+ assert false # Should never be executed
48
+ end
49
+ rescue => ex
50
+ ensure
51
+ assert ex.message =~ /unknown command/i
52
+ end
53
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,145 @@
1
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ require "cutest"
4
+ require "logger"
5
+ require "stringio"
6
+
7
+ begin
8
+ require "ruby-debug"
9
+ rescue LoadError
10
+ end
11
+
12
+ PORT = 6379
13
+ OPTIONS = {:port => PORT, :db => 15, :timeout => 3}
14
+ NODES = ["redis://127.0.0.1:6379/15"]
15
+
16
+ def init(redis)
17
+ begin
18
+ redis.flushdb
19
+ redis.select 14
20
+ redis.flushdb
21
+ redis.select 15
22
+ redis
23
+ rescue Errno::ECONNREFUSED
24
+ puts <<-EOS
25
+
26
+ Cannot connect to Redis.
27
+
28
+ Make sure Redis is running on localhost, port 6379.
29
+ This testing suite connects to the database 15.
30
+
31
+ To install redis:
32
+ visit <http://code.google.com/p/redis/>.
33
+
34
+ To start the server:
35
+ rake start
36
+
37
+ To stop the server:
38
+ rake stop
39
+
40
+ EOS
41
+ exit 1
42
+ end
43
+ end
44
+
45
+ $VERBOSE = true
46
+
47
+ require "redis"
48
+
49
+ def driver
50
+ Redis::Connection.drivers.last.to_s.split("::").last.downcase.to_sym
51
+ end
52
+
53
+ if driver == :synchrony
54
+ # Make cutest fiber + eventmachine aware if the synchrony driver is used.
55
+ undef test if defined? test
56
+ def test(name = nil, &block)
57
+ cutest[:test] = name
58
+
59
+ blk = Proc.new do
60
+ prepare.each { |blk| blk.call }
61
+ block.call(setup && setup.call)
62
+ end
63
+
64
+ t = Thread.current[:cutest]
65
+ if defined? EventMachine
66
+ EM.synchrony do
67
+ Thread.current[:cutest] = t
68
+ blk.call
69
+ EM.stop
70
+ end
71
+ else
72
+ blk.call
73
+ end
74
+ end
75
+
76
+ class Wire < Fiber
77
+ # We cannot run this fiber explicitly because EM schedules it. Resuming the
78
+ # current fiber on the next tick to let the reactor do work.
79
+ def self.pass
80
+ f = Fiber.current
81
+ EM.next_tick { f.resume }
82
+ Fiber.yield
83
+ end
84
+
85
+ def self.sleep(sec)
86
+ EM::Synchrony.sleep(sec)
87
+ end
88
+
89
+ def initialize(&blk)
90
+ super
91
+
92
+ # Schedule run in next tick
93
+ EM.next_tick { resume }
94
+ end
95
+
96
+ def join
97
+ self.class.pass while alive?
98
+ end
99
+ end
100
+ else
101
+ class Wire < Thread
102
+ def self.sleep(sec)
103
+ Kernel.sleep(sec)
104
+ end
105
+ end
106
+ end
107
+
108
+ def capture_stderr
109
+ stderr = $stderr
110
+ $stderr = StringIO.new
111
+
112
+ yield
113
+
114
+ $stderr = stderr
115
+ end
116
+
117
+ def silent
118
+ verbose, $VERBOSE = $VERBOSE, false
119
+
120
+ begin
121
+ yield
122
+ ensure
123
+ $VERBOSE = verbose
124
+ end
125
+ end
126
+
127
+ def with_external_encoding(encoding)
128
+ original_encoding = Encoding.default_external
129
+
130
+ begin
131
+ silent { Encoding.default_external = Encoding.find(encoding) }
132
+ yield
133
+ ensure
134
+ silent { Encoding.default_external = original_encoding }
135
+ end
136
+ end
137
+
138
+ def assert_nothing_raised(*exceptions)
139
+ begin
140
+ yield
141
+ rescue *exceptions
142
+ flunk(caller[1])
143
+ end
144
+ end
145
+
@@ -0,0 +1,160 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require File.expand_path("./redis_mock", File.dirname(__FILE__))
5
+
6
+ include RedisMock::Helper
7
+
8
+ setup do
9
+ log = StringIO.new
10
+
11
+ [Redis.new(OPTIONS.merge(:logger => ::Logger.new(log))), log]
12
+ end
13
+
14
+ $TEST_PIPELINING = true
15
+
16
+ load File.expand_path("./lint/internals.rb", File.dirname(__FILE__))
17
+
18
+ test "provides a meaningful inspect" do |r, _|
19
+ assert "#<Redis client v#{Redis::VERSION} connected to redis://127.0.0.1:6379/15 (Redis v#{r.info["redis_version"]})>" == r.inspect
20
+ end
21
+
22
+ test "Redis.current" do
23
+ Redis.current.set("foo", "bar")
24
+
25
+ assert "bar" == Redis.current.get("foo")
26
+
27
+ Redis.current = Redis.new(OPTIONS.merge(:db => 14))
28
+
29
+ assert Redis.current.get("foo").nil?
30
+ end
31
+
32
+ test "Timeout" do
33
+ assert_nothing_raised do
34
+ Redis.new(OPTIONS.merge(:timeout => 0))
35
+ end
36
+ end
37
+
38
+ # Don't use assert_raise because Timeour::Error in 1.8 inherits
39
+ # Exception instead of StandardError (on 1.9).
40
+ test "Connection timeout" do
41
+ # EM immediately raises CONNREFUSED
42
+ next if driver == :synchrony
43
+
44
+ result = false
45
+
46
+ begin
47
+ Redis.new(OPTIONS.merge(:host => "10.255.255.254", :timeout => 0.1)).ping
48
+ rescue Timeout::Error
49
+ result = true
50
+ ensure
51
+ assert result
52
+ end
53
+ end
54
+
55
+ test "Retry when first read raises ECONNRESET" do
56
+ $request = 0
57
+
58
+ command = lambda do
59
+ case ($request += 1)
60
+ when 1; nil # Close on first command
61
+ else "+%d" % $request
62
+ end
63
+ end
64
+
65
+ redis_mock(:ping => command) do
66
+ redis = Redis.connect(:port => 6380, :timeout => 0.1)
67
+ assert "2" == redis.ping
68
+ end
69
+ end
70
+
71
+ test "Don't retry when wrapped inside #without_reconnect" do
72
+ $request = 0
73
+
74
+ command = lambda do
75
+ case ($request += 1)
76
+ when 1; nil # Close on first command
77
+ else "+%d" % $request
78
+ end
79
+ end
80
+
81
+ redis_mock(:ping => command) do
82
+ redis = Redis.connect(:port => 6380, :timeout => 0.1)
83
+ assert_raise Errno::ECONNRESET do
84
+ redis.without_reconnect do
85
+ redis.ping
86
+ end
87
+ end
88
+
89
+ assert !redis.client.connected?
90
+ end
91
+ end
92
+
93
+ test "Retry only once when read raises ECONNRESET" do
94
+ $request = 0
95
+
96
+ command = lambda do
97
+ case ($request += 1)
98
+ when 1; nil # Close on first command
99
+ when 2; nil # Close on second command
100
+ else "+%d" % $request
101
+ end
102
+ end
103
+
104
+ redis_mock(:ping => command) do
105
+ redis = Redis.connect(:port => 6380, :timeout => 0.1)
106
+ assert_raise Errno::ECONNRESET do
107
+ redis.ping
108
+ end
109
+
110
+ assert !redis.client.connected?
111
+ end
112
+ end
113
+
114
+ test "Don't retry when second read in pipeline raises ECONNRESET" do
115
+ $request = 0
116
+
117
+ command = lambda do
118
+ case ($request += 1)
119
+ when 2; nil # Close on second command
120
+ else "+%d" % $request
121
+ end
122
+ end
123
+
124
+ redis_mock(:ping => command) do
125
+ redis = Redis.connect(:port => 6380, :timeout => 0.1)
126
+ assert_raise Errno::ECONNRESET do
127
+ redis.pipelined do
128
+ redis.ping
129
+ redis.ping # Second #read times out
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ test "Connecting to UNIX domain socket" do
136
+ assert_nothing_raised do
137
+ Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock")).ping
138
+ end
139
+ end
140
+
141
+ # Using a mock server in a thread doesn't work here (possibly because blocking
142
+ # socket ops, raw socket timeouts and Ruby's thread scheduling don't mix).
143
+ test "Bubble EAGAIN without retrying" do
144
+ cmd = %{(sleep 0.3; echo "+PONG\r\n") | nc -l 6380}
145
+ IO.popen(cmd) do |_|
146
+ sleep 0.1 # Give nc a little time to start listening
147
+ redis = Redis.connect(:port => 6380, :timeout => 0.1)
148
+
149
+ begin
150
+ assert_raise(Errno::EAGAIN) { redis.ping }
151
+ ensure
152
+ # Explicitly close connection so nc can quit
153
+ redis.client.disconnect
154
+
155
+ # Make the reactor loop do a tick to really close
156
+ EM::Synchrony.sleep(0) if driver == :synchrony
157
+ end
158
+ end
159
+ end
160
+
@@ -0,0 +1,114 @@
1
+ test "HSET and HGET" do |r|
2
+ r.hset("foo", "f1", "s1")
3
+
4
+ assert "s1" == r.hget("foo", "f1")
5
+ end
6
+
7
+ test "HDEL" do |r|
8
+ r.hset("foo", "f1", "s1")
9
+
10
+ assert "s1" == r.hget("foo", "f1")
11
+
12
+ r.hdel("foo", "f1")
13
+
14
+ assert nil == r.hget("foo", "f1")
15
+ end
16
+
17
+ test "HEXISTS" do |r|
18
+ assert false == r.hexists("foo", "f1")
19
+
20
+ r.hset("foo", "f1", "s1")
21
+
22
+ assert r.hexists("foo", "f1")
23
+ end
24
+
25
+ test "HLEN" do |r|
26
+ assert 0 == r.hlen("foo")
27
+
28
+ r.hset("foo", "f1", "s1")
29
+
30
+ assert 1 == r.hlen("foo")
31
+
32
+ r.hset("foo", "f2", "s2")
33
+
34
+ assert 2 == r.hlen("foo")
35
+ end
36
+
37
+ test "HKEYS" do |r|
38
+ assert [] == r.hkeys("foo")
39
+
40
+ r.hset("foo", "f1", "s1")
41
+ r.hset("foo", "f2", "s2")
42
+
43
+ assert ["f1", "f2"] == r.hkeys("foo")
44
+ end
45
+
46
+ test "HVALS" do |r|
47
+ assert [] == r.hvals("foo")
48
+
49
+ r.hset("foo", "f1", "s1")
50
+ r.hset("foo", "f2", "s2")
51
+
52
+ assert ["s1", "s2"] == r.hvals("foo")
53
+ end
54
+
55
+ test "HGETALL" do |r|
56
+ assert({} == r.hgetall("foo"))
57
+
58
+ r.hset("foo", "f1", "s1")
59
+ r.hset("foo", "f2", "s2")
60
+
61
+ assert({"f1" => "s1", "f2" => "s2"} == r.hgetall("foo"))
62
+ end
63
+
64
+ test "HMSET" do |r|
65
+ r.hmset("hash", "foo1", "bar1", "foo2", "bar2")
66
+
67
+ assert "bar1" == r.hget("hash", "foo1")
68
+ assert "bar2" == r.hget("hash", "foo2")
69
+ end
70
+
71
+ test "HMSET with invalid arguments" do |r|
72
+ assert_raise(RuntimeError) do
73
+ r.hmset("hash", "foo1", "bar1", "foo2", "bar2", "foo3")
74
+ end
75
+ end
76
+
77
+ test "Mapped HMSET" do |r|
78
+ r.mapped_hmset("foo", :f1 => "s1", :f2 => "s2")
79
+
80
+ assert "s1" == r.hget("foo", "f1")
81
+ assert "s2" == r.hget("foo", "f2")
82
+ end
83
+
84
+ test "HMGET" do |r|
85
+ r.hset("foo", "f1", "s1")
86
+ r.hset("foo", "f2", "s2")
87
+ r.hset("foo", "f3", "s3")
88
+
89
+ assert ["s2", "s3"] == r.hmget("foo", "f2", "f3")
90
+ end
91
+
92
+ test "HMGET mapped" do |r|
93
+ r.hset("foo", "f1", "s1")
94
+ r.hset("foo", "f2", "s2")
95
+ r.hset("foo", "f3", "s3")
96
+
97
+ assert({"f1" => "s1"} == r.mapped_hmget("foo", "f1"))
98
+ assert({"f1" => "s1", "f2" => "s2"} == r.mapped_hmget("foo", "f1", "f2"))
99
+ end
100
+
101
+ test "HINCRBY" do |r|
102
+ r.hincrby("foo", "f1", 1)
103
+
104
+ assert "1" == r.hget("foo", "f1")
105
+
106
+ r.hincrby("foo", "f1", 2)
107
+
108
+ assert "3" == r.hget("foo", "f1")
109
+
110
+ r.hincrby("foo", "f1", -1)
111
+
112
+ assert "2" == r.hget("foo", "f1")
113
+ end
114
+
@@ -0,0 +1,37 @@
1
+ test "Logger" do |r, log|
2
+ r.ping
3
+
4
+ assert log.string =~ /Redis >> PING/
5
+ assert log.string =~ /Redis >> \d+\.\d+ms/
6
+ end
7
+
8
+ test "Logger with pipelining" do |r, log|
9
+ r.pipelined do
10
+ r.set "foo", "bar"
11
+ r.get "foo"
12
+ end
13
+
14
+ assert log.string["SET foo bar"]
15
+ assert log.string["GET foo"]
16
+ end if $TEST_PIPELINING
17
+
18
+ test "Recovers from failed commands" do |r, _|
19
+ # See http://github.com/ezmobius/redis-rb/issues#issue/28
20
+
21
+ assert_raise(ArgumentError) do
22
+ r.srem "foo"
23
+ end
24
+
25
+ assert_nothing_raised do
26
+ r.info
27
+ end
28
+ end
29
+
30
+ test "raises on protocol errors" do
31
+ redis_mock(:ping => lambda { |*_| "foo" }) do
32
+ assert_raise(Redis::ProtocolError) do
33
+ Redis.connect(:port => 6380).ping
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,93 @@
1
+ test "RPUSH" do |r|
2
+ r.rpush "foo", "s1"
3
+ r.rpush "foo", "s2"
4
+
5
+ assert 2 == r.llen("foo")
6
+ assert "s2" == r.rpop("foo")
7
+ end
8
+
9
+ test "LPUSH" do |r|
10
+ r.lpush "foo", "s1"
11
+ r.lpush "foo", "s2"
12
+
13
+ assert 2 == r.llen("foo")
14
+ assert "s2" == r.lpop("foo")
15
+ end
16
+
17
+ test "LLEN" do |r|
18
+ r.rpush "foo", "s1"
19
+ r.rpush "foo", "s2"
20
+
21
+ assert 2 == r.llen("foo")
22
+ end
23
+
24
+ test "LRANGE" do |r|
25
+ r.rpush "foo", "s1"
26
+ r.rpush "foo", "s2"
27
+ r.rpush "foo", "s3"
28
+
29
+ assert ["s2", "s3"] == r.lrange("foo", 1, -1)
30
+ assert ["s1", "s2"] == r.lrange("foo", 0, 1)
31
+
32
+ assert [] == r.lrange("bar", 0, -1)
33
+ end
34
+
35
+ test "LTRIM" do |r|
36
+ r.rpush "foo", "s1"
37
+ r.rpush "foo", "s2"
38
+ r.rpush "foo", "s3"
39
+
40
+ r.ltrim "foo", 0, 1
41
+
42
+ assert 2 == r.llen("foo")
43
+ assert ["s1", "s2"] == r.lrange("foo", 0, -1)
44
+ end
45
+
46
+ test "LINDEX" do |r|
47
+ r.rpush "foo", "s1"
48
+ r.rpush "foo", "s2"
49
+
50
+ assert "s1" == r.lindex("foo", 0)
51
+ assert "s2" == r.lindex("foo", 1)
52
+ end
53
+
54
+ test "LSET" do |r|
55
+ r.rpush "foo", "s1"
56
+ r.rpush "foo", "s2"
57
+
58
+ assert "s2" == r.lindex("foo", 1)
59
+ assert r.lset("foo", 1, "s3")
60
+ assert "s3" == r.lindex("foo", 1)
61
+
62
+ assert_raise RuntimeError do
63
+ r.lset("foo", 4, "s3")
64
+ end
65
+ end
66
+
67
+ test "LREM" do |r|
68
+ r.rpush "foo", "s1"
69
+ r.rpush "foo", "s2"
70
+
71
+ assert 1 == r.lrem("foo", 1, "s1")
72
+ assert ["s2"] == r.lrange("foo", 0, -1)
73
+ end
74
+
75
+ test "LPOP" do |r|
76
+ r.rpush "foo", "s1"
77
+ r.rpush "foo", "s2"
78
+
79
+ assert 2 == r.llen("foo")
80
+ assert "s1" == r.lpop("foo")
81
+ assert 1 == r.llen("foo")
82
+ end
83
+
84
+ test "RPOP" do |r|
85
+ r.rpush "foo", "s1"
86
+ r.rpush "foo", "s2"
87
+
88
+ assert 2 == r.llen("foo")
89
+ assert "s2" == r.rpop("foo")
90
+ assert 1 == r.llen("foo")
91
+ end
92
+
93
+