redis 3.0.0.rc1 → 3.0.0.rc2

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 (77) hide show
  1. data/.travis.yml +50 -0
  2. data/.travis/Gemfile +11 -0
  3. data/CHANGELOG.md +47 -19
  4. data/README.md +160 -149
  5. data/Rakefile +15 -50
  6. data/examples/pubsub.rb +1 -1
  7. data/examples/unicorn/config.ru +1 -1
  8. data/examples/unicorn/unicorn.rb +1 -1
  9. data/lib/redis.rb +790 -390
  10. data/lib/redis/client.rb +137 -49
  11. data/lib/redis/connection/hiredis.rb +26 -15
  12. data/lib/redis/connection/ruby.rb +170 -53
  13. data/lib/redis/connection/synchrony.rb +23 -35
  14. data/lib/redis/distributed.rb +92 -32
  15. data/lib/redis/errors.rb +4 -2
  16. data/lib/redis/pipeline.rb +17 -6
  17. data/lib/redis/version.rb +1 -1
  18. data/redis.gemspec +4 -6
  19. data/test/blocking_commands_test.rb +42 -0
  20. data/test/command_map_test.rb +18 -17
  21. data/test/commands_on_hashes_test.rb +13 -12
  22. data/test/commands_on_lists_test.rb +35 -45
  23. data/test/commands_on_sets_test.rb +55 -54
  24. data/test/commands_on_sorted_sets_test.rb +106 -105
  25. data/test/commands_on_strings_test.rb +64 -55
  26. data/test/commands_on_value_types_test.rb +66 -54
  27. data/test/connection_handling_test.rb +136 -151
  28. data/test/distributed_blocking_commands_test.rb +33 -40
  29. data/test/distributed_commands_on_hashes_test.rb +6 -7
  30. data/test/distributed_commands_on_lists_test.rb +13 -14
  31. data/test/distributed_commands_on_sets_test.rb +57 -58
  32. data/test/distributed_commands_on_sorted_sets_test.rb +11 -12
  33. data/test/distributed_commands_on_strings_test.rb +31 -32
  34. data/test/distributed_commands_on_value_types_test.rb +61 -46
  35. data/test/distributed_commands_requiring_clustering_test.rb +108 -108
  36. data/test/distributed_connection_handling_test.rb +14 -15
  37. data/test/distributed_internals_test.rb +7 -19
  38. data/test/distributed_key_tags_test.rb +36 -36
  39. data/test/distributed_persistence_control_commands_test.rb +17 -14
  40. data/test/distributed_publish_subscribe_test.rb +61 -69
  41. data/test/distributed_remote_server_control_commands_test.rb +39 -28
  42. data/test/distributed_sorting_test.rb +12 -13
  43. data/test/distributed_test.rb +40 -41
  44. data/test/distributed_transactions_test.rb +20 -21
  45. data/test/encoding_test.rb +12 -9
  46. data/test/error_replies_test.rb +42 -36
  47. data/test/helper.rb +118 -85
  48. data/test/helper_test.rb +20 -6
  49. data/test/internals_test.rb +167 -103
  50. data/test/lint/blocking_commands.rb +124 -0
  51. data/test/lint/hashes.rb +115 -93
  52. data/test/lint/lists.rb +86 -80
  53. data/test/lint/sets.rb +68 -62
  54. data/test/lint/sorted_sets.rb +200 -195
  55. data/test/lint/strings.rb +112 -94
  56. data/test/lint/value_types.rb +76 -55
  57. data/test/persistence_control_commands_test.rb +17 -12
  58. data/test/pipelining_commands_test.rb +135 -126
  59. data/test/publish_subscribe_test.rb +105 -110
  60. data/test/remote_server_control_commands_test.rb +74 -58
  61. data/test/sorting_test.rb +31 -29
  62. data/test/support/connection/hiredis.rb +1 -0
  63. data/test/support/connection/ruby.rb +1 -0
  64. data/test/support/connection/synchrony.rb +17 -0
  65. data/test/{redis_mock.rb → support/redis_mock.rb} +24 -21
  66. data/test/support/wire/synchrony.rb +24 -0
  67. data/test/support/wire/thread.rb +5 -0
  68. data/test/synchrony_driver.rb +9 -9
  69. data/test/test.conf +1 -1
  70. data/test/thread_safety_test.rb +21 -19
  71. data/test/transactions_test.rb +189 -118
  72. data/test/unknown_commands_test.rb +9 -8
  73. data/test/url_param_test.rb +46 -41
  74. metadata +28 -43
  75. data/TODO.md +0 -4
  76. data/benchmarking/thread_safety.rb +0 -38
  77. data/test/lint/internals.rb +0 -36
@@ -1,53 +1,59 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require "helper"
4
4
 
5
- setup do
6
- init Redis.new(OPTIONS)
7
- end
5
+ class TestErrorReplies < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
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|
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 with_reconnection_check
15
14
  before = r.info["total_connections_received"]
16
15
  yield(r)
17
16
  after = r.info["total_connections_received"]
18
- assert before == after
17
+ ensure
18
+ assert_equal before, after
19
19
  end
20
- end
21
20
 
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
21
+ def test_error_reply_for_single_command
22
+ with_reconnection_check do
23
+ begin
24
+ r.unknown_command
25
+ rescue => ex
26
+ ensure
27
+ assert ex.message =~ /unknown command/i
28
+ end
29
+ end
28
30
  end
29
- end
30
31
 
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
32
+ def test_raise_first_error_reply_in_pipeline
33
+ with_reconnection_check do
34
+ begin
35
+ r.pipelined do
36
+ r.set("foo", "s1")
37
+ r.incr("foo") # not an integer
38
+ r.lpush("foo", "value") # wrong kind of value
39
+ end
40
+ rescue => ex
41
+ ensure
42
+ assert ex.message =~ /not an integer/i
43
+ end
37
44
  end
38
- rescue => ex
39
- ensure
40
- assert ex.message =~ /not an integer/i
41
45
  end
42
- end
43
46
 
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
47
+ def test_recover_from_raise_in__call_loop
48
+ with_reconnection_check do
49
+ begin
50
+ r.client.call_loop([:invalid_monitor]) do
51
+ assert false # Should never be executed
52
+ end
53
+ rescue => ex
54
+ ensure
55
+ assert ex.message =~ /unknown command/i
56
+ end
48
57
  end
49
- rescue => ex
50
- ensure
51
- assert ex.message =~ /unknown command/i
52
58
  end
53
59
  end
@@ -1,6 +1,6 @@
1
1
  $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2
2
 
3
- #require "cutest"
3
+ require "test/unit"
4
4
  require "logger"
5
5
  require "stringio"
6
6
 
@@ -9,23 +9,34 @@ begin
9
9
  rescue LoadError
10
10
  end
11
11
 
12
- PORT = 6379
13
- OPTIONS = {:port => PORT, :db => 15, :timeout => 3}
14
- NODES = ["redis://127.0.0.1:6379/15"]
12
+ $VERBOSE = true
13
+
14
+ ENV["conn"] ||= "ruby"
15
+
16
+ require "redis"
17
+ require "redis/distributed"
18
+ require "redis/connection/#{ENV["conn"]}"
19
+
20
+ require "support/redis_mock"
21
+ require "support/connection/#{ENV["conn"]}"
22
+
23
+ PORT = 6381
24
+ OPTIONS = {:port => PORT, :db => 15, :timeout => 0.1}
25
+ NODES = ["redis://127.0.0.1:#{PORT}/15"]
15
26
 
16
27
  def init(redis)
17
28
  begin
18
- redis.flushdb
19
29
  redis.select 14
20
30
  redis.flushdb
21
31
  redis.select 15
32
+ redis.flushdb
22
33
  redis
23
34
  rescue Redis::CannotConnectError
24
35
  puts <<-EOS
25
36
 
26
37
  Cannot connect to Redis.
27
38
 
28
- Make sure Redis is running on localhost, port 6379.
39
+ Make sure Redis is running on localhost, port #{PORT}.
29
40
  This testing suite connects to the database 15.
30
41
 
31
42
  To install redis:
@@ -42,114 +53,136 @@ def init(redis)
42
53
  end
43
54
  end
44
55
 
45
- $VERBOSE = true
56
+ def driver(*drivers, &blk)
57
+ if drivers.map(&:to_s).include?(ENV["conn"])
58
+ class_eval(&blk)
59
+ end
60
+ end
46
61
 
47
- require "redis/connection/%s" % (ENV["REDIS_CONNECTION_DRIVER"] || "ruby")
48
- require "redis"
62
+ module Helper
49
63
 
50
- def driver
51
- Redis::Connection.drivers.last.to_s.split("::").last.downcase.to_sym
52
- end
64
+ def run(runner)
65
+ if respond_to?(:around)
66
+ around { super(runner) }
67
+ else
68
+ super
69
+ end
70
+ end
53
71
 
54
- if driver == :synchrony
55
- # Make cutest fiber + eventmachine aware if the synchrony driver is used.
56
- undef test if defined? test
57
- def test(name = nil, &block)
58
- cutest[:test] = name
72
+ def silent
73
+ verbose, $VERBOSE = $VERBOSE, false
59
74
 
60
- blk = Proc.new do
61
- prepare.each { |blk| blk.call }
62
- block.call(setup && setup.call)
75
+ begin
76
+ yield
77
+ ensure
78
+ $VERBOSE = verbose
63
79
  end
80
+ end
64
81
 
65
- t = Thread.current[:cutest]
66
- if defined? EventMachine
67
- EM.synchrony do
68
- Thread.current[:cutest] = t
69
- blk.call
70
- EM.stop
71
- end
72
- else
73
- blk.call
82
+ def with_external_encoding(encoding)
83
+ original_encoding = Encoding.default_external
84
+
85
+ begin
86
+ silent { Encoding.default_external = Encoding.find(encoding) }
87
+ yield
88
+ ensure
89
+ silent { Encoding.default_external = original_encoding }
74
90
  end
75
91
  end
76
92
 
77
- class Wire < Fiber
78
- # We cannot run this fiber explicitly because EM schedules it. Resuming the
79
- # current fiber on the next tick to let the reactor do work.
80
- def self.pass
81
- f = Fiber.current
82
- EM.next_tick { f.resume }
83
- Fiber.yield
93
+ class Version
94
+
95
+ include Comparable
96
+
97
+ attr :parts
98
+
99
+ def initialize(v)
100
+ case v
101
+ when Version
102
+ @parts = v.parts
103
+ else
104
+ @parts = v.to_s.split(".")
105
+ end
84
106
  end
85
107
 
86
- def self.sleep(sec)
87
- EM::Synchrony.sleep(sec)
108
+ def <=>(other)
109
+ other = Version.new(other)
110
+ length = [self.parts.length, other.parts.length].max
111
+ length.times do |i|
112
+ a, b = self.parts[i], other.parts[i]
113
+
114
+ return -1 if a.nil?
115
+ return +1 if b.nil?
116
+ return a <=> b if a != b
117
+ end
118
+
119
+ 0
88
120
  end
121
+ end
89
122
 
90
- def initialize(&blk)
91
- super
123
+ module Generic
124
+
125
+ include Helper
92
126
 
93
- # Schedule run in next tick
94
- EM.next_tick { resume }
127
+ attr_reader :log
128
+ attr_reader :redis
129
+
130
+ alias :r :redis
131
+
132
+ def setup
133
+ @log = StringIO.new
134
+ @redis = init _new_client
95
135
  end
96
136
 
97
- def join
98
- self.class.pass while alive?
137
+ def teardown
138
+ @redis.quit if @redis
99
139
  end
100
- end
101
- else
102
- class Wire < Thread
103
- def self.sleep(sec)
104
- Kernel.sleep(sec)
140
+
141
+ def redis_mock(commands, options = {}, &blk)
142
+ RedisMock.start(commands) do |port|
143
+ yield _new_client(options.merge(:port => port))
144
+ end
105
145
  end
106
146
  end
107
- end
108
147
 
109
- def capture_stderr
110
- stderr = $stderr
111
- $stderr = StringIO.new
148
+ module Client
112
149
 
113
- yield
150
+ include Generic
114
151
 
115
- $stderr = stderr
116
- end
152
+ def version
153
+ Version.new(redis.info["redis_version"])
154
+ end
117
155
 
118
- def silent
119
- verbose, $VERBOSE = $VERBOSE, false
156
+ private
120
157
 
121
- begin
122
- yield
123
- ensure
124
- $VERBOSE = verbose
158
+ def _format_options(options)
159
+ OPTIONS.merge(:logger => ::Logger.new(@log)).merge(options)
160
+ end
161
+
162
+ def _new_client(options = {})
163
+ Redis.new(_format_options(options))
164
+ end
125
165
  end
126
- end
127
166
 
128
- def version(r)
129
- info = r.info
130
- info = info.first unless info.is_a?(Hash)
131
- version_str_to_i info["redis_version"]
132
- end
167
+ module Distributed
133
168
 
134
- def version_str_to_i(version_str)
135
- version_str.split(".").map{ |v| v.ljust(2, '0') }.join.to_i
136
- end
169
+ include Generic
137
170
 
138
- def with_external_encoding(encoding)
139
- original_encoding = Encoding.default_external
171
+ def version
172
+ Version.new(redis.info.first["redis_version"])
173
+ end
140
174
 
141
- begin
142
- silent { Encoding.default_external = Encoding.find(encoding) }
143
- yield
144
- ensure
145
- silent { Encoding.default_external = original_encoding }
146
- end
147
- end
175
+ private
148
176
 
149
- def assert_nothing_raised(*exceptions)
150
- begin
151
- yield
152
- rescue *exceptions
153
- flunk(caller[1])
177
+ def _format_options(options)
178
+ {
179
+ :timeout => OPTIONS[:timeout],
180
+ :logger => ::Logger.new(@log),
181
+ }.merge(options)
182
+ end
183
+
184
+ def _new_client(options = {})
185
+ Redis::Distributed.new(NODES, _format_options(options))
186
+ end
154
187
  end
155
188
  end
@@ -1,8 +1,22 @@
1
- require File.expand_path("./helper", File.dirname(__FILE__))
1
+ # encoding: UTF-8
2
2
 
3
- test "version_str_to_i" do
4
- assert_equal 200000, version_str_to_i('2.0.0')
5
- assert_equal 202020, version_str_to_i('2.2.2')
6
- assert_equal 202022, version_str_to_i('2.2.22')
7
- assert_equal 222222, version_str_to_i('22.22.22')
3
+ require "helper"
4
+
5
+ class TestHelper < Test::Unit::TestCase
6
+
7
+ include Helper
8
+
9
+ def test_version_comparison
10
+ v = Version.new("2.0.0")
11
+
12
+ assert v < "3"
13
+ assert v > "1"
14
+ assert v > "2"
15
+
16
+ assert v < "2.1"
17
+ assert v < "2.0.1"
18
+ assert v < "2.0.0.1"
19
+
20
+ assert v == "2.0.0"
21
+ end
8
22
  end
@@ -1,152 +1,216 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
4
- require File.expand_path("./redis_mock", File.dirname(__FILE__))
3
+ require "helper"
5
4
 
6
- include RedisMock::Helper
5
+ class TestInternals < Test::Unit::TestCase
7
6
 
8
- setup do
9
- log = StringIO.new
7
+ include Helper::Client
10
8
 
11
- [Redis.new(OPTIONS.merge(:logger => ::Logger.new(log))), log]
12
- end
9
+ def test_logger
10
+ r.ping
13
11
 
14
- $TEST_PIPELINING = true
12
+ assert log.string =~ /Redis >> PING/
13
+ assert log.string =~ /Redis >> \d+\.\d+ms/
14
+ end
15
15
 
16
- load File.expand_path("./lint/internals.rb", File.dirname(__FILE__))
16
+ def test_logger_with_pipelining
17
+ r.pipelined do
18
+ r.set "foo", "bar"
19
+ r.get "foo"
20
+ end
17
21
 
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
22
+ assert log.string["SET foo bar"]
23
+ assert log.string["GET foo"]
24
+ end
21
25
 
22
- test "Redis.current" do
23
- Redis.current.set("foo", "bar")
26
+ def test_recovers_from_failed_commands
27
+ # See https://github.com/redis/redis-rb/issues#issue/28
24
28
 
25
- assert "bar" == Redis.current.get("foo")
29
+ assert_raise(Redis::CommandError) do
30
+ r.command_that_doesnt_exist
31
+ end
26
32
 
27
- Redis.current = Redis.new(OPTIONS.merge(:db => 14))
33
+ assert_nothing_raised do
34
+ r.info
35
+ end
36
+ end
28
37
 
29
- assert Redis.current.get("foo").nil?
30
- end
38
+ def test_raises_on_protocol_errors
39
+ redis_mock(:ping => lambda { |*_| "foo" }) do |redis|
40
+ assert_raise(Redis::ProtocolError) do
41
+ redis.ping
42
+ end
43
+ end
44
+ end
31
45
 
32
- test "Timeout" do
33
- assert_nothing_raised do
34
- Redis.new(OPTIONS.merge(:timeout => 0))
46
+ def test_provides_a_meaningful_inspect
47
+ assert_equal "#<Redis client v#{Redis::VERSION} for redis://127.0.0.1:#{PORT}/15>", r.inspect
35
48
  end
36
- end
37
49
 
38
- test "Connection timeout" do
39
- next if driver == :synchrony
50
+ def test_redis_current
51
+ assert_equal "127.0.0.1", Redis.current.client.host
52
+ assert_equal 6379, Redis.current.client.port
53
+ assert_equal 0, Redis.current.client.db
54
+
55
+ Redis.current = Redis.new(OPTIONS.merge(:port => 6380, :db => 1))
56
+
57
+ t = Thread.new do
58
+ assert_equal "127.0.0.1", Redis.current.client.host
59
+ assert_equal 6380, Redis.current.client.port
60
+ assert_equal 1, Redis.current.client.db
61
+ end
62
+
63
+ t.join
40
64
 
41
- assert_raise Redis::CannotConnectError do
42
- Redis.new(OPTIONS.merge(:host => "10.255.255.254", :timeout => 0.1)).ping
65
+ assert_equal "127.0.0.1", Redis.current.client.host
66
+ assert_equal 6380, Redis.current.client.port
67
+ assert_equal 1, Redis.current.client.db
68
+ end
69
+
70
+ def test_default_id_with_host_and_port
71
+ redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0))
72
+ assert_equal "redis://host:1234/0", redis.client.id
43
73
  end
44
- end
45
74
 
46
- test "Retry when first read raises ECONNRESET" do
47
- $request = 0
75
+ def test_default_id_with_host_and_port_and_explicit_scheme
76
+ redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0, :scheme => "foo"))
77
+ assert_equal "redis://host:1234/0", redis.client.id
78
+ end
48
79
 
49
- command = lambda do
50
- case ($request += 1)
51
- when 1; nil # Close on first command
52
- else "+%d" % $request
53
- end
80
+ def test_default_id_with_path
81
+ redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0))
82
+ assert_equal "redis:///tmp/redis.sock/0", redis.client.id
54
83
  end
55
84
 
56
- redis_mock(:ping => command) do
57
- redis = Redis.connect(:port => 6380, :timeout => 0.1)
58
- assert "2" == redis.ping
85
+ def test_default_id_with_path_and_explicit_scheme
86
+ redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0, :scheme => "foo"))
87
+ assert_equal "redis:///tmp/redis.sock/0", redis.client.id
59
88
  end
60
- end
61
89
 
62
- test "Don't retry when wrapped inside #without_reconnect" do
63
- $request = 0
90
+ def test_override_id
91
+ redis = Redis.new(OPTIONS.merge(:id => "test"))
92
+ assert_equal redis.client.id, "test"
93
+ end
64
94
 
65
- command = lambda do
66
- case ($request += 1)
67
- when 1; nil # Close on first command
68
- else "+%d" % $request
95
+ def test_timeout
96
+ assert_nothing_raised do
97
+ Redis.new(OPTIONS.merge(:timeout => 0))
69
98
  end
70
99
  end
71
100
 
72
- redis_mock(:ping => command) do
73
- redis = Redis.connect(:port => 6380, :timeout => 0.1)
74
- assert_raise Redis::ConnectionError do
75
- redis.without_reconnect do
76
- redis.ping
101
+ def test_time
102
+ return if version < "2.5.4"
103
+
104
+ # Test that the difference between the time that Ruby reports and the time
105
+ # that Redis reports is minimal (prevents the test from being racy).
106
+ rv = r.time
107
+
108
+ redis_usec = rv[0] * 1_000_000 + rv[1]
109
+ ruby_usec = Integer(Time.now.to_f * 1_000_000)
110
+
111
+ assert 500_000 > (ruby_usec - redis_usec).abs
112
+ end
113
+
114
+ def test_connection_timeout
115
+ assert_raise Redis::CannotConnectError do
116
+ Redis.new(OPTIONS.merge(:host => "10.255.255.254", :timeout => 0.1)).ping
117
+ end
118
+ end
119
+
120
+ def test_retry_when_first_read_raises_econnreset
121
+ $request = 0
122
+
123
+ command = lambda do
124
+ case ($request += 1)
125
+ when 1; nil # Close on first command
126
+ else "+%d" % $request
77
127
  end
78
128
  end
79
129
 
80
- assert !redis.client.connected?
130
+ redis_mock(:ping => command, :timeout => 0.1) do |redis|
131
+ assert_equal "2", redis.ping
132
+ end
81
133
  end
82
- end
83
134
 
84
- test "Retry only once when read raises ECONNRESET" do
85
- $request = 0
135
+ def test_don_t_retry_when_wrapped_inside__without_reconnect
136
+ $request = 0
137
+
138
+ command = lambda do
139
+ case ($request += 1)
140
+ when 1; nil # Close on first command
141
+ else "+%d" % $request
142
+ end
143
+ end
144
+
145
+ redis_mock(:ping => command, :timeout => 0.1) do |redis|
146
+ assert_raise Redis::ConnectionError do
147
+ redis.without_reconnect do
148
+ redis.ping
149
+ end
150
+ end
86
151
 
87
- command = lambda do
88
- case ($request += 1)
89
- when 1; nil # Close on first command
90
- when 2; nil # Close on second command
91
- else "+%d" % $request
152
+ assert !redis.client.connected?
92
153
  end
93
154
  end
94
155
 
95
- redis_mock(:ping => command) do
96
- redis = Redis.connect(:port => 6380, :timeout => 0.1)
97
- assert_raise Redis::ConnectionError do
98
- redis.ping
156
+ def test_retry_only_once_when_read_raises_econnreset
157
+ $request = 0
158
+
159
+ command = lambda do
160
+ case ($request += 1)
161
+ when 1; nil # Close on first command
162
+ when 2; nil # Close on second command
163
+ else "+%d" % $request
164
+ end
99
165
  end
100
166
 
101
- assert !redis.client.connected?
167
+ redis_mock(:ping => command, :timeout => 0.1) do |redis|
168
+ assert_raise Redis::ConnectionError do
169
+ redis.ping
170
+ end
171
+
172
+ assert !redis.client.connected?
173
+ end
102
174
  end
103
- end
104
175
 
105
- test "Don't retry when second read in pipeline raises ECONNRESET" do
106
- $request = 0
176
+ def test_don_t_retry_when_second_read_in_pipeline_raises_econnreset
177
+ $request = 0
107
178
 
108
- command = lambda do
109
- case ($request += 1)
110
- when 2; nil # Close on second command
111
- else "+%d" % $request
179
+ command = lambda do
180
+ case ($request += 1)
181
+ when 2; nil # Close on second command
182
+ else "+%d" % $request
183
+ end
112
184
  end
113
- end
114
185
 
115
- redis_mock(:ping => command) do
116
- redis = Redis.connect(:port => 6380, :timeout => 0.1)
117
- assert_raise Redis::ConnectionError do
118
- redis.pipelined do
119
- redis.ping
120
- redis.ping # Second #read times out
186
+ redis_mock(:ping => command, :timeout => 0.1) do |redis|
187
+ assert_raise Redis::ConnectionError do
188
+ redis.pipelined do
189
+ redis.ping
190
+ redis.ping # Second #read times out
191
+ end
121
192
  end
122
193
  end
123
194
  end
124
- end
125
195
 
126
- test "Connecting to UNIX domain socket" do
127
- assert_nothing_raised do
128
- Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock")).ping
196
+ def test_connecting_to_unix_domain_socket
197
+ assert_nothing_raised do
198
+ Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock")).ping
199
+ end
129
200
  end
130
- end
131
201
 
132
- # if driver == :ruby || driver == :hiredis
133
- # # Using a mock server in a thread doesn't work here (possibly because blocking
134
- # # socket ops, raw socket timeouts and Ruby's thread scheduling don't mix).
135
- # test "Bubble EAGAIN without retrying" do
136
- # cmd = %{(sleep 0.3; echo "+PONG\r\n") | nc -l 6380}
137
- # IO.popen(cmd) do |_|
138
- # sleep 0.1 # Give nc a little time to start listening
139
- # redis = Redis.connect(:port => 6380, :timeout => 0.1)
140
- #
141
- # begin
142
- # assert_raise(Errno::EAGAIN) { redis.ping }
143
- # ensure
144
- # # Explicitly close connection so nc can quit
145
- # redis.client.disconnect
146
- #
147
- # # Make the reactor loop do a tick to really close
148
- # EM::Synchrony.sleep(0) if driver == :synchrony
149
- # end
150
- # end
151
- # end
152
- # end
202
+ driver(:ruby, :hiredis) do
203
+ def test_bubble_timeout_without_retrying
204
+ serv = TCPServer.new(6380)
205
+
206
+ redis = Redis.new(:port => 6380, :timeout => 0.1)
207
+
208
+ assert_raise(Redis::TimeoutError) do
209
+ redis.ping
210
+ end
211
+
212
+ ensure
213
+ serv.close if serv
214
+ end
215
+ end
216
+ end