ione 1.0.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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