ione 1.0.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.
@@ -0,0 +1,737 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+
6
+ module Ione
7
+ describe Promise do
8
+ let :promise do
9
+ described_class.new
10
+ end
11
+
12
+ let :future do
13
+ promise.future
14
+ end
15
+
16
+ let :error do
17
+ StandardError.new('bork')
18
+ end
19
+
20
+ describe '#fulfill' do
21
+ it 'resolves its future' do
22
+ promise.fulfill
23
+ future.should be_resolved
24
+ end
25
+
26
+ it 'raises an error if fulfilled a second time' do
27
+ promise.fulfill
28
+ expect { promise.fulfill }.to raise_error(FutureError)
29
+ end
30
+
31
+ it 'raises an error if failed after being fulfilled' do
32
+ promise.fulfill
33
+ expect { promise.fail(error) }.to raise_error(FutureError)
34
+ end
35
+
36
+ it 'returns nil' do
37
+ promise.fulfill(:foo).should be_nil
38
+ end
39
+ end
40
+
41
+ describe '#fail' do
42
+ it 'fails its future' do
43
+ promise.fail(error)
44
+ future.should be_failed
45
+ end
46
+
47
+ it 'raises an error if failed a second time' do
48
+ promise.fail(error)
49
+ expect { promise.fail(error) }.to raise_error(FutureError)
50
+ end
51
+
52
+ it 'raises an error if fulfilled after being failed' do
53
+ promise.fail(error)
54
+ expect { promise.fulfill }.to raise_error(FutureError)
55
+ end
56
+
57
+ it 'returns nil' do
58
+ promise.fail(error).should be_nil
59
+ end
60
+ end
61
+
62
+ describe '#observe' do
63
+ it 'resolves its future when the specified future is resolved' do
64
+ p2 = Promise.new
65
+ promise.observe(p2.future)
66
+ p2.fulfill
67
+ promise.future.should be_resolved
68
+ end
69
+
70
+ it 'fails its future when the specified future fails' do
71
+ p2 = Promise.new
72
+ promise.observe(p2.future)
73
+ p2.fail(error)
74
+ promise.future.should be_failed
75
+ end
76
+
77
+ it 'silently ignores double fulfillment/failure' do
78
+ p2 = Promise.new
79
+ promise.observe(p2.future)
80
+ promise.fail(error)
81
+ p2.fulfill
82
+ end
83
+
84
+ it 'returns nil' do
85
+ promise.observe(Promise.new.future).should be_nil
86
+ end
87
+ end
88
+
89
+ describe '#try' do
90
+ it 'fulfills the promise with the result of the block' do
91
+ promise.try do
92
+ 3 + 4
93
+ end
94
+ promise.future.value.should == 7
95
+ end
96
+
97
+ it 'fails the promise when the block raises an error' do
98
+ promise.try do
99
+ raise error
100
+ end
101
+ expect { promise.future.value }.to raise_error(/bork/)
102
+ end
103
+
104
+ it 'calls the block with the specified arguments' do
105
+ promise.try(:foo, 3) do |a, b|
106
+ a.length + b
107
+ end
108
+ promise.future.value.should == 6
109
+ end
110
+
111
+ it 'returns nil' do
112
+ promise.try { }.should be_nil
113
+ end
114
+ end
115
+ end
116
+
117
+ describe Future do
118
+ let :promise do
119
+ Promise.new
120
+ end
121
+
122
+ let :future do
123
+ promise.future
124
+ end
125
+
126
+ let :error do
127
+ StandardError.new('bork')
128
+ end
129
+
130
+ def async(*context, &listener)
131
+ Thread.start(*context, &listener)
132
+ end
133
+
134
+ def delayed(*context, &listener)
135
+ async(*context) do |*ctx|
136
+ sleep(0.1)
137
+ listener.call(*context)
138
+ end
139
+ end
140
+
141
+ describe '#completed?' do
142
+ it 'is true when the promise is fulfilled' do
143
+ promise.fulfill
144
+ future.should be_completed
145
+ end
146
+
147
+ it 'is true when the promise is failed' do
148
+ promise.fail(StandardError.new('bork'))
149
+ future.should be_completed
150
+ end
151
+ end
152
+
153
+ describe '#resolved?' do
154
+ it 'is true when the promise is fulfilled' do
155
+ promise.fulfill('foo')
156
+ future.should be_resolved
157
+ end
158
+
159
+ it 'is true when the promise is fulfilled with something falsy' do
160
+ promise.fulfill(nil)
161
+ future.should be_resolved
162
+ end
163
+
164
+ it 'is false when the promise is failed' do
165
+ promise.fail(StandardError.new('bork'))
166
+ future.should_not be_resolved
167
+ end
168
+ end
169
+
170
+ describe '#failed?' do
171
+ it 'is true when the promise is failed' do
172
+ promise.fail(error)
173
+ future.should be_failed
174
+ end
175
+
176
+ it 'is false when the promise is fulfilled' do
177
+ promise.fulfill
178
+ future.should_not be_failed
179
+ end
180
+ end
181
+
182
+ describe '#on_complete' do
183
+ context 'registers listeners and' do
184
+ it 'notifies all listeners when the promise is fulfilled' do
185
+ v1, v2 = nil, nil
186
+ future.on_complete { |f| v1 = f.value }
187
+ future.on_complete { |f| v2 = f.value }
188
+ promise.fulfill('bar')
189
+ v1.should == 'bar'
190
+ v2.should == 'bar'
191
+ end
192
+
193
+ it 'notifies all listeners when the promise fails' do
194
+ e1, e2 = nil, nil
195
+ future.on_complete { |f| begin; f.value; rescue => err; e1 = err; end }
196
+ future.on_complete { |f| begin; f.value; rescue => err; e2 = err; end }
197
+ future.fail(error)
198
+ e1.message.should == error.message
199
+ e2.message.should == error.message
200
+ end
201
+
202
+ it 'notifies all listeners when the promise is fulfilled, even when one raises an error' do
203
+ value = nil
204
+ future.on_complete { |f| raise 'Blurgh' }
205
+ future.on_complete { |f| value = f.value }
206
+ promise.fulfill('bar')
207
+ value.should == 'bar'
208
+ end
209
+
210
+ it 'notifies all listeners when the promise fails, even when one raises an error' do
211
+ err = nil
212
+ future.on_complete { |f| raise 'Blurgh' }
213
+ future.on_complete { |f| begin; f.value; rescue => err; e = err; end }
214
+ promise.fail(error)
215
+ err.message.should == 'bork'
216
+ end
217
+
218
+ it 'notifies listeners registered after the promise was fulfilled' do
219
+ promise.fulfill('bar')
220
+ expect { future.on_complete { |v| raise 'blurgh' } }.to_not raise_error
221
+ end
222
+
223
+ it 'notifies listeners registered after the promise failed' do
224
+ promise.fail(error)
225
+ expect { future.on_complete { |v| raise 'blurgh' } }.to_not raise_error
226
+ end
227
+
228
+ it 'returns nil' do
229
+ future.on_complete { :foo }.should be_nil
230
+ end
231
+
232
+ it 'returns nil when the future is already resolved' do
233
+ promise.fulfill
234
+ future.on_complete { :foo }.should be_nil
235
+ end
236
+
237
+ it 'returns nil when the future already has failed' do
238
+ promise.fail(error)
239
+ future.on_complete { :foo }.should be_nil
240
+ end
241
+ end
242
+ end
243
+
244
+ describe '#on_value' do
245
+ context 'registers listeners and' do
246
+ it 'notifies all value listeners when the promise is fulfilled' do
247
+ v1, v2 = nil, nil
248
+ future.on_value { |v| v1 = v }
249
+ future.on_value { |v| v2 = v }
250
+ promise.fulfill('bar')
251
+ v1.should == 'bar'
252
+ v2.should == 'bar'
253
+ end
254
+
255
+ it 'notifies all listeners even when one raises an error' do
256
+ value = nil
257
+ future.on_value { |v| raise 'Blurgh' }
258
+ future.on_value { |v| value = v }
259
+ promise.fulfill('bar')
260
+ value.should == 'bar'
261
+ end
262
+
263
+ it 'notifies listeners registered after the promise was resolved' do
264
+ v1, v2 = nil, nil
265
+ promise.fulfill('bar')
266
+ future.on_value { |v| v1 = v }
267
+ future.on_value { |v| v2 = v }
268
+ v1.should == 'bar'
269
+ v2.should == 'bar'
270
+ end
271
+
272
+ it 'does not raise any error when the listener raises an error when already resolved' do
273
+ promise.fulfill('bar')
274
+ expect { future.on_value { |v| raise 'blurgh' } }.to_not raise_error
275
+ end
276
+
277
+ it 'returns nil' do
278
+ future.on_value { :foo }.should be_nil
279
+ end
280
+
281
+ it 'returns nil when the future is already resolved' do
282
+ promise.fulfill
283
+ future.on_failure { :foo }.should be_nil
284
+ end
285
+ end
286
+ end
287
+
288
+ describe '#on_failure' do
289
+ context 'registers listeners and' do
290
+ it 'notifies all failure listeners when the promise fails' do
291
+ e1, e2 = nil, nil
292
+ future.on_failure { |err| e1 = err }
293
+ future.on_failure { |err| e2 = err }
294
+ promise.fail(error)
295
+ e1.message.should eql(error.message)
296
+ e2.message.should eql(error.message)
297
+ end
298
+
299
+ it 'notifies all listeners even if one raises an error' do
300
+ e = nil
301
+ future.on_failure { |err| raise 'Blurgh' }
302
+ future.on_failure { |err| e = err }
303
+ promise.fail(error)
304
+ e.message.should eql(error.message)
305
+ end
306
+
307
+ it 'notifies new listeners even when already failed' do
308
+ e1, e2 = nil, nil
309
+ promise.fail(error)
310
+ future.on_failure { |e| e1 = e }
311
+ future.on_failure { |e| e2 = e }
312
+ e1.message.should eql(error.message)
313
+ e2.message.should eql(error.message)
314
+ end
315
+
316
+ it 'does not raise any error when the listener raises an error when already failed' do
317
+ promise.fail(error)
318
+ expect { future.on_failure { |e| raise 'Blurgh' } }.to_not raise_error
319
+ end
320
+
321
+ it 'returns nil' do
322
+ future.on_failure { :foo }.should be_nil
323
+ end
324
+
325
+ it 'returns nil when the future already has failed' do
326
+ promise.fail(error)
327
+ future.on_failure { :foo }.should be_nil
328
+ end
329
+ end
330
+ end
331
+
332
+ describe '#value' do
333
+ it 'is nil by default' do
334
+ promise.fulfill
335
+ future.value.should be_nil
336
+ end
337
+
338
+ it 'is the object passed to Promise#fulfill' do
339
+ obj = 'hello world'
340
+ promise.fulfill(obj)
341
+ future.value.should equal(obj)
342
+ end
343
+
344
+ it 'raises the error passed to Promise#fail' do
345
+ promise.fail(StandardError.new('bork'))
346
+ expect { future.value }.to raise_error(/bork/)
347
+ end
348
+
349
+ it 'blocks until the promise is completed' do
350
+ d = delayed(promise) do |p|
351
+ p.fulfill('bar')
352
+ end
353
+ d.value
354
+ future.value.should == 'bar'
355
+ end
356
+
357
+ it 'blocks on #value until fulfilled, when value is nil' do
358
+ d = delayed(promise) do |p|
359
+ p.fulfill
360
+ end
361
+ d.value
362
+ future.value.should be_nil
363
+ end
364
+
365
+ it 'blocks on #value until failed' do
366
+ d = delayed(promise) do |p|
367
+ p.fail(StandardError.new('bork'))
368
+ end
369
+ d.value
370
+ expect { future.value }.to raise_error('bork')
371
+ end
372
+
373
+ it 'allows multiple threads to block on #value until fulfilled' do
374
+ listeners = Array.new(10) do
375
+ async(future) do |f|
376
+ f.value
377
+ end
378
+ end
379
+ sleep 0.1
380
+ promise.fulfill(:hello)
381
+ listeners.map(&:value).should == Array.new(10, :hello)
382
+ end
383
+ end
384
+
385
+ describe '#map' do
386
+ context 'returns a new future that' do
387
+ it 'will be fulfilled with the result of the given block' do
388
+ mapped_value = nil
389
+ p = Promise.new
390
+ f = p.future.map { |v| v * 2 }
391
+ f.on_value { |v| mapped_value = v }
392
+ p.fulfill(3)
393
+ mapped_value.should == 3 * 2
394
+ end
395
+
396
+ it 'will be fulfilled with the specified value' do
397
+ mapped_value = nil
398
+ p = Promise.new
399
+ f = p.future.map(7)
400
+ f.on_value { |v| mapped_value = v }
401
+ p.fulfill(3)
402
+ mapped_value.should == 7
403
+ end
404
+
405
+ it 'will be fulfilled with the result of the given block, even if a value is specified' do
406
+ mapped_value = nil
407
+ p = Promise.new
408
+ f = p.future.map(7) { |v| v * 2 }
409
+ f.on_value { |v| mapped_value = v }
410
+ p.fulfill(3)
411
+ mapped_value.should == 3 * 2
412
+ end
413
+
414
+ it 'will be fulfilled with nil when neither value nor block is specified' do
415
+ mapped_value = 3
416
+ p = Promise.new
417
+ f = p.future.map
418
+ f.on_value { |v| mapped_value = v }
419
+ p.fulfill(3)
420
+ mapped_value.should be_nil
421
+ end
422
+
423
+ it 'fails when the original future fails' do
424
+ failed = false
425
+ p = Promise.new
426
+ f = p.future.map { |v| v * 2 }
427
+ f.on_failure { failed = true }
428
+ p.fail(StandardError.new('Blurgh'))
429
+ failed.should be_true
430
+ end
431
+
432
+ it 'fails when the block raises an error' do
433
+ p = Promise.new
434
+ f = p.future.map { |v| raise 'blurgh' }
435
+ d = delayed do
436
+ p.fulfill
437
+ end
438
+ d.value
439
+ expect { f.value }.to raise_error('blurgh')
440
+ end
441
+ end
442
+ end
443
+
444
+ describe '#flat_map' do
445
+ it 'works like #map, but expects that the block returns a future' do
446
+ p = Promise.new
447
+ f = p.future.flat_map { |v| Future.resolved(v * 2) }
448
+ p.fulfill(3)
449
+ f.value.should == 3 * 2
450
+ end
451
+
452
+ it 'fails when the block raises an error' do
453
+ p = Promise.new
454
+ f = p.future.flat_map { |v| raise 'Hurgh' }
455
+ p.fulfill(3)
456
+ expect { f.value }.to raise_error('Hurgh')
457
+ end
458
+ end
459
+
460
+ describe '#recover' do
461
+ context 'returns a new future that' do
462
+ it 'becomes fulfilled with a value created by the block when the source future fails' do
463
+ p = Promise.new
464
+ f = p.future.recover { 'foo' }
465
+ p.fail(error)
466
+ f.value.should == 'foo'
467
+ end
468
+
469
+ it 'becomes fulfilled with a specfied value when the source future fails' do
470
+ p = Promise.new
471
+ f = p.future.recover('bar')
472
+ p.fail(error)
473
+ f.value.should == 'bar'
474
+ end
475
+
476
+ it 'becomes fulfilled with a value created by the block even when a value is specified when the source future fails' do
477
+ p = Promise.new
478
+ f = p.future.recover('bar') { 'foo' }
479
+ p.fail(error)
480
+ f.value.should == 'foo'
481
+ end
482
+
483
+ it 'becomes fulfilled with nil value when no value nor block is specified and the source future fails' do
484
+ p = Promise.new
485
+ f = p.future.recover
486
+ p.fail(error)
487
+ f.value.should be_nil
488
+ end
489
+
490
+ it 'yields the error to the block' do
491
+ p = Promise.new
492
+ f = p.future.recover { |e| e.message }
493
+ p.fail(error)
494
+ f.value.should == error.message
495
+ end
496
+
497
+ it 'becomes fulfilled with the value of the source future when the source future is fulfilled' do
498
+ p = Promise.new
499
+ f = p.future.recover { 'foo' }
500
+ p.fulfill('bar')
501
+ f.value.should == 'bar'
502
+ end
503
+
504
+ it 'fails with the error raised in the given block' do
505
+ p = Promise.new
506
+ f = p.future.recover { raise 'snork' }
507
+ p.fail(StandardError.new('bork'))
508
+ expect { f.value }.to raise_error('snork')
509
+ end
510
+ end
511
+ end
512
+
513
+ describe '#fallback' do
514
+ context 'returns a new future that' do
515
+ it 'is resolved with the value of the fallback future when the source future fails' do
516
+ p1 = Promise.new
517
+ p2 = Promise.new
518
+ f = p1.future.fallback { p2.future }
519
+ p1.fail(error)
520
+ p2.fulfill('foo')
521
+ f.value.should == 'foo'
522
+ end
523
+
524
+ it 'yields the error to the block' do
525
+ p1 = Promise.new
526
+ p2 = Promise.new
527
+ f = p1.future.fallback do |error|
528
+ Future.resolved(error.message)
529
+ end
530
+ p1.fail(error)
531
+ f.value.should == error.message
532
+ end
533
+
534
+ it 'is resolved with the value of the source future when the source future fullfills' do
535
+ p1 = Promise.new
536
+ p2 = Promise.new
537
+ f = p1.future.fallback { p2.future }
538
+ p2.fulfill('bar')
539
+ p1.fulfill('foo')
540
+ f.value.should == 'foo'
541
+ end
542
+
543
+ it 'fails when the block raises an error' do
544
+ p = Promise.new
545
+ f = p.future.fallback { raise 'bork' }
546
+ p.fail(StandardError.new('splork'))
547
+ expect { f.value }.to raise_error('bork')
548
+ end
549
+
550
+ it 'fails when the fallback future fails' do
551
+ p1 = Promise.new
552
+ p2 = Promise.new
553
+ f = p1.future.fallback { p2.future }
554
+ p2.fail(StandardError.new('bork'))
555
+ p1.fail(StandardError.new('fnork'))
556
+ expect { f.value }.to raise_error('bork')
557
+ end
558
+ end
559
+ end
560
+
561
+ describe '.all' do
562
+ context 'returns a new future which' do
563
+ it 'is resolved when the source futures are resolved' do
564
+ p1 = Promise.new
565
+ p2 = Promise.new
566
+ f = Future.all(p1.future, p2.future)
567
+ p1.fulfill
568
+ f.should_not be_resolved
569
+ p2.fulfill
570
+ f.should be_resolved
571
+ end
572
+
573
+ it 'resolves when the source futures are resolved' do
574
+ sequence = []
575
+ p1 = Promise.new
576
+ p2 = Promise.new
577
+ p3 = Promise.new
578
+ f = Future.all(p1.future, p2.future, p3.future)
579
+ p1.future.on_value { sequence << 1 }
580
+ p2.future.on_value { sequence << 2 }
581
+ p3.future.on_value { sequence << 3 }
582
+ p2.fulfill
583
+ p1.fulfill
584
+ p3.fulfill
585
+ sequence.should == [2, 1, 3]
586
+ end
587
+
588
+ it 'returns an array of the values of the source futures, in order ' do
589
+ p1 = Promise.new
590
+ p2 = Promise.new
591
+ p3 = Promise.new
592
+ f = Future.all(p1.future, p2.future, p3.future)
593
+ p2.fulfill(2)
594
+ p1.fulfill(1)
595
+ p3.fulfill(3)
596
+ f.value.should == [1, 2, 3]
597
+ end
598
+
599
+ it 'fails if any of the source futures fail' do
600
+ p1 = Promise.new
601
+ p2 = Promise.new
602
+ p3 = Promise.new
603
+ p4 = Promise.new
604
+ f = Future.all(p1.future, p2.future, p3.future, p4.future)
605
+ p2.fulfill
606
+ p1.fail(StandardError.new('hurgh'))
607
+ p3.fail(StandardError.new('murgasd'))
608
+ p4.fulfill
609
+ expect { f.value }.to raise_error('hurgh')
610
+ f.should be_failed
611
+ end
612
+
613
+ it 'completes with an empty list when no futures are given' do
614
+ Future.all.value.should == []
615
+ end
616
+ end
617
+ end
618
+
619
+ describe '.first' do
620
+ context 'it returns a new future which' do
621
+ it 'is resolved when the first of the source futures is resolved' do
622
+ p1 = Promise.new
623
+ p2 = Promise.new
624
+ p3 = Promise.new
625
+ f = Future.first(p1.future, p2.future, p3.future)
626
+ p2.fulfill
627
+ f.should be_resolved
628
+ end
629
+
630
+ it 'fullfills with the value of the first source future' do
631
+ p1 = Promise.new
632
+ p2 = Promise.new
633
+ p3 = Promise.new
634
+ f = Future.first(p1.future, p2.future, p3.future)
635
+ p2.fulfill('foo')
636
+ f.value.should == 'foo'
637
+ end
638
+
639
+ it 'is unaffected by the fullfillment of the other futures' do
640
+ p1 = Promise.new
641
+ p2 = Promise.new
642
+ p3 = Promise.new
643
+ f = Future.first(p1.future, p2.future, p3.future)
644
+ p2.fulfill
645
+ p1.fulfill
646
+ p3.fulfill
647
+ f.value
648
+ end
649
+
650
+ it 'is unaffected by a future failing when at least one resolves' do
651
+ p1 = Promise.new
652
+ p2 = Promise.new
653
+ p3 = Promise.new
654
+ f = Future.first(p1.future, p2.future, p3.future)
655
+ p2.fail(error)
656
+ p1.fail(error)
657
+ p3.fulfill
658
+ expect { f.value }.to_not raise_error
659
+ end
660
+
661
+ it 'fails if all of the source futures fail' do
662
+ p1 = Promise.new
663
+ p2 = Promise.new
664
+ p3 = Promise.new
665
+ f = Future.first(p1.future, p2.future, p3.future)
666
+ p2.fail(error)
667
+ p1.fail(error)
668
+ p3.fail(error)
669
+ f.should be_failed
670
+ end
671
+
672
+ it 'fails with the error of the last future to fail' do
673
+ p1 = Promise.new
674
+ p2 = Promise.new
675
+ p3 = Promise.new
676
+ f = Future.first(p1.future, p2.future, p3.future)
677
+ p2.fail(StandardError.new('bork2'))
678
+ p1.fail(StandardError.new('bork1'))
679
+ p3.fail(StandardError.new('bork3'))
680
+ expect { f.value }.to raise_error('bork3')
681
+ end
682
+
683
+ it 'completes with nil when no futures are given' do
684
+ Future.first.value.should be_nil
685
+ end
686
+ end
687
+ end
688
+
689
+ describe '.resolved' do
690
+ context 'returns a future which' do
691
+ let :future do
692
+ described_class.resolved('hello world')
693
+ end
694
+
695
+ it 'is resolved' do
696
+ future.should be_resolved
697
+ end
698
+
699
+ it 'calls callbacks immediately' do
700
+ value = nil
701
+ future.on_value { |v| value = v }
702
+ value.should == 'hello world'
703
+ end
704
+
705
+ it 'does not block on #value' do
706
+ future.value.should == 'hello world'
707
+ end
708
+
709
+ it 'defaults to the value nil' do
710
+ described_class.resolved.value.should be_nil
711
+ end
712
+ end
713
+ end
714
+
715
+ describe '.failed' do
716
+ let :future do
717
+ described_class.failed(error)
718
+ end
719
+
720
+ context 'returns a future which' do
721
+ it 'is failed when created' do
722
+ future.should be_failed
723
+ end
724
+
725
+ it 'calls callbacks immediately' do
726
+ error = nil
727
+ future.on_failure { |e| error = e }
728
+ error.message.should == 'bork'
729
+ end
730
+
731
+ it 'does not block on #value' do
732
+ expect { future.value }.to raise_error('bork')
733
+ end
734
+ end
735
+ end
736
+ end
737
+ end