ione 1.2.4 → 1.3.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 eq(2)
20
+ heap.size.should == 2
21
21
  heap.push(101)
22
- heap.size.should eq(3)
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 eq(4)
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 eq(4)
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 eq(1)
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 eq(3)
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 eq(3)
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 eq(3)
82
- heap.peek.should eq(3)
83
- heap.peek.should eq(3)
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 eq(3)
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 eq(3)
103
- heap.pop.should eq(7)
104
- heap.size.should eq(1)
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 eq(3)
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 eq(100)
129
- heap.size.should eq(2)
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 eq(4)
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 eq('example.com')
178
- accepted_handlers.first.port.should eq(3333)
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 eq('example.com')
201
- received_connection2.host.should eq('example.com')
202
- received_connection2.port.should eq(3333)
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 eq(1)
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 eq('foo bar')
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(selector: selector, clock: clock)
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 eq([:stopped, :restarted])
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 completed' do
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 'closes all sockets' do
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
- connection = reactor.connect('example.com', 9999, 5).value
197
- reactor.stop.value
198
- connection.should be_closed
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 eq('Blurgh')
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 eq([
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 eq([
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
- connection = nil
296
- reactor.start.value
297
- reactor.connect('example.com', 9999, 5) { |c| connection = c }.value
298
- connection.should_not be_nil
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
- reactor.start.value
303
- x = reactor.connect('example.com', 9999, 5) { :foo }.value
304
- x.should eq(:foo)
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
- reactor.start.value
309
- connection = reactor.connect('example.com', 9999).value
310
- connection.connection_timeout.should eq(5)
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
- reactor.start.value
315
- connection = reactor.connect('example.com', 9999, timeout: 9).value
316
- connection.connection_timeout.should eq(9)
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
- reactor.start.value
321
- reactor.connect('example.com', 9999, 5).value.should be_a(Connection)
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
- reactor.start.value
326
- connection = reactor.connect('example.com', 9999, 5).value
327
- await { selector.last_arguments[0].length > 1 }
328
- selector.last_arguments[0].should include(connection)
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
- reactor.start.value
333
- connection = reactor.connect('example.com', 9999, ssl: true).value
334
- connection.should be_a(SslConnection)
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
- ssl_context = double(:ssl_context)
339
- reactor.start.value
340
- f = reactor.connect('example.com', 9999, ssl: ssl_context)
341
- expect { f.value }.to raise_error
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
- f = reactor.connect('example.com', 9999)
347
- reactor.start.value
348
- f.value
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
- reactor.start.value
355
- reactor.stop.value
356
- f = reactor.connect('example.com', 9999)
357
- reactor.start.value
358
- f.value
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 eq(:foo)
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 eq(5)
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 eq(9)
534
+ acceptor.backlog.should == 9
393
535
  end
394
536
 
395
537
  it 'returns the acceptor when no block is given' do