ione 1.2.4 → 1.3.0.pre0
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.
- checksums.yaml +4 -4
- data/lib/ione/future.rb +3 -5
- data/lib/ione/io/acceptor.rb +3 -1
- data/lib/ione/io/base_connection.rb +18 -4
- data/lib/ione/io/connection.rb +0 -1
- data/lib/ione/io/io_reactor.rb +26 -5
- data/lib/ione/io/ssl_connection.rb +0 -1
- data/lib/ione/version.rb +1 -1
- data/spec/integration/io_spec.rb +3 -3
- data/spec/integration/ssl_spec.rb +2 -7
- data/spec/ione/byte_buffer_spec.rb +38 -38
- data/spec/ione/future_spec.rb +65 -64
- data/spec/ione/heap_spec.rb +18 -18
- data/spec/ione/io/acceptor_spec.rb +5 -5
- data/spec/ione/io/connection_common.rb +15 -2
- data/spec/ione/io/io_reactor_spec.rb +194 -52
- data/spec/ione/io/ssl_acceptor_spec.rb +3 -3
- data/spec/ione/io/ssl_connection_spec.rb +3 -3
- data/spec/spec_helper.rb +1 -4
- data/spec/support/fake_server.rb +0 -1
- data/spec/support/server_helper.rb +15 -0
- metadata +7 -5
data/spec/ione/heap_spec.rb
CHANGED
@@ -17,9 +17,9 @@ module Ione
|
|
17
17
|
it 'returns the number of items in the heap' do
|
18
18
|
heap.push(13)
|
19
19
|
heap.push(100)
|
20
|
-
heap.size.should
|
20
|
+
heap.size.should == 2
|
21
21
|
heap.push(101)
|
22
|
-
heap.size.should
|
22
|
+
heap.size.should == 3
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -40,7 +40,7 @@ module Ione
|
|
40
40
|
heap.push(3)
|
41
41
|
heap.push(6)
|
42
42
|
heap.push(5)
|
43
|
-
heap.size.should
|
43
|
+
heap.size.should == 4
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'is aliased as #<<' do
|
@@ -48,14 +48,14 @@ module Ione
|
|
48
48
|
heap << 3
|
49
49
|
heap << 6
|
50
50
|
heap << 5
|
51
|
-
heap.size.should
|
51
|
+
heap.size.should == 4
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'does not add duplicates' do
|
55
55
|
heap << 3
|
56
56
|
heap << 3
|
57
57
|
heap << 3
|
58
|
-
heap.size.should
|
58
|
+
heap.size.should == 1
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -66,21 +66,21 @@ module Ione
|
|
66
66
|
|
67
67
|
it 'returns the only item when there is only one' do
|
68
68
|
heap.push(3)
|
69
|
-
heap.peek.should
|
69
|
+
heap.peek.should == 3
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'returns the smallest item' do
|
73
73
|
heap.push(10)
|
74
74
|
heap.push(3)
|
75
75
|
heap.push(7)
|
76
|
-
heap.peek.should
|
76
|
+
heap.peek.should == 3
|
77
77
|
end
|
78
78
|
|
79
79
|
it 'does not remove the item from the heap' do
|
80
80
|
heap.push(3)
|
81
|
-
heap.peek.should
|
82
|
-
heap.peek.should
|
83
|
-
heap.peek.should
|
81
|
+
heap.peek.should == 3
|
82
|
+
heap.peek.should == 3
|
83
|
+
heap.peek.should == 3
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
@@ -91,7 +91,7 @@ module Ione
|
|
91
91
|
|
92
92
|
it 'returns and removes the only item when there is only one' do
|
93
93
|
heap.push(3)
|
94
|
-
heap.pop.should
|
94
|
+
heap.pop.should == 3
|
95
95
|
heap.should be_empty
|
96
96
|
end
|
97
97
|
|
@@ -99,14 +99,14 @@ module Ione
|
|
99
99
|
heap.push(10)
|
100
100
|
heap.push(3)
|
101
101
|
heap.push(7)
|
102
|
-
heap.pop.should
|
103
|
-
heap.pop.should
|
104
|
-
heap.size.should
|
102
|
+
heap.pop.should == 3
|
103
|
+
heap.pop.should == 7
|
104
|
+
heap.size.should == 1
|
105
105
|
end
|
106
106
|
|
107
107
|
it 'removes the item from the heap' do
|
108
108
|
heap.push(3)
|
109
|
-
heap.pop.should
|
109
|
+
heap.pop.should == 3
|
110
110
|
heap.pop.should be_nil
|
111
111
|
end
|
112
112
|
end
|
@@ -125,8 +125,8 @@ module Ione
|
|
125
125
|
heap.push(101)
|
126
126
|
heap.delete(4)
|
127
127
|
heap.pop
|
128
|
-
heap.peek.should
|
129
|
-
heap.size.should
|
128
|
+
heap.peek.should == 100
|
129
|
+
heap.size.should == 2
|
130
130
|
end
|
131
131
|
|
132
132
|
it 'removes the last item from the heap' do
|
@@ -155,7 +155,7 @@ module Ione
|
|
155
155
|
heap.push(3)
|
156
156
|
heap.push(4)
|
157
157
|
heap.push(5)
|
158
|
-
heap.delete(4).should
|
158
|
+
heap.delete(4).should == 4
|
159
159
|
end
|
160
160
|
|
161
161
|
it 'returns nil when the item is not found' do
|
@@ -174,8 +174,8 @@ module Ione
|
|
174
174
|
acceptor.bind
|
175
175
|
acceptor.read
|
176
176
|
accepted_handlers.should have(1).item
|
177
|
-
accepted_handlers.first.host.should
|
178
|
-
accepted_handlers.first.port.should
|
177
|
+
accepted_handlers.first.host.should == 'example.com'
|
178
|
+
accepted_handlers.first.port.should == 3333
|
179
179
|
end
|
180
180
|
|
181
181
|
it 'passes the unblocker along to the connection handler' do
|
@@ -197,9 +197,9 @@ module Ione
|
|
197
197
|
acceptor.on_accept { |c| received_connection2 = c }
|
198
198
|
acceptor.bind
|
199
199
|
acceptor.read
|
200
|
-
received_connection1.host.should
|
201
|
-
received_connection2.host.should
|
202
|
-
received_connection2.port.should
|
200
|
+
received_connection1.host.should == 'example.com'
|
201
|
+
received_connection2.host.should == 'example.com'
|
202
|
+
received_connection2.port.should == 3333
|
203
203
|
end
|
204
204
|
|
205
205
|
it 'ignores exceptions raised by the connection callback' do
|
@@ -40,7 +40,7 @@ shared_examples_for 'a connection' do |options|
|
|
40
40
|
handler.on_closed { calls += 1 }
|
41
41
|
handler.close
|
42
42
|
handler.close
|
43
|
-
calls.should
|
43
|
+
calls.should == 1
|
44
44
|
end
|
45
45
|
|
46
46
|
it 'returns false if it did nothing' do
|
@@ -83,6 +83,19 @@ shared_examples_for 'a connection' do |options|
|
|
83
83
|
handler.should be_closed
|
84
84
|
end
|
85
85
|
|
86
|
+
it 'closes the socket for reading if possible' do
|
87
|
+
socket.stub(:close_read)
|
88
|
+
handler.write('hello world')
|
89
|
+
handler.drain
|
90
|
+
socket.should have_received(:close_read)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'ignores IO errors when closing for reading' do
|
94
|
+
socket.stub(:close_read).and_raise(IOError, 'Boork')
|
95
|
+
handler.write('hello world')
|
96
|
+
expect { handler.drain }.to_not raise_error
|
97
|
+
end
|
98
|
+
|
86
99
|
it 'returns a future that completes when the socket has closed' do
|
87
100
|
handler.write('hello world')
|
88
101
|
f = handler.drain
|
@@ -238,7 +251,7 @@ shared_examples_for 'a connection' do |options|
|
|
238
251
|
data = nil
|
239
252
|
handler.on_data { |d| data = d }
|
240
253
|
handler.read
|
241
|
-
data.should
|
254
|
+
data.should == 'foo bar'
|
242
255
|
end
|
243
256
|
|
244
257
|
context 'when #read_nonblock raises an error' do
|
@@ -7,7 +7,11 @@ module Ione
|
|
7
7
|
module Io
|
8
8
|
describe IoReactor do
|
9
9
|
let :reactor do
|
10
|
-
described_class.new(
|
10
|
+
described_class.new(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
let :options do
|
14
|
+
{selector: selector, clock: clock, drain_timeout: 3}
|
11
15
|
end
|
12
16
|
|
13
17
|
let! :selector do
|
@@ -37,7 +41,7 @@ module Ione
|
|
37
41
|
end
|
38
42
|
|
39
43
|
after do
|
40
|
-
reactor.stop if reactor.running?
|
44
|
+
reactor.stop.value if reactor.running?
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
@@ -77,15 +81,16 @@ module Ione
|
|
77
81
|
end
|
78
82
|
reactor.start.value
|
79
83
|
stopped_future = reactor.stop
|
80
|
-
restarted_future = reactor.start
|
81
84
|
sequence = []
|
82
85
|
stopped_future.on_complete { sequence << :stopped }
|
86
|
+
restarted_future = reactor.start
|
83
87
|
restarted_future.on_complete { sequence << :restarted }
|
84
88
|
barrier.push(nil)
|
85
89
|
stopped_future.value
|
86
90
|
restarted_future.value
|
91
|
+
await { sequence.size >= 2 }
|
87
92
|
begin
|
88
|
-
sequence.should
|
93
|
+
sequence.should == [:stopped, :restarted]
|
89
94
|
ensure
|
90
95
|
reactor.stop
|
91
96
|
barrier.push(nil) while reactor.running?
|
@@ -93,7 +98,6 @@ module Ione
|
|
93
98
|
end
|
94
99
|
|
95
100
|
it 'restarts the reactor even when restarted before a failed stop' do
|
96
|
-
pending 'This test is broken in JRuby' if RUBY_ENGINE == 'jruby'
|
97
101
|
barrier = Queue.new
|
98
102
|
selector.handler do
|
99
103
|
if barrier.pop == :fail
|
@@ -112,6 +116,7 @@ module Ione
|
|
112
116
|
barrier.push(:fail)
|
113
117
|
stopped_future.value rescue nil
|
114
118
|
restarted_future.value
|
119
|
+
await { crashed && restarted }
|
115
120
|
begin
|
116
121
|
crashed.should be_true
|
117
122
|
restarted.should be_true
|
@@ -136,6 +141,8 @@ module Ione
|
|
136
141
|
|
137
142
|
context 'when already started' do
|
138
143
|
it 'is not started again' do
|
144
|
+
calls = 0
|
145
|
+
lock = Mutex.new
|
139
146
|
ticks = Queue.new
|
140
147
|
barrier = Queue.new
|
141
148
|
selector.handler do
|
@@ -176,7 +183,7 @@ module Ione
|
|
176
183
|
reactor.should_not be_running
|
177
184
|
end
|
178
185
|
|
179
|
-
it 'keeps running until stop
|
186
|
+
it 'keeps running until stop completes' do
|
180
187
|
running_barrier = Queue.new
|
181
188
|
stop_barrier = Queue.new
|
182
189
|
selector.handler do
|
@@ -191,11 +198,125 @@ module Ione
|
|
191
198
|
stop_barrier.push(nil) until future.completed?
|
192
199
|
end
|
193
200
|
|
194
|
-
it '
|
201
|
+
it 'unblocks the reactor' do
|
202
|
+
running_barrier = Queue.new
|
203
|
+
selector.handler do |readables, writables, _, _|
|
204
|
+
running_barrier.push(nil)
|
205
|
+
IO.select(readables, writables, nil, 5)
|
206
|
+
end
|
195
207
|
reactor.start.value
|
196
|
-
|
197
|
-
reactor.stop
|
198
|
-
|
208
|
+
running_barrier.pop
|
209
|
+
stopped_future = reactor.stop
|
210
|
+
await { stopped_future.completed? }
|
211
|
+
stopped_future.should be_completed
|
212
|
+
stopped_future.value
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'drains all sockets' do
|
216
|
+
reactor.start.value
|
217
|
+
TCPServer.open(0) do |server|
|
218
|
+
lazy_socket = Thread.start { server.accept }
|
219
|
+
connection = reactor.connect(server.addr[3], server.addr[1], 5).value
|
220
|
+
writable = false
|
221
|
+
connection.stub(:stub_writable?) { writable }
|
222
|
+
class <<connection; alias_method :writable?, :stub_writable?; end
|
223
|
+
connection.write('12345678')
|
224
|
+
release_barrier = barrier = Queue.new
|
225
|
+
selector.handler do |readables, writables, _, _|
|
226
|
+
barrier.pop
|
227
|
+
[[], writables, []]
|
228
|
+
end
|
229
|
+
await { barrier.num_waiting > 0 }
|
230
|
+
barrier = []
|
231
|
+
release_barrier.push(nil)
|
232
|
+
connection.stub(:flush) do
|
233
|
+
writable = false
|
234
|
+
end
|
235
|
+
writable = true
|
236
|
+
reactor.stop.value
|
237
|
+
connection.should have_received(:flush)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'waits on drain to complete upto the specified drain timeout' do
|
242
|
+
time = time_increment = next_increment = 0
|
243
|
+
mutex = Mutex.new
|
244
|
+
clock.stub(:now) { mutex.synchronize { time } }
|
245
|
+
selector.handler do |_, writables, _, _|
|
246
|
+
mutex.synchronize do
|
247
|
+
time += time_increment
|
248
|
+
time_increment = next_increment
|
249
|
+
end
|
250
|
+
[[], writables, []]
|
251
|
+
end
|
252
|
+
reactor.start.value
|
253
|
+
TCPServer.open(0) do |server|
|
254
|
+
lazy_socket = Thread.start { server.accept }
|
255
|
+
connection = reactor.connect(server.addr[3], server.addr[1], 5).value
|
256
|
+
stopped_future = nil
|
257
|
+
mutex.synchronize do
|
258
|
+
connection.stub(:writable?).and_return(true)
|
259
|
+
connection.stub(:flush)
|
260
|
+
next_increment = 1
|
261
|
+
stopped_future = reactor.stop
|
262
|
+
end
|
263
|
+
expect { stopped_future.value }.to raise_error(ReactorError, /timeout/)
|
264
|
+
(time).should eq(3)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'waits on drain to complete upto five seconds by default' do
|
269
|
+
with_server do |host, port|
|
270
|
+
options.delete(:drain_timeout)
|
271
|
+
time = time_increment = next_increment = 0
|
272
|
+
mutex = Mutex.new
|
273
|
+
clock.stub(:now) { mutex.synchronize { time } }
|
274
|
+
selector.handler do |_, writables, _, _|
|
275
|
+
mutex.synchronize do
|
276
|
+
time += time_increment
|
277
|
+
time_increment = next_increment
|
278
|
+
end
|
279
|
+
[[], writables, []]
|
280
|
+
end
|
281
|
+
reactor.start.value
|
282
|
+
connection = reactor.connect(host, port, 5).value
|
283
|
+
stopped_future = nil
|
284
|
+
mutex.synchronize do
|
285
|
+
connection.stub(:writable?).and_return(true)
|
286
|
+
connection.stub(:flush)
|
287
|
+
next_increment = 1
|
288
|
+
stopped_future = reactor.stop
|
289
|
+
end
|
290
|
+
expect { stopped_future.value }.to raise_error(ReactorError, /timeout/)
|
291
|
+
time.should eq(5)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'closes all sockets' do
|
296
|
+
with_server do |host, port|
|
297
|
+
reactor.start.value
|
298
|
+
connection = reactor.connect(host, port, 5).value
|
299
|
+
reactor.stop.value
|
300
|
+
connection.should be_closed
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'closes all sockets even if drain fails' do
|
305
|
+
with_server do |host, port|
|
306
|
+
reactor.start.value
|
307
|
+
connection = reactor.connect(host, port, 5).value
|
308
|
+
Thread.pass
|
309
|
+
writable = false
|
310
|
+
connection.stub(:stub_writable?) { writable }
|
311
|
+
class <<connection; alias_method :writable?, :stub_writable?; end
|
312
|
+
connection.write('12345678')
|
313
|
+
Thread.pass
|
314
|
+
connection.stub(:flush).and_raise(StandardError, 'Boork')
|
315
|
+
writable = true
|
316
|
+
f = reactor.stop
|
317
|
+
expect { f.value }.to raise_error(StandardError, 'Boork')
|
318
|
+
connection.should be_closed
|
319
|
+
end
|
199
320
|
end
|
200
321
|
|
201
322
|
it 'cancels all active timers' do
|
@@ -237,7 +358,7 @@ module Ione
|
|
237
358
|
reactor.on_error { |e| error = e }
|
238
359
|
reactor.start
|
239
360
|
await { error }
|
240
|
-
error.message.should
|
361
|
+
error.message.should == 'Blurgh'
|
241
362
|
end
|
242
363
|
|
243
364
|
it 'calls the listener immediately when the reactor has already crashed' do
|
@@ -266,17 +387,18 @@ module Ione
|
|
266
387
|
reactor.on_error { calls << :post_started }
|
267
388
|
barrier.push(nil)
|
268
389
|
await { !reactor.running? }
|
390
|
+
await { calls.size >= 2 }
|
269
391
|
reactor.on_error { calls << :pre_restarted }
|
270
|
-
calls.should
|
392
|
+
calls.should == [
|
271
393
|
:pre_started,
|
272
394
|
:post_started,
|
273
395
|
:pre_restarted,
|
274
|
-
]
|
396
|
+
]
|
275
397
|
reactor.start
|
276
398
|
reactor.on_error { calls << :post_restarted }
|
277
399
|
barrier.push(nil)
|
278
400
|
await { !reactor.running? }
|
279
|
-
calls.should
|
401
|
+
calls.should == [
|
280
402
|
:pre_started,
|
281
403
|
:post_started,
|
282
404
|
:pre_restarted,
|
@@ -284,7 +406,7 @@ module Ione
|
|
284
406
|
:post_started,
|
285
407
|
:pre_restarted,
|
286
408
|
:post_restarted,
|
287
|
-
]
|
409
|
+
]
|
288
410
|
end
|
289
411
|
end
|
290
412
|
|
@@ -292,70 +414,90 @@ module Ione
|
|
292
414
|
include_context 'running_reactor'
|
293
415
|
|
294
416
|
it 'calls the given block with a connection' do
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
417
|
+
with_server do |host, port|
|
418
|
+
connection = nil
|
419
|
+
reactor.start.value
|
420
|
+
reactor.connect(host, port, 5) { |c| connection = c }.value
|
421
|
+
connection.should_not be_nil
|
422
|
+
end
|
299
423
|
end
|
300
424
|
|
301
425
|
it 'returns a future that resolves to what the given block returns' do
|
302
|
-
|
303
|
-
|
304
|
-
|
426
|
+
with_server do |host, port|
|
427
|
+
reactor.start.value
|
428
|
+
x = reactor.connect(host, port, 5) { :foo }.value
|
429
|
+
x.should == :foo
|
430
|
+
end
|
305
431
|
end
|
306
432
|
|
307
433
|
it 'defaults to 5 as the connection timeout' do
|
308
|
-
|
309
|
-
|
310
|
-
|
434
|
+
with_server do |host, port|
|
435
|
+
reactor.start.value
|
436
|
+
connection = reactor.connect(host, port).value
|
437
|
+
connection.connection_timeout.should == 5
|
438
|
+
end
|
311
439
|
end
|
312
440
|
|
313
441
|
it 'takes the connection timeout from the :timeout option' do
|
314
|
-
|
315
|
-
|
316
|
-
|
442
|
+
with_server do |host, port|
|
443
|
+
reactor.start.value
|
444
|
+
connection = reactor.connect(host, port, timeout: 9).value
|
445
|
+
connection.connection_timeout.should == 9
|
446
|
+
end
|
317
447
|
end
|
318
448
|
|
319
449
|
it 'returns the connection when no block is given' do
|
320
|
-
|
321
|
-
|
450
|
+
with_server do |host, port|
|
451
|
+
reactor.start.value
|
452
|
+
reactor.connect(host, port, 5).value.should be_a(Connection)
|
453
|
+
end
|
322
454
|
end
|
323
455
|
|
324
456
|
it 'creates a connection and passes it to the selector as a readable' do
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
457
|
+
with_server do |host, port|
|
458
|
+
reactor.start.value
|
459
|
+
connection = reactor.connect(host, port, 5).value
|
460
|
+
await { selector.last_arguments[0].length > 1 }
|
461
|
+
selector.last_arguments[0].should include(connection)
|
462
|
+
end
|
329
463
|
end
|
330
464
|
|
331
465
|
it 'upgrades the connection to SSL' do
|
332
|
-
|
333
|
-
|
334
|
-
|
466
|
+
with_server do |host, port|
|
467
|
+
reactor.start.value
|
468
|
+
connection = reactor.connect(host, port, ssl: true).value
|
469
|
+
connection.should be_a(SslConnection)
|
470
|
+
end
|
335
471
|
end
|
336
472
|
|
337
473
|
it 'passes an SSL context to the SSL connection' do
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
474
|
+
with_server do |host, port|
|
475
|
+
ssl_context = double(:ssl_context)
|
476
|
+
reactor.start.value
|
477
|
+
f = reactor.connect(host, port, ssl: ssl_context)
|
478
|
+
expect { f.value }.to raise_error
|
479
|
+
end
|
342
480
|
end
|
343
481
|
|
344
482
|
context 'when called before the reactor is started' do
|
345
483
|
it 'waits for the reactor to start' do
|
346
|
-
|
347
|
-
|
348
|
-
|
484
|
+
with_server do |host, port|
|
485
|
+
f = reactor.connect(host, port)
|
486
|
+
reactor.start.value
|
487
|
+
f.value
|
488
|
+
end
|
349
489
|
end
|
350
490
|
end
|
351
491
|
|
352
492
|
context 'when called after the reactor has stopped' do
|
353
493
|
it 'waits for the reactor to be restarted' do
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
494
|
+
with_server do |host, port|
|
495
|
+
reactor.start.value
|
496
|
+
reactor.stop.value
|
497
|
+
f = reactor.connect(host, port)
|
498
|
+
reactor.start.value
|
499
|
+
f.value
|
500
|
+
end
|
359
501
|
end
|
360
502
|
end
|
361
503
|
end
|
@@ -377,19 +519,19 @@ module Ione
|
|
377
519
|
it 'returns a future that resolves to what the given block returns' do
|
378
520
|
reactor.start.value
|
379
521
|
x = reactor.bind(ENV['SERVER_HOST'], port, 5) { |acceptor| :foo }.value
|
380
|
-
x.should
|
522
|
+
x.should == :foo
|
381
523
|
end
|
382
524
|
|
383
525
|
it 'defaults to a backlog of 5' do
|
384
526
|
reactor.start.value
|
385
527
|
acceptor = reactor.bind(ENV['SERVER_HOST'], port).value
|
386
|
-
acceptor.backlog.should
|
528
|
+
acceptor.backlog.should == 5
|
387
529
|
end
|
388
530
|
|
389
531
|
it 'takes the backlog from the :backlog option' do
|
390
532
|
reactor.start.value
|
391
533
|
acceptor = reactor.bind(ENV['SERVER_HOST'], port, backlog: 9).value
|
392
|
-
acceptor.backlog.should
|
534
|
+
acceptor.backlog.should == 9
|
393
535
|
end
|
394
536
|
|
395
537
|
it 'returns the acceptor when no block is given' do
|