Skip to content

Commit 457b838

Browse files
committed
Merge pull request #345 from ruby-concurrency/fix/promise-specs
Fix promise specs
2 parents 1da7c0e + 7bb72a7 commit 457b838

File tree

1 file changed

+76
-66
lines changed

1 file changed

+76
-66
lines changed

spec/concurrent/promise_spec.rb

Lines changed: 76 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ module Concurrent
1313
let!(:rejected_reason) { StandardError.new('mojo jojo') }
1414

1515
let(:pending_subject) do
16-
Promise.new(executor: executor){ sleep(0.1); fulfilled_value }.execute
16+
executor = Concurrent::SingleThreadExecutor.new
17+
executor.post{ sleep(5) }
18+
Promise.execute(executor: executor){ fulfilled_value }
1719
end
1820

1921
let(:fulfilled_subject) do
20-
Promise.fulfill(fulfilled_value, executor: executor)
22+
Promise.new(executor: executor){ fulfilled_value }.execute.tap{ sleep(0.1) }
2123
end
2224

2325
let(:rejected_subject) do
24-
Promise.reject(rejected_reason, executor: executor)
26+
Promise.new(executor: executor){ raise rejected_reason }.execute.tap{ sleep(0.1) }
2527
end
2628

2729
it_should_behave_like :ivar do
@@ -97,28 +99,27 @@ def get_ivar_from_args(opts)
9799

98100
describe '.new' do
99101
it 'should return an unscheduled Promise' do
100-
p = Promise.new(executor: executor){ nil }
102+
p = Promise.new(executor: :immediate){ nil }
101103
expect(p).to be_unscheduled
102104
end
103105
end
104106

105107
describe '.execute' do
106108
it 'creates a new Promise' do
107-
p = Promise.execute(executor: executor){ nil }
109+
p = Promise.execute(executor: :immediate){ nil }
108110
expect(p).to be_a(Promise)
109111
end
110112

111113
it 'passes the block to the new Promise' do
112-
p = Promise.execute(executor: executor){ 20 }
113-
sleep(0.1)
114+
p = Promise.execute(executor: :immediate){ 20 }
114115
expect(p.value).to eq 20
115116
end
116117

117118
it 'calls #execute on the new Promise' do
118119
p = double('promise')
119-
allow(Promise).to receive(:new).with({executor: executor}).and_return(p)
120+
allow(Promise).to receive(:new).with(any_args).and_return(p)
120121
expect(p).to receive(:execute).with(no_args)
121-
Promise.execute(executor: executor){ nil }
122+
Promise.execute(executor: :immediate){ nil }
122123
end
123124
end
124125
end
@@ -128,52 +129,70 @@ def get_ivar_from_args(opts)
128129
context 'unscheduled' do
129130

130131
it 'sets the promise to :pending' do
131-
p = Promise.new(executor: executor){ sleep(0.1) }.execute
132+
start_latch = CountDownLatch.new
133+
end_latch = CountDownLatch.new
134+
p = Promise.new(executor: executor) do
135+
start_latch.count_down
136+
end_latch.wait(1)
137+
end
138+
start_latch.wait(1)
139+
p.execute
132140
expect(p).to be_pending
141+
end_latch.count_down
133142
end
134143

135144
it 'posts the block given in construction' do
136-
expect(executor).to receive(:post).with(any_args)
145+
executor = ImmediateExecutor.new
146+
expect(executor).to receive(:post).with(any_args).and_call_original
137147
Promise.new(executor: executor){ nil }.execute
138148
end
139149
end
140150

141151
context 'pending' do
142152

143153
it 'sets the promise to :pending' do
144-
p = pending_subject.execute
154+
latch = CountDownLatch.new
155+
p = Promise.new{ latch.wait(1) }.execute
145156
expect(p).to be_pending
157+
latch.count_down
146158
end
147159

148-
it 'does not posts again' do
160+
it 'does not post again' do
161+
executor = SimpleExecutorService.new
149162
expect(executor).to receive(:post).with(any_args).once
150-
pending_subject.execute
163+
164+
latch = CountDownLatch.new
165+
p = Promise.new(executor: executor){ latch.wait(1) }.execute
166+
167+
10.times { p.execute }
168+
latch.count_down
151169
end
152170
end
153171

154172
describe 'with children' do
155173

156-
let(:root) { Promise.new(executor: executor){ sleep(0.1); nil } }
157-
let(:c1) { root.then { sleep(0.1); nil } }
158-
let(:c2) { root.then { sleep(0.1); nil } }
159-
let(:c2_1) { c2.then { sleep(0.1); nil } }
174+
let(:start_latch) { CountDownLatch.new(4) }
175+
let(:end_latch) { CountDownLatch.new(1) }
176+
let(:root) { Promise.new(executor: executor){ start_latch.count_down; end_latch.wait(5) } }
177+
let(:c1) { root.then { start_latch.count_down; end_latch.wait(5) } }
178+
let(:c2) { root.then { start_latch.count_down; end_latch.wait(5) } }
179+
let(:c2_1) { c2.then { start_latch.count_down; end_latch.wait(5) } }
160180

161181
context 'when called on the root' do
162182
it 'should set all promises to :pending' do
163183
root.execute
164-
165-
expect(c1).to be_pending
166-
expect(c2).to be_pending
167-
expect(c2_1).to be_pending
184+
start_latch.wait(1)
168185
[root, c1, c2, c2_1].each { |p| expect(p).to be_pending }
186+
end_latch.count_down
169187
end
170188
end
171189

172190
context 'when called on a child' do
173191
it 'should set all promises to :pending' do
174192
c2_1.execute
175-
193+
start_latch.wait(1)
176194
[root, c1, c2, c2_1].each { |p| expect(p).to be_pending }
195+
end_latch.count_down
177196
end
178197
end
179198
end
@@ -298,38 +317,38 @@ def get_ivar_from_args(opts)
298317
end
299318

300319
it 'succeeds if both promises succeed' do
301-
child = Promise.new(executor: executor) { 1 }.
302-
flat_map { |v| Promise.new(executor: executor) { v + 10 } }.execute.wait
320+
child = Promise.new(executor: :immediate) { 1 }.
321+
flat_map { |v| Promise.new(executor: :immediate) { v + 10 } }.execute.wait
303322

304323
expect(child.value!).to eq(11)
305324
end
306325

307326
it 'fails if the left promise fails' do
308-
child = Promise.new(executor: executor) { fail }.
309-
flat_map { |v| Promise.new(executor: executor) { v + 10 } }.execute.wait
327+
child = Promise.new(executor: :immediate) { fail }.
328+
flat_map { |v| Promise.new(executor: :immediate) { v + 10 } }.execute.wait
310329

311330
expect(child).to be_rejected
312331
end
313332

314333
it 'fails if the right promise fails' do
315-
child = Promise.new(executor: executor) { 1 }.
316-
flat_map { |v| Promise.new(executor: executor) { fail } }.execute.wait
334+
child = Promise.new(executor: :immediate) { 1 }.
335+
flat_map { |v| Promise.new(executor: :immediate) { fail } }.execute.wait
317336

318337
expect(child).to be_rejected
319338
end
320339

321340
it 'fails if the generating block fails' do
322-
child = Promise.new(executor: executor) { }.flat_map { fail }.execute.wait
341+
child = Promise.new(executor: :immediate) { }.flat_map { fail }.execute.wait
323342

324343
expect(child).to be_rejected
325344
end
326345

327346
end
328347

329348
describe '#zip' do
330-
let(:promise1) { Promise.new(executor: executor) { 1 } }
331-
let(:promise2) { Promise.new(executor: executor) { 2 } }
332-
let(:promise3) { Promise.new(executor: executor) { [3] } }
349+
let(:promise1) { Promise.new(executor: :immediate) { 1 } }
350+
let(:promise2) { Promise.new(executor: :immediate) { 2 } }
351+
let(:promise3) { Promise.new(executor: :immediate) { [3] } }
333352

334353
it 'yields the results as an array' do
335354
composite = promise1.zip(promise2, promise3).execute.wait
@@ -345,9 +364,9 @@ def get_ivar_from_args(opts)
345364
end
346365

347366
describe '.zip' do
348-
let(:promise1) { Promise.new(executor: executor) { 1 } }
349-
let(:promise2) { Promise.new(executor: executor) { 2 } }
350-
let(:promise3) { Promise.new(executor: executor) { [3] } }
367+
let(:promise1) { Promise.new(executor: :immediate) { 1 } }
368+
let(:promise2) { Promise.new(executor: :immediate) { 2 } }
369+
let(:promise3) { Promise.new(executor: :immediate) { [3] } }
351370

352371
it 'yields the results as an array' do
353372
composite = Promise.zip(promise1, promise2, promise3).execute.wait
@@ -364,9 +383,9 @@ def get_ivar_from_args(opts)
364383

365384
describe 'aggregators' do
366385

367-
let(:promise1) { Promise.new(executor: executor) { 1 } }
368-
let(:promise2) { Promise.new(executor: executor) { 2 } }
369-
let(:promise3) { Promise.new(executor: executor) { [3] } }
386+
let(:promise1) { Promise.new(executor: :immediate) { 1 } }
387+
let(:promise2) { Promise.new(executor: :immediate) { 2 } }
388+
let(:promise3) { Promise.new(executor: :immediate) { [3] } }
370389

371390
describe '.all?' do
372391

@@ -386,7 +405,7 @@ def get_ivar_from_args(opts)
386405

387406
composite = Promise.all?(promise1, promise2, promise3).
388407
then { counter.up; latch.count_down }.
389-
rescue { counter.down; latch.count_down }.
408+
rescue { counter.down; latch.count_down }.
390409
execute
391410

392411
latch.wait(1)
@@ -400,7 +419,7 @@ def get_ivar_from_args(opts)
400419

401420
composite = Promise.all?.
402421
then { counter.up; latch.count_down }.
403-
rescue { counter.down; latch.count_down }.
422+
rescue { counter.down; latch.count_down }.
404423
execute
405424

406425
latch.wait(1)
@@ -414,7 +433,7 @@ def get_ivar_from_args(opts)
414433

415434
composite = Promise.all?(promise1, promise2, rejected_subject, promise3).
416435
then { counter.up; latch.count_down }.
417-
rescue { counter.down; latch.count_down }.
436+
rescue { counter.down; latch.count_down }.
418437
execute
419438

420439
latch.wait(1)
@@ -441,7 +460,7 @@ def get_ivar_from_args(opts)
441460

442461
composite = Promise.any?(promise1, promise2, rejected_subject, promise3).
443462
then { counter.up; latch.count_down }.
444-
rescue { counter.down; latch.count_down }.
463+
rescue { counter.down; latch.count_down }.
445464
execute
446465

447466
latch.wait(1)
@@ -455,7 +474,7 @@ def get_ivar_from_args(opts)
455474

456475
composite = Promise.any?.
457476
then { counter.up; latch.count_down }.
458-
rescue { counter.down; latch.count_down }.
477+
rescue { counter.down; latch.count_down }.
459478
execute
460479

461480
latch.wait(1)
@@ -469,7 +488,7 @@ def get_ivar_from_args(opts)
469488

470489
composite = Promise.any?(rejected_subject, rejected_subject, rejected_subject, rejected_subject).
471490
then { counter.up; latch.count_down }.
472-
rescue { counter.down; latch.count_down }.
491+
rescue { counter.down; latch.count_down }.
473492
execute
474493

475494
latch.wait(1)
@@ -500,7 +519,7 @@ def get_ivar_from_args(opts)
500519
end
501520

502521
it 'can be called with a block' do
503-
p = Promise.new(executor: executor)
522+
p = Promise.new(executor: :immediate)
504523
ch = p.then(&:to_s)
505524
p.set { :value }
506525

@@ -533,46 +552,40 @@ def get_ivar_from_args(opts)
533552

534553
it 'passes the result of each block to all its children' do
535554
expected = nil
536-
Promise.new(executor: executor){ 20 }.then{ |result| expected = result }.execute
537-
sleep(0.1)
555+
Promise.new(executor: :immediate){ 20 }.then{ |result| expected = result }.execute
538556
expect(expected).to eq 20
539557
end
540558

541559
it 'sets the promise value to the result if its block' do
542-
root = Promise.new(executor: executor){ 20 }
560+
root = Promise.new(executor: :immediate){ 20 }
543561
p = root.then{ |result| result * 2}.execute
544-
sleep(0.1)
545562
expect(root.value).to eq 20
546563
expect(p.value).to eq 40
547564
end
548565

549566
it 'sets the promise state to :fulfilled if the block completes' do
550-
p = Promise.new(executor: executor){ 10 * 2 }.then{|result| result * 2}.execute
551-
sleep(0.1)
567+
p = Promise.new(executor: :immediate){ 10 * 2 }.then{|result| result * 2}.execute
552568
expect(p).to be_fulfilled
553569
end
554570

555571
it 'passes the last result through when a promise has no block' do
556572
expected = nil
557-
Promise.new(executor: executor){ 20 }.then(Proc.new{}).then{|result| expected = result}.execute
558-
sleep(0.1)
573+
Promise.new(executor: :immediate){ 20 }.then(Proc.new{}).then{|result| expected = result}.execute
559574
expect(expected).to eq 20
560575
end
561576

562577
it 'uses result as fulfillment value when a promise has no block' do
563-
p = Promise.new(executor: executor){ 20 }.then(Proc.new{}).execute
564-
sleep(0.1)
578+
p = Promise.new(executor: :immediate){ 20 }.then(Proc.new{}).execute
565579
expect(p.value).to eq 20
566580
end
567581

568582
it 'can manage long chain' do
569-
root = Promise.new(executor: executor){ 20 }
583+
root = Promise.new(executor: :immediate){ 20 }
570584
p1 = root.then { |b| b * 3 }
571585
p2 = root.then { |c| c + 2 }
572586
p3 = p1.then { |d| d + 7 }
573587

574588
root.execute
575-
sleep(0.1)
576589

577590
expect(root.value).to eq 20
578591
expect(p1.value).to eq 60
@@ -585,27 +598,24 @@ def get_ivar_from_args(opts)
585598

586599
it 'passes the reason to all its children' do
587600
expected = nil
588-
Promise.new(executor: executor){ raise ArgumentError }.then(Proc.new{ |reason| expected = reason }).execute
589-
sleep(0.1)
601+
handler = proc { |reason| expected = reason }
602+
Promise.new(executor: :immediate){ raise ArgumentError }.then(handler).execute
590603
expect(expected).to be_a ArgumentError
591604
end
592605

593606
it 'sets the promise value to the result if its block' do
594-
root = Promise.new(executor: executor){ raise ArgumentError }
607+
root = Promise.new(executor: :immediate){ raise ArgumentError }
595608
p = root.then(Proc.new{ |reason| 42 }).execute
596-
sleep(0.1)
597609
expect(p.value).to eq 42
598610
end
599611

600612
it 'sets the promise state to :rejected if the block completes' do
601-
p = Promise.new(executor: executor){ raise ArgumentError }.execute
602-
sleep(0.1)
613+
p = Promise.new(executor: :immediate){ raise ArgumentError }.execute
603614
expect(p).to be_rejected
604615
end
605616

606617
it 'uses reason as rejection reason when a promise has no rescue callable' do
607-
p = Promise.new(executor: ImmediateExecutor.new){ raise ArgumentError }.then{ |val| val }.execute
608-
sleep(0.1)
618+
p = Promise.new(executor: :immediate){ raise ArgumentError }.then{ |val| val }.execute
609619
expect(p).to be_rejected
610620
expect(p.reason).to be_a ArgumentError
611621
end

0 commit comments

Comments
 (0)