ione 1.2.0 → 1.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.
@@ -822,6 +822,17 @@ module Ione
822
822
  future.value.should == {'foo' => 'bar', 'qux' => 'baz', 'hello' => 'world'}
823
823
  end
824
824
 
825
+ it 'accepts boolean accumulators' do
826
+ futures = [
827
+ Future.resolved([:foo]),
828
+ Future.resolved([]),
829
+ ]
830
+ future = Future.reduce(futures, false) do |accumulator, value|
831
+ accumulator || value.empty?
832
+ end
833
+ future.value.should == true
834
+ end
835
+
825
836
  it 'calls the block with the values in the order of the source futures' do
826
837
  promises = [Promise.new, Promise.new, Promise.new, Promise.new, Promise.new]
827
838
  futures = promises.map(&:future)
@@ -868,6 +879,18 @@ module Ione
868
879
  future.should be_failed
869
880
  end
870
881
 
882
+ it 'allows invocations to return nil' do
883
+ futures = [Future.resolved(1), Future.resolved(2), Future.resolved(3)]
884
+ future = Future.reduce(futures, []) do |accumulator, value|
885
+ if value == 2
886
+ nil
887
+ else
888
+ value
889
+ end
890
+ end
891
+ future.value.should eq(3)
892
+ end
893
+
871
894
  context 'when the list of futures is empty' do
872
895
  it 'returns a future that resolves to the initial value' do
873
896
  Future.reduce([], :foo).value.should == :foo
@@ -938,6 +961,95 @@ module Ione
938
961
  end
939
962
  end
940
963
 
964
+ describe '.after' do
965
+ context 'returns a new future which' do
966
+ it 'is resolved when the source futures are resolved' do
967
+ p1 = Promise.new
968
+ p2 = Promise.new
969
+ f = Future.after(p1.future, p2.future)
970
+ p1.fulfill
971
+ f.should_not be_resolved
972
+ p2.fulfill
973
+ f.should be_resolved
974
+ end
975
+
976
+ it 'returns no value' do
977
+ p1 = Promise.new
978
+ p2 = Promise.new
979
+ p3 = Promise.new
980
+ f = Future.after(p1.future, p2.future, p3.future)
981
+ p2.fulfill(2)
982
+ p1.fulfill(1)
983
+ p3.fulfill(3)
984
+ f.value.should be_nil
985
+ end
986
+
987
+ it 'fails if any of the source futures fail' do
988
+ p1 = Promise.new
989
+ p2 = Promise.new
990
+ p3 = Promise.new
991
+ p4 = Promise.new
992
+ f = Future.after(p1.future, p2.future, p3.future, p4.future)
993
+ p2.fulfill
994
+ p1.fail(StandardError.new('hurgh'))
995
+ p3.fail(StandardError.new('murgasd'))
996
+ p4.fulfill
997
+ expect { f.value }.to raise_error(/hurgh|murgasd/)
998
+ f.should be_failed
999
+ end
1000
+
1001
+ it 'completes with nil when no futures are given' do
1002
+ Future.after.value.should be_nil
1003
+ end
1004
+
1005
+ it 'completes with nil when an empty list is given' do
1006
+ Future.after([]).value.should be_nil
1007
+ end
1008
+
1009
+ it 'completes with nil when an empty enumerable is given' do
1010
+ Future.after([].to_enum).value.should be_nil
1011
+ end
1012
+
1013
+ it 'completes with nil when a single future is given' do
1014
+ f = Future.resolved(1)
1015
+ Future.after(f).value.should be_nil
1016
+ end
1017
+
1018
+ it 'accepts a list of futures' do
1019
+ promises = [Promise.new, Promise.new, Promise.new]
1020
+ futures = promises.map(&:future)
1021
+ f = Future.after(futures)
1022
+ promises.each(&:fulfill)
1023
+ f.value.should be_nil
1024
+ end
1025
+
1026
+ it 'accepts an enumerable of futures' do
1027
+ promises = [Promise.new, Promise.new, Promise.new]
1028
+ futures = promises.map(&:future).to_enum
1029
+ f = Future.after(futures)
1030
+ promises.each(&:fulfill)
1031
+ f.value.should be_nil
1032
+ end
1033
+
1034
+ it 'accepts an enumerable of one future' do
1035
+ promises = [Promise.new]
1036
+ futures = promises.map(&:future).to_enum
1037
+ f = Future.after(futures)
1038
+ promises.each(&:fulfill)
1039
+ f.value.should be_nil
1040
+ end
1041
+
1042
+ it 'accepts anything that implements #on_complete as futures' do
1043
+ ff1, ff2, ff3 = double, double, double
1044
+ ff1.stub(:on_complete) { |&listener| listener.call(1, nil) }
1045
+ ff2.stub(:on_complete) { |&listener| listener.call(2, nil) }
1046
+ ff3.stub(:on_complete) { |&listener| listener.call(3, nil) }
1047
+ future = Future.after(ff1, ff2, ff3)
1048
+ future.value.should be_nil
1049
+ end
1050
+ end
1051
+ end
1052
+
941
1053
  describe '.all' do
942
1054
  context 'returns a new future which' do
943
1055
  it 'is resolved when the source futures are resolved' do
@@ -312,15 +312,15 @@ module Ione
312
312
  end
313
313
 
314
314
  it 'includes the connection state' do
315
- handler.to_s.should include('connecting')
315
+ handler.to_s.should include('CONNECTING')
316
316
  socket.stub(:connect_nonblock).and_raise(Errno::EINPROGRESS)
317
317
  handler.connect
318
- handler.to_s.should include('connecting')
318
+ handler.to_s.should include('CONNECTING')
319
319
  socket.stub(:connect_nonblock)
320
320
  handler.connect
321
- handler.to_s.should include('connected')
321
+ handler.to_s.should include('CONNECTED')
322
322
  handler.close
323
- handler.to_s.should include('closed')
323
+ handler.to_s.should include('CLOSED')
324
324
  end
325
325
  end
326
326
  end
@@ -59,12 +59,6 @@ module Ione
59
59
  reactor.should be_running
60
60
  end
61
61
 
62
- it 'cannot be started again once stopped' do
63
- reactor.start.value
64
- reactor.stop.value
65
- expect { reactor.start }.to raise_error(ReactorError)
66
- end
67
-
68
62
  it 'calls the selector' do
69
63
  called = false
70
64
  selector.handler { called = true; [[], [], []] }
@@ -73,6 +67,95 @@ module Ione
73
67
  reactor.stop.value
74
68
  called.should be_true, 'expected the selector to have been called'
75
69
  end
70
+
71
+ context 'when stopping' do
72
+ it 'waits for the reactor to stop, then starts it again' do
73
+ barrier = Queue.new
74
+ selector.handler do
75
+ barrier.pop
76
+ [[], [], []]
77
+ end
78
+ reactor.start.value
79
+ stopped_future = reactor.stop
80
+ restarted_future = reactor.start
81
+ sequence = []
82
+ stopped_future.on_complete { sequence << :stopped }
83
+ restarted_future.on_complete { sequence << :restarted }
84
+ barrier.push(nil)
85
+ stopped_future.value
86
+ restarted_future.value
87
+ begin
88
+ sequence.should == [:stopped, :restarted]
89
+ ensure
90
+ reactor.stop
91
+ barrier.push(nil) while reactor.running?
92
+ end
93
+ end
94
+
95
+ it 'restarts the reactor even when restarted before a failed stop' do
96
+ barrier = Queue.new
97
+ selector.handler do
98
+ if barrier.pop == :fail
99
+ raise 'Blurgh'
100
+ else
101
+ [[], [], []]
102
+ end
103
+ end
104
+ reactor.start.value
105
+ stopped_future = reactor.stop
106
+ restarted_future = reactor.start
107
+ crashed = false
108
+ restarted = false
109
+ stopped_future.on_failure { crashed = true }
110
+ restarted_future.on_complete { restarted = true }
111
+ barrier.push(:fail)
112
+ stopped_future.value rescue nil
113
+ restarted_future.value
114
+ begin
115
+ crashed.should be_true
116
+ restarted.should be_true
117
+ ensure
118
+ reactor.stop
119
+ barrier.push(nil) while reactor.running?
120
+ end
121
+ end
122
+ end
123
+
124
+ context 'when stopped' do
125
+ before do
126
+ reactor.start.value
127
+ reactor.stop.value
128
+ end
129
+
130
+ it 'starts the reactor again' do
131
+ reactor.start.value
132
+ reactor.should be_running
133
+ end
134
+ end
135
+
136
+ context 'when already started' do
137
+ it 'is not started again' do
138
+ calls = 0
139
+ lock = Mutex.new
140
+ ticks = Queue.new
141
+ barrier = Queue.new
142
+ selector.handler do
143
+ ticks.push(:tick)
144
+ barrier.pop
145
+ [[], [], []]
146
+ end
147
+ reactor.start.value
148
+ reactor.start.value
149
+ reactor.start.value
150
+ begin
151
+ ticks.pop.should_not be_nil
152
+ ticks.size.should be_zero
153
+ ensure
154
+ reactor.stop
155
+ barrier.push(nil) while reactor.running?
156
+ end
157
+ end
158
+ end
76
159
  end
77
160
 
78
161
  describe '#stop' do
@@ -114,6 +197,20 @@ module Ione
114
197
  active_timer1.should be_failed
115
198
  active_timer2.should be_failed
116
199
  end
200
+
201
+ context 'when not started' do
202
+ it 'does nothing' do
203
+ reactor = described_class.new(selector: selector, clock: clock)
204
+ expect { reactor.stop.value }.to_not raise_error
205
+ end
206
+ end
207
+
208
+ context 'when already stopped' do
209
+ it 'does nothing' do
210
+ reactor.stop.value
211
+ expect { reactor.stop.value }.to_not raise_error
212
+ end
213
+ end
117
214
  end
118
215
 
119
216
  describe '#on_error' do
@@ -145,6 +242,36 @@ module Ione
145
242
  await { called }
146
243
  called.should be_true, 'expected all close listeners to have been called'
147
244
  end
245
+
246
+ it 'calls all listeners when the reactor crashes after being restarted' do
247
+ calls = []
248
+ barrier = Queue.new
249
+ selector.handler { barrier.pop; raise 'Blurgh' }
250
+ reactor.on_error { calls << :pre_started }
251
+ reactor.start
252
+ reactor.on_error { calls << :post_started }
253
+ barrier.push(nil)
254
+ await { !reactor.running? }
255
+ reactor.on_error { calls << :pre_restarted }
256
+ calls.should == [
257
+ :pre_started,
258
+ :post_started,
259
+ :pre_restarted,
260
+ ]
261
+ reactor.start
262
+ reactor.on_error { calls << :post_restarted }
263
+ barrier.push(nil)
264
+ await { !reactor.running? }
265
+ calls.should == [
266
+ :pre_started,
267
+ :post_started,
268
+ :pre_restarted,
269
+ :pre_started,
270
+ :post_started,
271
+ :pre_restarted,
272
+ :post_restarted,
273
+ ]
274
+ end
148
275
  end
149
276
 
150
277
  describe '#connect' do
@@ -199,6 +326,24 @@ module Ione
199
326
  f = reactor.connect('example.com', 9999, ssl: ssl_context)
200
327
  expect { f.value }.to raise_error
201
328
  end
329
+
330
+ context 'when called before the reactor is started' do
331
+ it 'waits for the reactor to start' do
332
+ f = reactor.connect('example.com', 9999)
333
+ reactor.start.value
334
+ f.value
335
+ end
336
+ end
337
+
338
+ context 'when called after the reactor has stopped' do
339
+ it 'waits for the reactor to be restarted' do
340
+ reactor.start.value
341
+ reactor.stop.value
342
+ f = reactor.connect('example.com', 9999)
343
+ reactor.start.value
344
+ f.value
345
+ end
346
+ end
202
347
  end
203
348
 
204
349
  describe '#bind' do
@@ -252,22 +397,72 @@ module Ione
252
397
  acceptor = reactor.bind(ENV['SERVER_HOST'], port, ssl: ssl_context).value
253
398
  acceptor.should be_an(SslAcceptor)
254
399
  end
400
+
401
+ context 'when called before the reactor is started' do
402
+ it 'waits for the reactor to start' do
403
+ f = reactor.bind(ENV['SERVER_HOST'], port, 5)
404
+ reactor.start.value
405
+ f.value
406
+ end
407
+ end
408
+
409
+ context 'when called after the reactor has stopped' do
410
+ it 'waits for the reactor to be restarted' do
411
+ reactor.start.value
412
+ reactor.stop.value
413
+ f = reactor.bind(ENV['SERVER_HOST'], port, 5)
414
+ reactor.start.value
415
+ f.value
416
+ end
417
+ end
255
418
  end
256
419
 
257
420
  describe '#schedule_timer' do
258
- before do
259
- reactor.start.value
421
+ context 'when the reactor is running' do
422
+ before do
423
+ reactor.start.value
424
+ end
425
+
426
+ after do
427
+ reactor.stop.value
428
+ end
429
+
430
+ it 'returns a future that is resolved after the specified duration' do
431
+ clock.stub(:now).and_return(1)
432
+ f = reactor.schedule_timer(0.1)
433
+ clock.stub(:now).and_return(1.1)
434
+ await { f.resolved? }
435
+ end
260
436
  end
261
437
 
262
- after do
263
- reactor.stop.value
438
+ context 'when called before the reactor is started' do
439
+ after do
440
+ reactor.stop.value if reactor.running?
441
+ end
442
+
443
+ it 'waits for the reactor to start' do
444
+ clock.stub(:now).and_return(1)
445
+ f = reactor.schedule_timer(0.1)
446
+ clock.stub(:now).and_return(2)
447
+ reactor.start.value
448
+ f.value
449
+ end
264
450
  end
265
451
 
266
- it 'returns a future that is resolved after the specified duration' do
267
- clock.stub(:now).and_return(1)
268
- f = reactor.schedule_timer(0.1)
269
- clock.stub(:now).and_return(1.1)
270
- await { f.resolved? }
452
+ context 'when called after the reactor has stopped' do
453
+ after do
454
+ reactor.stop.value if reactor.running?
455
+ end
456
+
457
+ it 'waits for the reactor to be restarted' do
458
+ reactor.start.value
459
+ reactor.stop.value
460
+ clock.stub(:now).and_return(1)
461
+ f = reactor.schedule_timer(0.1)
462
+ clock.stub(:now).and_return(2)
463
+ reactor.start.value
464
+ f.value
465
+ end
271
466
  end
272
467
  end
273
468
 
@@ -322,6 +517,29 @@ module Ione
322
517
  it 'does nothing when given nil' do
323
518
  reactor.cancel_timer(nil)
324
519
  end
520
+
521
+ context 'when called before the reactor is started' do
522
+ it 'removes the timer before the reactor starts' do
523
+ clock.stub(:now).and_return(1)
524
+ f = reactor.schedule_timer(0.1)
525
+ reactor.cancel_timer(f)
526
+ clock.stub(:now).and_return(2)
527
+ f.should be_failed
528
+ reactor.start.value
529
+ end
530
+ end
531
+
532
+ context 'when called after the reactor has stopped' do
533
+ it 'removes the timer before the reactor is started again' do
534
+ reactor.start.value
535
+ reactor.stop.value
536
+ clock.stub(:now).and_return(1)
537
+ f = reactor.schedule_timer(0.1)
538
+ reactor.cancel_timer(f)
539
+ f.should be_failed
540
+ reactor.start.value
541
+ end
542
+ end
325
543
  end
326
544
 
327
545
  describe '#to_s' do
@@ -330,9 +548,8 @@ module Ione
330
548
  reactor.to_s.should include('Ione::Io::IoReactor')
331
549
  end
332
550
 
333
- it 'includes a list of its connections' do
334
- reactor.to_s.should include('@connections=[')
335
- reactor.to_s.should include('#<Ione::Io::Unblocker>')
551
+ it 'includes the state' do
552
+ reactor.to_s.should include('PENDING')
336
553
  end
337
554
  end
338
555
  end