aws-flow-core 1.0.0
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.
- data/Gemfile +9 -0
- data/LICENSE.TXT +15 -0
- data/NOTICE.TXT +14 -0
- data/Rakefile +27 -0
- data/aws-flow-core.gemspec +12 -0
- data/lib/aws/flow.rb +26 -0
- data/lib/aws/flow/async_backtrace.rb +134 -0
- data/lib/aws/flow/async_scope.rb +195 -0
- data/lib/aws/flow/begin_rescue_ensure.rb +386 -0
- data/lib/aws/flow/fiber.rb +77 -0
- data/lib/aws/flow/flow_utils.rb +50 -0
- data/lib/aws/flow/future.rb +109 -0
- data/lib/aws/flow/implementation.rb +151 -0
- data/lib/aws/flow/simple_dfa.rb +85 -0
- data/lib/aws/flow/tasks.rb +405 -0
- data/test/aws/async_backtrace_spec.rb +41 -0
- data/test/aws/async_scope_spec.rb +118 -0
- data/test/aws/begin_rescue_ensure_spec.rb +665 -0
- data/test/aws/external_task_spec.rb +197 -0
- data/test/aws/factories.rb +52 -0
- data/test/aws/fiber_condition_variable_spec.rb +163 -0
- data/test/aws/fiber_spec.rb +78 -0
- data/test/aws/flow_spec.rb +255 -0
- data/test/aws/future_spec.rb +210 -0
- data/test/aws/rubyflow.rb +22 -0
- data/test/aws/simple_dfa_spec.rb +63 -0
- data/test/aws/spec_helper.rb +36 -0
- metadata +85 -0
@@ -0,0 +1,665 @@
|
|
1
|
+
##
|
2
|
+
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
5
|
+
# You may not use this file except in compliance with the License.
|
6
|
+
# A copy of the License is located at
|
7
|
+
#
|
8
|
+
# http://aws.amazon.com/apache2.0
|
9
|
+
#
|
10
|
+
# or in the "license" file accompanying this file. This file is distributed
|
11
|
+
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
12
|
+
# express or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
##
|
15
|
+
|
16
|
+
|
17
|
+
def change_test(options = {})
|
18
|
+
subject { this_bre }
|
19
|
+
its(:heirs) { options[:heirs] ? (should_not be_empty) : (should be_empty) }
|
20
|
+
its(:nonDaemonHeirsCount) { should == options[:nonDaemonHeirsCount] }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe BeginRescueEnsure do
|
24
|
+
let(:condition) { FiberConditionVariable.new }
|
25
|
+
let(:flowfactory) { FlowFactory.new }
|
26
|
+
|
27
|
+
def make_trying_BRE(begin_tasks = [])
|
28
|
+
scope = AsyncScope.new do
|
29
|
+
@error_handler = error_handler do |t|
|
30
|
+
t.begin do
|
31
|
+
begin_tasks.each { |task| task.call}
|
32
|
+
condition.wait
|
33
|
+
end
|
34
|
+
t.rescue(IOError) {|x| condition.wait }
|
35
|
+
t.rescue(CancellationException) { |x| x }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
scope.eventLoop
|
39
|
+
@error_handler
|
40
|
+
end
|
41
|
+
|
42
|
+
def make_catching_BRE(begin_tasks = [])
|
43
|
+
scope = AsyncScope.new do
|
44
|
+
@error_handler = error_handler do |t|
|
45
|
+
t.begin do
|
46
|
+
raise IOError
|
47
|
+
end
|
48
|
+
t.rescue(IOError) do |x|
|
49
|
+
begin_tasks.each { |task| task.call }
|
50
|
+
condition.wait
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
scope.eventLoop
|
55
|
+
@error_handler
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:trying_BRE) do
|
59
|
+
make_trying_BRE
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:catching_BRE) do
|
63
|
+
make_catching_BRE
|
64
|
+
end
|
65
|
+
context "BeginRescueEnsure in the :trying state" do
|
66
|
+
subject { trying_BRE }
|
67
|
+
its(:heirs) { should_not be_empty }
|
68
|
+
its(:nonDaemonHeirsCount) { should == 1 }
|
69
|
+
|
70
|
+
context "which adds a task," do
|
71
|
+
let(:trying_BRE_with_one_task) do
|
72
|
+
make_trying_BRE([lambda { task { condition.wait } }])
|
73
|
+
end
|
74
|
+
let(:first_task) { trying_BRE_with_one_task.heirs.first }
|
75
|
+
subject { trying_BRE_with_one_task }
|
76
|
+
its(:heirs) { should_not be_empty }
|
77
|
+
its(:nonDaemonHeirsCount) { should == 2 }
|
78
|
+
|
79
|
+
context "and then has the task status changed," do
|
80
|
+
context "and cancel the BRE" do
|
81
|
+
let(:this_bre) { trying_BRE_with_one_task.cancel(StandardError.new); trying_BRE_with_one_task }
|
82
|
+
# As a note: we trigger the rescue task, so we have a task in the heirs
|
83
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 1
|
84
|
+
end
|
85
|
+
|
86
|
+
context "and fail the the task" do
|
87
|
+
let(:this_bre) { expect { trying_BRE_with_one_task.fail(first_task, StandardError.new) }.to raise_error StandardError; trying_BRE_with_one_task }
|
88
|
+
change_test :heirs => false, :nonDaemonHeirsCount => 0
|
89
|
+
end
|
90
|
+
|
91
|
+
context "and remove the task " do
|
92
|
+
let(:this_bre) { trying_BRE_with_one_task.remove(first_task); trying_BRE_with_one_task }
|
93
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 1
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "which adds two tasks" do
|
99
|
+
let(:trying_BRE_with_two_tasks) do
|
100
|
+
make_trying_BRE([lambda { task { condition.wait }}, lambda { task { condition.wait }}])
|
101
|
+
end
|
102
|
+
let(:first_task) { trying_BRE_with_two_tasks.heirs.first }
|
103
|
+
subject { trying_BRE_with_two_tasks }
|
104
|
+
its(:heirs) { should_not be_empty }
|
105
|
+
its(:nonDaemonHeirsCount) { should == 3 }
|
106
|
+
|
107
|
+
context "And then we change the task status" do
|
108
|
+
context "and cancel the BRE" do
|
109
|
+
let(:this_bre) { trying_BRE_with_two_tasks.cancel(StandardError.new); trying_BRE_with_two_tasks }
|
110
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 1
|
111
|
+
end
|
112
|
+
|
113
|
+
context "and fail the the task" do
|
114
|
+
let(:this_bre) { expect { trying_BRE_with_two_tasks.fail(first_task, StandardError.new) }.to raise_error StandardError; trying_BRE_with_two_tasks }
|
115
|
+
change_test :heirs => false, :nonDaemonHeirsCount => 0
|
116
|
+
end
|
117
|
+
|
118
|
+
context "and remove the task " do
|
119
|
+
let(:this_bre) { trying_BRE_with_two_tasks.remove(first_task); trying_BRE_with_two_tasks }
|
120
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 2
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "BeginRescueEnsure in the :catching state" do
|
127
|
+
subject { catching_BRE}
|
128
|
+
its(:heirs) { should_not be_empty }
|
129
|
+
its(:nonDaemonHeirsCount) { should == 1 }
|
130
|
+
|
131
|
+
context "which adds a task," do
|
132
|
+
let(:catching_BRE_with_one_task) do
|
133
|
+
make_catching_BRE([lambda { task { condition.wait } }])
|
134
|
+
end
|
135
|
+
let(:first_task) { catching_BRE_with_one_task.heirs.first }
|
136
|
+
subject { catching_BRE_with_one_task }
|
137
|
+
its(:heirs) { should_not be_empty }
|
138
|
+
its(:nonDaemonHeirsCount) { should == 2 }
|
139
|
+
|
140
|
+
context "and then has the task status changed," do
|
141
|
+
context "and cancel the BRE" do
|
142
|
+
let(:this_bre) { catching_BRE_with_one_task.cancel(StandardError.new); catching_BRE_with_one_task }
|
143
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 2
|
144
|
+
end
|
145
|
+
|
146
|
+
context "and fail the the task" do
|
147
|
+
let(:this_bre) { expect { catching_BRE_with_one_task.fail(first_task, StandardError.new) }.to raise_error StandardError; catching_BRE_with_one_task }
|
148
|
+
change_test :heirs => false, :nonDaemonHeirsCount => 0
|
149
|
+
end
|
150
|
+
|
151
|
+
context "and remove the task " do
|
152
|
+
let(:this_bre) { catching_BRE_with_one_task.remove(first_task); catching_BRE_with_one_task }
|
153
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 1
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "which adds two tasks" do
|
159
|
+
let(:catching_BRE_with_two_tasks) do
|
160
|
+
make_catching_BRE([lambda { task { condition.wait } },lambda { task { condition.wait } } ])
|
161
|
+
end
|
162
|
+
let(:first_task) { catching_BRE_with_two_tasks.heirs.first }
|
163
|
+
subject { catching_BRE_with_two_tasks }
|
164
|
+
its(:heirs) { should_not be_empty }
|
165
|
+
its(:nonDaemonHeirsCount) { should == 3 }
|
166
|
+
|
167
|
+
context "And then we change the task status" do
|
168
|
+
context "and cancel the BRE" do
|
169
|
+
let(:this_bre) { catching_BRE_with_two_tasks.cancel(StandardError.new); catching_BRE_with_two_tasks }
|
170
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 3
|
171
|
+
end
|
172
|
+
|
173
|
+
context "and fail the the task" do
|
174
|
+
let(:this_bre) { expect { catching_BRE_with_two_tasks.fail(first_task, StandardError.new) }.to raise_error StandardError; catching_BRE_with_two_tasks }
|
175
|
+
change_test :heirs => false, :nonDaemonHeirsCount => 0
|
176
|
+
end
|
177
|
+
|
178
|
+
context "and remove the task " do
|
179
|
+
let(:this_bre) { catching_BRE_with_two_tasks.remove(first_task); catching_BRE_with_two_tasks }
|
180
|
+
change_test :heirs => true, :nonDaemonHeirsCount => 2
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
before(:each) { class RescueTestingError < StandardError; end }
|
188
|
+
let(:trace) { [] }
|
189
|
+
let(:flowfactory) { FlowFactory.new }
|
190
|
+
let(:scope) { flowfactory.generate_scope }
|
191
|
+
|
192
|
+
it "makes sure that error handling without resuming does nothing" do
|
193
|
+
|
194
|
+
scope = AsyncScope.new do
|
195
|
+
_error_handler do |t|
|
196
|
+
t.begin { trace << :in_the_begin }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
trace.should == []
|
200
|
+
end
|
201
|
+
|
202
|
+
{:Task => lambda { |&x| task(&x) }, :DaemonTask => lambda { |&x| daemon_task(&x) }}.each_pair do |name, task_creation|
|
203
|
+
|
204
|
+
# TODO: Reinstate this test, if it is easy enough
|
205
|
+
#
|
206
|
+
# it "makes sure that error handling with a #{name} free floating raises" do
|
207
|
+
# scope = AsyncScope.new do
|
208
|
+
# error_handler do |t|
|
209
|
+
# t.begin { trace << :in_the_begin }
|
210
|
+
# t.rescue(RescueTestingError) {}
|
211
|
+
# task_creation.call { trace << :in_the_daemon }
|
212
|
+
# end
|
213
|
+
# end
|
214
|
+
# expect { scope.eventLoop }.to raise_error NoContextException, /You have not scoped this task correctly!/
|
215
|
+
# end
|
216
|
+
|
217
|
+
it "make sure that #{name} in a BRE runs correctly, and that the BRE supports signal " do
|
218
|
+
condition = FiberConditionVariable.new
|
219
|
+
scope = AsyncScope.new do
|
220
|
+
error_handler do |t|
|
221
|
+
t.begin do
|
222
|
+
trace << :in_the_begin
|
223
|
+
task_creation.call { trace << :in_the_daemon; condition.signal }
|
224
|
+
condition.wait
|
225
|
+
end
|
226
|
+
t.rescue(RescueTestingError) {}
|
227
|
+
end
|
228
|
+
end
|
229
|
+
scope.eventLoop
|
230
|
+
trace.should == [:in_the_begin, :in_the_daemon]
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
it "makes sure that a BRE can finish with a daemon task still alive" do
|
235
|
+
condition = FiberConditionVariable.new
|
236
|
+
scope = AsyncScope.new do
|
237
|
+
error_handler do |t|
|
238
|
+
t.begin do
|
239
|
+
trace << :in_the_begin
|
240
|
+
@daemon_task = daemon_task { condition.wait; :should_never_be_hit }
|
241
|
+
end
|
242
|
+
t.rescue(RescueTestingError) {}
|
243
|
+
end
|
244
|
+
end
|
245
|
+
scope.eventLoop
|
246
|
+
trace.should == [:in_the_begin]
|
247
|
+
end
|
248
|
+
|
249
|
+
# TODO Reinstate this test, after fixing it so it doesn't add to BRE directly
|
250
|
+
# it "makes sure that a daemon task can cancel a BRE" do
|
251
|
+
# flowfactory = FlowFactory.new
|
252
|
+
# condition = FiberConditionVariable.new
|
253
|
+
# scope = flowfactory.generate_scope
|
254
|
+
# bre = flowfactory.generate_BRE(:begin => lambda { condition.wait })
|
255
|
+
# daemon_task = DaemonTask.new(bre) { condition.wait }
|
256
|
+
# task = Task.new(bre) { condition.wait }
|
257
|
+
# bre << task
|
258
|
+
# bre << daemon_task
|
259
|
+
# scope.eventLoop
|
260
|
+
# bre.remove(task)
|
261
|
+
# scope.eventLoop
|
262
|
+
# trace.should == []
|
263
|
+
# end
|
264
|
+
|
265
|
+
|
266
|
+
# TODO Reinstate this test, after fixing it so it doesn't add to BRE directly
|
267
|
+
# it "makes sure that a daemon task cancelling does not override other fails" do
|
268
|
+
# flowfactory = FlowFactory.new
|
269
|
+
# condition = FiberConditionVariable.new
|
270
|
+
# scope = flowfactory.generate_scope
|
271
|
+
# bre = flowfactory.generate_BRE(:begin => lambda { condition.wait })
|
272
|
+
# daemon_task = DaemonTask.new(bre) { condition.wait }
|
273
|
+
# task = Task.new(bre) { condition.wait }
|
274
|
+
# bre << daemon_task
|
275
|
+
# bre << task
|
276
|
+
# scope.eventLoop
|
277
|
+
# daemon_task.cancel(StandardError)
|
278
|
+
# bre.fail(task, RescueTestingError)
|
279
|
+
# expect { debugger; scope.eventLoop }.to raise_error RescueTestingError
|
280
|
+
# trace.should == []
|
281
|
+
# end
|
282
|
+
|
283
|
+
# TODO Fix these up with the FlowFactory
|
284
|
+
it "makes sure that creating a task in the rescue block will run it" do
|
285
|
+
scope = AsyncScope.new do
|
286
|
+
error_handler do |t|
|
287
|
+
t.begin { raise RescueTestingError}
|
288
|
+
|
289
|
+
t.rescue(RescueTestingError) do
|
290
|
+
trace << :in_the_rescue
|
291
|
+
task { trace << :in_the_task }
|
292
|
+
end
|
293
|
+
end
|
294
|
+
scope.eventLoop
|
295
|
+
trace.should == [:in_the_beginning, :in_the_task]
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
it "makes sure that creating a task inside a BRE block will run it" do
|
300
|
+
scope = AsyncScope.new do
|
301
|
+
error_handler do |t|
|
302
|
+
t.begin do
|
303
|
+
trace << :in_the_beginning
|
304
|
+
task { trace << :in_the_task }
|
305
|
+
end
|
306
|
+
t.rescue(StandardError) {|x| }
|
307
|
+
end
|
308
|
+
end
|
309
|
+
scope.eventLoop
|
310
|
+
trace.should == [:in_the_beginning, :in_the_task]
|
311
|
+
end
|
312
|
+
|
313
|
+
it "ensures you can have error handling without ensure" do
|
314
|
+
scope = AsyncScope.new do
|
315
|
+
error_handler do |t|
|
316
|
+
t.begin { trace << :in_the_begin }
|
317
|
+
t.rescue(StandardError) { trace << :in_the_rescue }
|
318
|
+
end
|
319
|
+
end
|
320
|
+
scope.eventLoop
|
321
|
+
trace.should == [:in_the_begin]
|
322
|
+
end
|
323
|
+
|
324
|
+
it "ensures that rescue picks the first matching rescue block" do
|
325
|
+
scope = AsyncScope.new do
|
326
|
+
@error_handler = _error_handler do |t|
|
327
|
+
t.begin do
|
328
|
+
trace << :in_the_begin
|
329
|
+
raise RescueTestingError
|
330
|
+
end
|
331
|
+
t.rescue(RescueTestingError) { trace << :in_the_rescue }
|
332
|
+
t.rescue(StandardError) { trace << :in_the_bad_rescue }
|
333
|
+
end
|
334
|
+
end
|
335
|
+
scope.eventLoop
|
336
|
+
trace.should == [:in_the_begin, :in_the_rescue]
|
337
|
+
end
|
338
|
+
|
339
|
+
it "ensures that rescue blocks trickle to the first applicable block" do
|
340
|
+
scope = AsyncScope.new do
|
341
|
+
error_handler do |t|
|
342
|
+
t.begin do
|
343
|
+
trace << :in_the_begin
|
344
|
+
raise RescueTestingError
|
345
|
+
end
|
346
|
+
t.rescue(IOError) { trace << :bad_rescue }
|
347
|
+
t.rescue(RescueTestingError) { trace << :in_the_rescue }
|
348
|
+
end
|
349
|
+
end
|
350
|
+
scope.eventLoop
|
351
|
+
trace.should == [:in_the_begin, :in_the_rescue]
|
352
|
+
end
|
353
|
+
|
354
|
+
it "ensures that the same rescue exception twice causes an exception" do
|
355
|
+
scope = AsyncScope.new do
|
356
|
+
error_handler do |t|
|
357
|
+
t.begin {}
|
358
|
+
t.rescue(RescueTestingError) {}
|
359
|
+
t.rescue(RescueTestingError) {}
|
360
|
+
end
|
361
|
+
end
|
362
|
+
# TODO Make rescue give a specific error in this case
|
363
|
+
|
364
|
+
expect { scope.eventLoop }.to raise_error /You have already registered RescueTestingError/
|
365
|
+
end
|
366
|
+
|
367
|
+
it "ensures that stack traces work properly" do
|
368
|
+
scope = AsyncScope.new do
|
369
|
+
error_handler do |t|
|
370
|
+
t.begin { raise "simulated" }
|
371
|
+
t.rescue(Exception) do |e|
|
372
|
+
e.backtrace.should include "------ continuation ------"
|
373
|
+
e.message.should == "simulated"
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
scope.eventLoop
|
378
|
+
end
|
379
|
+
|
380
|
+
it "makes sure an error is raised if none of the rescue blocks apply to it" do
|
381
|
+
scope = AsyncScope.new do
|
382
|
+
error_handler do |t|
|
383
|
+
t.begin { raise RescueTestingError }
|
384
|
+
t.rescue(IOError) { p "nothing doing here" }
|
385
|
+
end
|
386
|
+
end
|
387
|
+
expect { scope.eventLoop }.to raise_error RescueTestingError
|
388
|
+
end
|
389
|
+
|
390
|
+
it "ensures that the ensure block is called with a rescue path" do
|
391
|
+
scope = AsyncScope.new do
|
392
|
+
error_handler do |t|
|
393
|
+
t.begin do
|
394
|
+
trace << :in_the_begin
|
395
|
+
raise RescueTestingError
|
396
|
+
end
|
397
|
+
t.rescue(RescueTestingError) { trace << :in_the_rescue }
|
398
|
+
t.ensure { trace << :in_the_ensure }
|
399
|
+
end
|
400
|
+
end
|
401
|
+
scope.eventLoop
|
402
|
+
trace.should == [:in_the_begin, :in_the_rescue, :in_the_ensure]
|
403
|
+
end
|
404
|
+
|
405
|
+
it "calls ensure after subtask ensure" do
|
406
|
+
scope = AsyncScope.new do
|
407
|
+
error_handler do |t|
|
408
|
+
t.begin do
|
409
|
+
trace << :in_the_top_begin
|
410
|
+
error_handler do |h|
|
411
|
+
h.begin { trace << :in_the_inner_begin }
|
412
|
+
h.rescue(StandardError) { }
|
413
|
+
h.ensure { trace << :in_the_inner_ensure }
|
414
|
+
end
|
415
|
+
t.rescue(StandardError) { }
|
416
|
+
t.ensure { trace << :in_the_outer_ensure}
|
417
|
+
end
|
418
|
+
end
|
419
|
+
scope.eventLoop
|
420
|
+
trace.should == [:in_the_top_begin, :in_the_inner_begin, :in_the_inner_ensure, :in_the_outer_ensure]
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
it "ensures that an ensure block will be called when an exception is raised" do
|
425
|
+
scope = AsyncScope.new do
|
426
|
+
error_handler do |t|
|
427
|
+
t.begin do
|
428
|
+
trace << :in_the_begin
|
429
|
+
raise StandardError
|
430
|
+
end
|
431
|
+
t.rescue(RescueTestingError) { trace << :this_is_not_okay }
|
432
|
+
t.ensure { trace << :in_the_ensure }
|
433
|
+
end
|
434
|
+
end
|
435
|
+
expect { scope.eventLoop }.to raise_error StandardError
|
436
|
+
trace.should == [:in_the_begin, :in_the_ensure]
|
437
|
+
end
|
438
|
+
|
439
|
+
it "ensures that the ensure block is called without an applicable rescue case
|
440
|
+
passes the error up" do
|
441
|
+
scope = AsyncScope.new do
|
442
|
+
error_handler do |t|
|
443
|
+
t.begin do
|
444
|
+
trace << :in_the_begin
|
445
|
+
raise RescueTestingError
|
446
|
+
end
|
447
|
+
t.rescue(IOError) { trace << :in_the_rescue }
|
448
|
+
t.ensure { trace << :in_the_ensure }
|
449
|
+
expect { scope.eventLoop }.to raise_error RescueTestingError
|
450
|
+
trace.should == [:in_the_begin, :in_the_ensure]
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
it "ensures that two begins raise an error" do
|
456
|
+
scope = AsyncScope.new do
|
457
|
+
error_handler do |t|
|
458
|
+
t.begin { trace << :yay }
|
459
|
+
t.begin { trace << :oh_no }
|
460
|
+
t.rescue(StandardError) { trace << :should_never_be_hit }
|
461
|
+
end
|
462
|
+
end
|
463
|
+
expect { scope.eventLoop }.to raise_error RuntimeError, /Duplicated begin/
|
464
|
+
end
|
465
|
+
|
466
|
+
it "ensures that two ensure statements raise an error" do
|
467
|
+
scope = AsyncScope.new do
|
468
|
+
error_handler do |t|
|
469
|
+
t.begin { trace << :yay }
|
470
|
+
t.rescue(StandardError) { trace << :should_never_be_hit }
|
471
|
+
t.ensure { trace << :this_is_okay }
|
472
|
+
t.ensure { trace << :but_this_is_not }
|
473
|
+
end
|
474
|
+
end
|
475
|
+
expect { scope.eventLoop }.to raise_error /Duplicated ensure/
|
476
|
+
end
|
477
|
+
# TODO: Reinstate this test
|
478
|
+
# it "ensures that you can't call resume inside the error handler" do
|
479
|
+
# scope = AsyncScope.new do
|
480
|
+
# error_handler do |t|
|
481
|
+
# t.begin { trace << :in_the_begin }
|
482
|
+
# t.rescue(StandardError) { trace << :in_the_rescue }
|
483
|
+
# t.ensure { trace << :in_the_ensure }
|
484
|
+
# t.resume
|
485
|
+
# end
|
486
|
+
# end
|
487
|
+
# expect { scope.eventLoop }.to raise_error NoMethodError
|
488
|
+
# end
|
489
|
+
|
490
|
+
it "ensures that you can't access the private variables of a BeginRescueEnsure block" do
|
491
|
+
scope = AsyncScope.new do
|
492
|
+
error_handler do |t|
|
493
|
+
t.begin { "This is the begin" }
|
494
|
+
t.rescue(StandardError) { "This is the rescue" }
|
495
|
+
t.ensure { trace << t.begin_task }
|
496
|
+
end
|
497
|
+
end
|
498
|
+
expect { scope.eventLoop }.to raise_error
|
499
|
+
trace.should == []
|
500
|
+
end
|
501
|
+
|
502
|
+
describe "BRE's closed behavior" do
|
503
|
+
before (:each) do
|
504
|
+
@scope = AsyncScope.new do
|
505
|
+
@error_handler = error_handler do |t|
|
506
|
+
t.begin do
|
507
|
+
"this is the beginning"
|
508
|
+
end
|
509
|
+
t.rescue StandardError do
|
510
|
+
"this is the rescue"
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
it "ensures that BRE's end at closed" do
|
517
|
+
@scope.eventLoop
|
518
|
+
@error_handler.current_state.should == :closed
|
519
|
+
end
|
520
|
+
|
521
|
+
end
|
522
|
+
context "Cancelling a BRE in the created state" do
|
523
|
+
scope = AsyncScope.new
|
524
|
+
condition = FiberConditionVariable.new
|
525
|
+
error_handler = BeginRescueEnsure.new(:parent => scope.root_context)
|
526
|
+
scope << error_handler
|
527
|
+
error_handler.begin lambda { raise StandardError; condition.wait }
|
528
|
+
error_handler.rescue(IOError, lambda { "this is the rescue" })
|
529
|
+
error_handler.cancel(StandardError)
|
530
|
+
subject { error_handler }
|
531
|
+
its(:current_state) { should == :closed }
|
532
|
+
end
|
533
|
+
|
534
|
+
it "makes sure that get_heirs works" do
|
535
|
+
flowfactory = FlowFactory.new
|
536
|
+
condition = FiberConditionVariable.new
|
537
|
+
scope = flowfactory.generate_scope
|
538
|
+
bre = flowfactory.generate_BRE(:begin => lambda { task { condition.wait; trace << :yay} })
|
539
|
+
scope.eventLoop
|
540
|
+
bre.get_heirs.length.should > 1
|
541
|
+
trace.should == []
|
542
|
+
end
|
543
|
+
#TODO this test shouldn't be needed, but it's a good comparison for flowfactory being borked
|
544
|
+
it "makes sure that get_heirs works" do
|
545
|
+
condition = FiberConditionVariable.new
|
546
|
+
scope = AsyncScope.new do
|
547
|
+
error_handler do |t|
|
548
|
+
t.begin { task { condition.wait; trace << :yay }}
|
549
|
+
t.rescue(StandardError) {}
|
550
|
+
end
|
551
|
+
end
|
552
|
+
scope.eventLoop
|
553
|
+
trace.should == []
|
554
|
+
end
|
555
|
+
|
556
|
+
it "makes sure that you can have a schedule in the middle of a BRE" do
|
557
|
+
future = Future.new
|
558
|
+
scope = AsyncScope.new do
|
559
|
+
error_handler do |t|
|
560
|
+
t.begin { task { future.get }}
|
561
|
+
end
|
562
|
+
end
|
563
|
+
scope.eventLoop
|
564
|
+
future.set(nil)
|
565
|
+
completed = scope.eventLoop
|
566
|
+
completed.should == true
|
567
|
+
end
|
568
|
+
# it "makes sure that failing a begin task will cause the other tasks to not get run" do
|
569
|
+
# trace = []
|
570
|
+
# scope = AsyncScope.new do
|
571
|
+
# error_handler do |t|
|
572
|
+
# t.begin do
|
573
|
+
# condition = FiberConditionVariable.new
|
574
|
+
# error_handler do |first_t|
|
575
|
+
# first_t.begin do
|
576
|
+
# task do
|
577
|
+
# trace << "in the error"
|
578
|
+
# condition.signal
|
579
|
+
# raise "simulated error"
|
580
|
+
# end
|
581
|
+
# debugger
|
582
|
+
# condition.wait
|
583
|
+
# other_future = task do
|
584
|
+
# trace << "This should not be"
|
585
|
+
# end
|
586
|
+
# end
|
587
|
+
# first_t.rescue(StandardError) do |error|
|
588
|
+
# raise error
|
589
|
+
# end
|
590
|
+
# first_t.ensure {condition.signal}
|
591
|
+
# end
|
592
|
+
|
593
|
+
# error_handler do |second_t|
|
594
|
+
# second_t.begin do
|
595
|
+
# other_future = task do
|
596
|
+
# trace << "This should not be"
|
597
|
+
# end
|
598
|
+
# end
|
599
|
+
# second_t.rescue(StandardError) do |error|
|
600
|
+
# raise error
|
601
|
+
# end
|
602
|
+
# second_t.ensure {}
|
603
|
+
# end
|
604
|
+
# t.rescue(StandardError) {|error| raise error}
|
605
|
+
# t.ensure {}
|
606
|
+
# end
|
607
|
+
# end
|
608
|
+
# end
|
609
|
+
# debugger
|
610
|
+
# expect { scope.eventLoop }.to raise_error StandardError
|
611
|
+
# trace.should == []
|
612
|
+
# end
|
613
|
+
|
614
|
+
|
615
|
+
end
|
616
|
+
|
617
|
+
describe "BRE#alive?" do
|
618
|
+
context "checking different types of BREs" do
|
619
|
+
let(:flowfactory) { FlowFactory.new }
|
620
|
+
let(:scope) { flowfactory.generate_scope }
|
621
|
+
before(:each) do
|
622
|
+
@regular_bre = flowfactory.generate_BRE
|
623
|
+
@rescue_bre = flowfactory.generate_BRE(:begin => lambda { raise StandardError })
|
624
|
+
end
|
625
|
+
context "regular bre" do
|
626
|
+
subject { @regular_bre }
|
627
|
+
it { should be_alive }
|
628
|
+
context "and then we run the scope" do
|
629
|
+
let(:run_bre) { scope.eventLoop; @regular_bre }
|
630
|
+
subject { run_bre }
|
631
|
+
it { should_not be_alive }
|
632
|
+
end
|
633
|
+
end
|
634
|
+
context "rescue bre" do
|
635
|
+
subject { @rescue_bre }
|
636
|
+
it { should be_alive }
|
637
|
+
context "and then we run the scope" do
|
638
|
+
let(:run_bre) { scope.eventLoop; @rescue_bre }
|
639
|
+
subject { run_bre }
|
640
|
+
it { should_not be_alive }
|
641
|
+
end
|
642
|
+
end
|
643
|
+
end
|
644
|
+
end
|
645
|
+
describe "Misc" do
|
646
|
+
it "makes sure that the return value of a BRE is the begin" do
|
647
|
+
scope = AsyncScope.new do
|
648
|
+
@x = _error_handler do |t|
|
649
|
+
t.begin { 5 }
|
650
|
+
end
|
651
|
+
end
|
652
|
+
scope.eventLoop
|
653
|
+
@x.get.should == 5
|
654
|
+
end
|
655
|
+
it "meakes sure that if there is a failure, the result is the rescue value" do
|
656
|
+
scope = AsyncScope.new do
|
657
|
+
@x = _error_handler do |t|
|
658
|
+
t.begin { raise StandardError }
|
659
|
+
t.rescue(StandardError) { 6 }
|
660
|
+
end
|
661
|
+
end
|
662
|
+
scope.eventLoop
|
663
|
+
@x.get.should == 6
|
664
|
+
end
|
665
|
+
end
|