aws-flow 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,41 @@
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
+ def validate_stacktrace_content(method_to_call_on_async_backtrace, thing_to_look_for, should_it_be_there)
17
+ it "makes sure that any of #{thing_to_look_for.join", "} #{should_it_be_there} be printed when we call #{method_to_call_on_async_backtrace} on AsyncBacktrace" do
18
+ AsyncBacktrace.enable
19
+ AsyncBacktrace.send(method_to_call_on_async_backtrace)
20
+ scope = AsyncScope.new do
21
+ error_handler do |t|
22
+ t.begin { raise StandardError.new }
23
+ t.rescue(IOError) {}
24
+ end
25
+ end
26
+ begin
27
+ scope.eventLoop
28
+ rescue Exception => e
29
+ matching_lines = thing_to_look_for.select { |part| e.backtrace.to_s.include? part.to_s }
30
+
31
+ matching_lines.send(should_it_be_there, be_empty)
32
+ end
33
+ end
34
+ end
35
+
36
+ describe AsyncBacktrace do
37
+ validate_stacktrace_content(:enable, [:continuation], :should_not)
38
+ validate_stacktrace_content(:disable, [:continuation], :should)
39
+ validate_stacktrace_content(:enable_filtering, $RUBY_FLOW_FILES, :should)
40
+ validate_stacktrace_content(:disable_filtering, $RUBY_FLOW_FILES, :should_not)
41
+ end
@@ -0,0 +1,118 @@
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
+ describe AsyncScope do
17
+
18
+ it "makes sure that basic AsyncScoping works" do
19
+ trace = []
20
+ this_scope = AsyncScope.new() do
21
+ task { trace << :inside_task}
22
+ end
23
+ trace.should == []
24
+ this_scope.eventLoop
25
+ trace.should == [:inside_task]
26
+ end
27
+
28
+ context "empty AsyncScope" do
29
+ let(:scope) { AsyncScope.new }
30
+ let(:trace) { [] }
31
+
32
+ it "adds tasks like a queue" do
33
+ scope << Task.new(scope.root_context) { trace << :task }
34
+ scope.eventLoop
35
+ trace.should == [:task]
36
+ end
37
+
38
+ it "adds a task if given one on construction" do
39
+ scope = AsyncScope.new { trace << :task }
40
+ scope.eventLoop
41
+ trace.should == [:task]
42
+ end
43
+
44
+
45
+ it "runs a task asynchronously" do
46
+ scope << Task.new(scope.root_context) { trace << :inner }
47
+ trace << :outer
48
+ scope.eventLoop
49
+ trace.should == [:outer, :inner]
50
+ end
51
+
52
+ it "runs multiple tasks in order" do
53
+ scope << Task.new(scope.root_context) { trace << :first_task }
54
+ scope << Task.new(scope.root_context) { trace << :second_task }
55
+ scope.eventLoop
56
+ trace.should == [:first_task, :second_task]
57
+ end
58
+
59
+ it "resumes tasks after a Future is ready" do
60
+ f = Future.new
61
+ scope = AsyncScope.new do
62
+ task do
63
+ trace << :first
64
+ f.get
65
+ trace << :fourth
66
+ end
67
+
68
+ task do
69
+ trace << :second
70
+ f.set(:foo)
71
+ trace << :third
72
+ end
73
+ end
74
+ scope.eventLoop
75
+ trace.should == [:first, :second, :third, :fourth]
76
+ end
77
+
78
+ it "tests to make sure that scopes complete after an event loop" do
79
+ scope = AsyncScope.new do
80
+ error_handler do |t|
81
+ t.begin do
82
+ x = Future.new
83
+ y = Future.new
84
+ error_handler do |t|
85
+ t.begin {}
86
+ t.ensure { x.set }
87
+ end
88
+ x.get
89
+ error_handler do |t|
90
+ t.begin {}
91
+ t.ensure { y.set }
92
+ end
93
+ y.get
94
+ error_handler do |t|
95
+ t.begin {}
96
+ t.ensure {}
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ completed = scope.eventLoop
103
+ completed.should == true
104
+ end
105
+ it "ensures you can cancel an async scope " do
106
+ condition = FiberConditionVariable.new
107
+ @task = nil
108
+ scope = AsyncScope.new do
109
+ task do
110
+ condition.wait
111
+ end
112
+ end
113
+ scope.eventLoop
114
+ scope.cancel(CancellationException.new)
115
+ expect { scope.eventLoop }.to raise_error CancellationException
116
+ end
117
+ end
118
+ end
@@ -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