a-ruby-promise 0.0.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.
- checksums.yaml +7 -0
- data/lib/a-ruby-promise.rb +1 -0
- data/lib/promise.rb +95 -0
- data/lib/promise/version.rb +3 -0
- data/spec/aplus_spec.rb +46 -0
- data/spec/deferred.rb +16 -0
- data/spec/promise_spec.rb +50 -0
- data/spec/promises_aplus.rb +58 -0
- data/spec/promises_aplus/2_1_2_spec.rb +57 -0
- data/spec/promises_aplus/2_1_3_spec.rb +76 -0
- data/spec/promises_aplus/2_2_1_spec.rb +49 -0
- data/spec/promises_aplus/2_2_2_spec.rb +156 -0
- data/spec/promises_aplus/2_2_3_spec.rb +142 -0
- data/spec/promises_aplus/2_2_4_spec.rb +188 -0
- data/spec/promises_aplus/2_2_5_spec.rb +7 -0
- data/spec/promises_aplus/2_2_6_spec.rb +238 -0
- data/spec/promises_aplus/2_2_7_spec.rb +99 -0
- data/spec/spec_helper.rb +44 -0
- metadata +146 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative "../spec_helper"
|
3
|
+
require_relative "../promises_aplus"
|
4
|
+
|
5
|
+
dummy = { dummy: "dummy" } # we fulfill or reject with this when we don't intend to test against it
|
6
|
+
sentinel = { sentinel: "sentinel" } # a sentinel fulfillment value to test for with strict equality
|
7
|
+
|
8
|
+
describe "2.2.2: If `onFulfilled` is a function," do
|
9
|
+
include PromisesAplus
|
10
|
+
|
11
|
+
describe "2.2.2.1: it must be called after `promise` is fulfilled, with `promise`’s fulfillment value as its first argument." do
|
12
|
+
PromisesAplus.test_fulfilled(self, sentinel) do |promise, done|
|
13
|
+
promise.then ->(value) {
|
14
|
+
value.must_equal sentinel
|
15
|
+
done.call
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "2.2.2.2: it must not be called before `promise` is fulfilled" do
|
21
|
+
|
22
|
+
it "fulfilled after a delay" do
|
23
|
+
d = deferred
|
24
|
+
is_fulfilled = false
|
25
|
+
d.promise.then ->(v) {
|
26
|
+
assert_equal true, is_fulfilled
|
27
|
+
done!
|
28
|
+
}
|
29
|
+
|
30
|
+
Thread.new do
|
31
|
+
short_sleep
|
32
|
+
# Notice: flipped statements since we don't have delayed execution
|
33
|
+
is_fulfilled = true
|
34
|
+
d.resolve(dummy)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "never fulfilled" do
|
39
|
+
d = deferred
|
40
|
+
on_fulfilled_called = false
|
41
|
+
|
42
|
+
d.promise.then ->(v) {
|
43
|
+
on_fulfilled_called = true
|
44
|
+
done!
|
45
|
+
}
|
46
|
+
|
47
|
+
done = method(:done!)
|
48
|
+
Thread.new do
|
49
|
+
3.times { short_sleep }
|
50
|
+
assert_equal false, on_fulfilled_called
|
51
|
+
done.call
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "2.2.2.3: it must not be called more than once." do
|
57
|
+
it "already-fulfilled" do
|
58
|
+
times_called = 0
|
59
|
+
|
60
|
+
resolved(dummy).then(->(v) {
|
61
|
+
(times_called += 1).must_equal 1
|
62
|
+
done!
|
63
|
+
})
|
64
|
+
end
|
65
|
+
|
66
|
+
it "trying to fulfill a pending promise more than once, immediately" do
|
67
|
+
d = deferred
|
68
|
+
times_called = 0
|
69
|
+
|
70
|
+
d.promise.then(->(v) {
|
71
|
+
(times_called += 1).must_equal 1
|
72
|
+
done!
|
73
|
+
})
|
74
|
+
|
75
|
+
d.resolve(dummy)
|
76
|
+
d.resolve(dummy)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "trying to fulfill a pending promise more than once, delayed" do
|
80
|
+
d = deferred
|
81
|
+
times_called = 0
|
82
|
+
|
83
|
+
d.promise.then(->(v) {
|
84
|
+
assert_equal 1, (times_called += 1)
|
85
|
+
done!
|
86
|
+
})
|
87
|
+
|
88
|
+
Thread.new do
|
89
|
+
short_sleep
|
90
|
+
d.resolve(dummy)
|
91
|
+
d.resolve(dummy)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "trying to fulfill a pending promise more than once, immediately then delayed" do
|
96
|
+
d = deferred
|
97
|
+
times_called = 0
|
98
|
+
|
99
|
+
d.promise.then(->(v) {
|
100
|
+
(times_called += 1).must_equal 1
|
101
|
+
done!
|
102
|
+
})
|
103
|
+
|
104
|
+
d.resolve(dummy)
|
105
|
+
Thread.new do
|
106
|
+
short_sleep
|
107
|
+
d.resolve(dummy)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "when multiple `then` calls are made, spaced apart in time" do
|
112
|
+
d = deferred
|
113
|
+
times_called = [0, 0, 0]
|
114
|
+
|
115
|
+
d.promise.then(->(v) {
|
116
|
+
assert_equal 1, (times_called[0] += 1)
|
117
|
+
})
|
118
|
+
|
119
|
+
Thread.new do
|
120
|
+
short_sleep
|
121
|
+
d.promise.then(->(v) {
|
122
|
+
assert_equal 1, (times_called[1] += 1)
|
123
|
+
})
|
124
|
+
end
|
125
|
+
|
126
|
+
Thread.new do
|
127
|
+
2.times { short_sleep }
|
128
|
+
d.promise.then(->(v) {
|
129
|
+
assert_equal 1, (times_called[2] += 1)
|
130
|
+
done!
|
131
|
+
})
|
132
|
+
end
|
133
|
+
|
134
|
+
Thread.new do
|
135
|
+
3.times { short_sleep }
|
136
|
+
d.resolve(dummy)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
it "when `then` is interleaved with fulfillment" do
|
141
|
+
d = deferred
|
142
|
+
times_called = [0, 0]
|
143
|
+
|
144
|
+
d.promise.then(->(v) {
|
145
|
+
(times_called[0] += 1).must_equal 1
|
146
|
+
})
|
147
|
+
|
148
|
+
d.resolve(dummy)
|
149
|
+
|
150
|
+
d.promise.then(->(v) {
|
151
|
+
(times_called[1] += 1).must_equal 1
|
152
|
+
done!
|
153
|
+
})
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative "../spec_helper"
|
3
|
+
require_relative "../promises_aplus"
|
4
|
+
|
5
|
+
dummy = { dummy: "dummy" }
|
6
|
+
sentinel = { sentinel: "sentinel" }
|
7
|
+
|
8
|
+
describe "2.2.3: If `onRejected` is a function," do
|
9
|
+
include PromisesAplus
|
10
|
+
|
11
|
+
describe "2.2.3.1: it must be called after `promise` is rejected, with `promise`’s rejection reason as its first argument." do
|
12
|
+
PromisesAplus.test_rejected(self, sentinel) do |promise, done|
|
13
|
+
promise.then nil, ->(reason) {
|
14
|
+
reason.must_equal sentinel
|
15
|
+
done.call
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "2.2.3.2: it must not be called before `promise` is rejected" do
|
21
|
+
|
22
|
+
it "rejected after a delay" do
|
23
|
+
d = deferred
|
24
|
+
is_rejected = false
|
25
|
+
d.promise.then(nil, ->(r) {
|
26
|
+
is_rejected.must_equal true
|
27
|
+
done!
|
28
|
+
})
|
29
|
+
|
30
|
+
sleep 0.050
|
31
|
+
is_rejected = true
|
32
|
+
d.reject(dummy)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "never rejected" do
|
36
|
+
d = deferred
|
37
|
+
on_rejected_called = false
|
38
|
+
|
39
|
+
d.promise.then(nil, ->(r) {
|
40
|
+
on_rejected_called = true
|
41
|
+
done!
|
42
|
+
})
|
43
|
+
|
44
|
+
sleep 0.150
|
45
|
+
on_rejected_called.must_equal false
|
46
|
+
done!
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "2.2.3.3: it must not be called more than once." do
|
51
|
+
it "already-rejected" do
|
52
|
+
times_called = 0
|
53
|
+
|
54
|
+
rejected(dummy).then(nil, ->(r) {
|
55
|
+
(times_called += 1).must_equal 1
|
56
|
+
done!
|
57
|
+
})
|
58
|
+
end
|
59
|
+
|
60
|
+
it "trying to reject a pending promise more than once, immediately" do
|
61
|
+
d = deferred
|
62
|
+
times_called = 0
|
63
|
+
|
64
|
+
d.promise.then(nil, ->(r) {
|
65
|
+
(times_called += 1).must_equal 1
|
66
|
+
done!
|
67
|
+
})
|
68
|
+
|
69
|
+
d.reject(dummy)
|
70
|
+
d.reject(dummy)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "trying to reject a pending promise more than once, delayed" do
|
74
|
+
d = deferred
|
75
|
+
times_called = 0
|
76
|
+
|
77
|
+
d.promise.then(nil, ->(r) {
|
78
|
+
(times_called += 1).must_equal 1
|
79
|
+
done!
|
80
|
+
})
|
81
|
+
|
82
|
+
sleep 0.050
|
83
|
+
d.reject(dummy)
|
84
|
+
d.reject(dummy)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "trying to reject a pending promise more than once, immediately then delayed" do
|
88
|
+
d = deferred
|
89
|
+
times_called = 0
|
90
|
+
|
91
|
+
d.promise.then(nil, ->(r) {
|
92
|
+
(times_called += 1).must_equal 1
|
93
|
+
done!
|
94
|
+
})
|
95
|
+
|
96
|
+
d.reject(dummy)
|
97
|
+
|
98
|
+
sleep 0.050
|
99
|
+
d.reject(dummy)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "when multiple `then` calls are made, spaced apart in time" do
|
103
|
+
d = deferred
|
104
|
+
times_called = [0, 0, 0]
|
105
|
+
|
106
|
+
d.promise.then(nil, ->(r) {
|
107
|
+
(times_called[0] += 1).must_equal 1
|
108
|
+
})
|
109
|
+
|
110
|
+
sleep 0.050
|
111
|
+
d.promise.then(nil, ->(r) {
|
112
|
+
(times_called[1] += 1).must_equal 1
|
113
|
+
})
|
114
|
+
|
115
|
+
sleep 0.050
|
116
|
+
d.promise.then(nil, ->(r) {
|
117
|
+
(times_called[2] += 1).must_equal 1
|
118
|
+
done!
|
119
|
+
})
|
120
|
+
|
121
|
+
sleep 0.050
|
122
|
+
d.reject(dummy)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "when `then` is interleaved with rejection" do
|
126
|
+
d = deferred
|
127
|
+
|
128
|
+
times_called = [0, 0]
|
129
|
+
|
130
|
+
d.promise.then(nil, ->(r) {
|
131
|
+
(times_called[0] += 1).must_equal 1
|
132
|
+
})
|
133
|
+
|
134
|
+
d.reject(dummy)
|
135
|
+
|
136
|
+
d.promise.then(nil, ->(r) {
|
137
|
+
(times_called[1] += 1).must_equal 1
|
138
|
+
done!
|
139
|
+
})
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative "../spec_helper"
|
3
|
+
require_relative "../promises_aplus"
|
4
|
+
|
5
|
+
dummy = { dummy: "dummy" }
|
6
|
+
sentinel = { sentinel: "sentinel" }
|
7
|
+
|
8
|
+
describe "2.2.4: `onFulfilled` or `onRejected` must not be called until the execution context stack contains only platform code." do
|
9
|
+
include PromisesAplus
|
10
|
+
|
11
|
+
describe "`then` returns before the promise becomes fulfilled or rejected" do
|
12
|
+
before { done! and skip "don't know how to delay execution yet" }
|
13
|
+
|
14
|
+
PromisesAplus.test_fulfilled(self, sentinel) do |promise, done|
|
15
|
+
thenHasReturned = false
|
16
|
+
|
17
|
+
promise.then ->(v) {
|
18
|
+
thenHasReturned.must_equal true
|
19
|
+
done.call
|
20
|
+
}
|
21
|
+
|
22
|
+
thenHasReturned = true
|
23
|
+
end
|
24
|
+
|
25
|
+
PromisesAplus.test_rejected(self, dummy) do |promise, done|
|
26
|
+
thenHasReturned = false
|
27
|
+
|
28
|
+
promise.then nil, ->(r) {
|
29
|
+
thenHasReturned.must_equal true
|
30
|
+
done.call
|
31
|
+
}
|
32
|
+
|
33
|
+
thenHasReturned = true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "Clean-stack execution ordering tests (fulfillment case)" do
|
38
|
+
before { done! and skip "don't know how to delay execution yet" }
|
39
|
+
|
40
|
+
it "when `onFulfilled` is added immediately before the promise is fulfilled" do
|
41
|
+
d = deferred()
|
42
|
+
onFulfilledCalled = false
|
43
|
+
|
44
|
+
d.promise.then ->(v) do
|
45
|
+
onFulfilledCalled = true
|
46
|
+
end
|
47
|
+
|
48
|
+
d.resolve(dummy)
|
49
|
+
|
50
|
+
onFulfilledCalled.must_equal false
|
51
|
+
done!
|
52
|
+
end
|
53
|
+
|
54
|
+
it "when `onFulfilled` is added immediately after the promise is fulfilled" do
|
55
|
+
d = deferred()
|
56
|
+
onFulfilledCalled = false
|
57
|
+
|
58
|
+
d.resolve(dummy)
|
59
|
+
|
60
|
+
d.promise.then ->(v) do
|
61
|
+
onFulfilledCalled = true
|
62
|
+
end
|
63
|
+
|
64
|
+
onFulfilledCalled.must_equal false
|
65
|
+
done!
|
66
|
+
end
|
67
|
+
|
68
|
+
it "when one `onFulfilled` is added inside another `onFulfilled`" do
|
69
|
+
promise = resolved()
|
70
|
+
firstOnFulfilledFinished = false
|
71
|
+
|
72
|
+
promise.then ->(v) do
|
73
|
+
promise.then ->(v) do
|
74
|
+
firstOnFulfilledFinished.must_equal true
|
75
|
+
done!
|
76
|
+
end
|
77
|
+
firstOnFulfilledFinished = true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "when `onFulfilled` is added inside an `onRejected`" do
|
82
|
+
promise = rejected()
|
83
|
+
promise2 = resolved()
|
84
|
+
firstOnRejectedFinished = false
|
85
|
+
|
86
|
+
promise.then nil, ->(r) do
|
87
|
+
promise2.then ->(v) do
|
88
|
+
firstOnRejectedFinished.must_equal true
|
89
|
+
done!
|
90
|
+
end
|
91
|
+
firstOnRejectedFinished = true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "when the promise is fulfilled asynchronously" do
|
96
|
+
d = deferred()
|
97
|
+
firstStackFinished = false
|
98
|
+
|
99
|
+
f = Fiber.new do
|
100
|
+
d.resolve(dummy)
|
101
|
+
firstStackFinished = true
|
102
|
+
end
|
103
|
+
|
104
|
+
d.promise.then ->(v) do
|
105
|
+
firstStackFinished.must_equal true
|
106
|
+
done!
|
107
|
+
end
|
108
|
+
|
109
|
+
f.resume
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "Clean-stack execution ordering tests (rejection case)" do
|
114
|
+
before { done! and skip "don't know how to delay execution yet" }
|
115
|
+
|
116
|
+
it "when `onRejected` is added immediately before the promise is rejected" do
|
117
|
+
d = deferred()
|
118
|
+
onRejectedCalled = false
|
119
|
+
|
120
|
+
d.promise.then nil, ->(r) do
|
121
|
+
onRejectedCalled = true
|
122
|
+
end
|
123
|
+
|
124
|
+
d.reject(dummy)
|
125
|
+
|
126
|
+
onRejectedCalled.must_equal false
|
127
|
+
done!
|
128
|
+
end
|
129
|
+
|
130
|
+
it "when `onRejected` is added immediately after the promise is rejected" do
|
131
|
+
d = deferred()
|
132
|
+
onRejectedCalled = false
|
133
|
+
|
134
|
+
d.reject(dummy)
|
135
|
+
|
136
|
+
d.promise.then nil, ->(r) do
|
137
|
+
onRejectedCalled = true
|
138
|
+
end
|
139
|
+
|
140
|
+
onRejectedCalled.must_equal false
|
141
|
+
done!
|
142
|
+
end
|
143
|
+
|
144
|
+
it "when `onRejected` is added inside an `onFulfilled`" do
|
145
|
+
promise = resolved()
|
146
|
+
promise2 = rejected()
|
147
|
+
firstOnFulfilledFinished = false
|
148
|
+
|
149
|
+
promise.then ->(v) do
|
150
|
+
promise2.then nil, ->(r) do
|
151
|
+
firstOnFulfilledFinished.must_equal true
|
152
|
+
done!
|
153
|
+
end
|
154
|
+
firstOnFulfilledFinished = true
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
it "when one `onRejected` is added inside another `onRejected`" do
|
159
|
+
promise = rejected()
|
160
|
+
firstOnRejectedFinished = false
|
161
|
+
|
162
|
+
promise.then nil, ->(r) do
|
163
|
+
promise.then nil, ->(r) do
|
164
|
+
firstOnRejectedFinished.must_equal true
|
165
|
+
done!
|
166
|
+
end
|
167
|
+
firstOnRejectedFinished = true
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
it "when the promise is rejected asynchronously" do
|
172
|
+
d = deferred()
|
173
|
+
firstStackFinished = false
|
174
|
+
|
175
|
+
f = Fiber.new do
|
176
|
+
d.reject(dummy)
|
177
|
+
firstStackFinished = true
|
178
|
+
end
|
179
|
+
|
180
|
+
d.promise.then nil, ->(r) do
|
181
|
+
firstStackFinished.must_equal true
|
182
|
+
done!
|
183
|
+
end
|
184
|
+
|
185
|
+
f.resume
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|