aws-flow 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.
Files changed (62) hide show
  1. data/Gemfile +8 -0
  2. data/LICENSE.TXT +15 -0
  3. data/NOTICE.TXT +14 -0
  4. data/Rakefile +39 -0
  5. data/aws-flow-core/Gemfile +9 -0
  6. data/aws-flow-core/LICENSE.TXT +15 -0
  7. data/aws-flow-core/NOTICE.TXT +14 -0
  8. data/aws-flow-core/Rakefile +27 -0
  9. data/aws-flow-core/aws-flow-core.gemspec +12 -0
  10. data/aws-flow-core/lib/aws/flow.rb +26 -0
  11. data/aws-flow-core/lib/aws/flow/async_backtrace.rb +134 -0
  12. data/aws-flow-core/lib/aws/flow/async_scope.rb +195 -0
  13. data/aws-flow-core/lib/aws/flow/begin_rescue_ensure.rb +386 -0
  14. data/aws-flow-core/lib/aws/flow/fiber.rb +77 -0
  15. data/aws-flow-core/lib/aws/flow/flow_utils.rb +50 -0
  16. data/aws-flow-core/lib/aws/flow/future.rb +109 -0
  17. data/aws-flow-core/lib/aws/flow/implementation.rb +151 -0
  18. data/aws-flow-core/lib/aws/flow/simple_dfa.rb +85 -0
  19. data/aws-flow-core/lib/aws/flow/tasks.rb +405 -0
  20. data/aws-flow-core/test/aws/async_backtrace_spec.rb +41 -0
  21. data/aws-flow-core/test/aws/async_scope_spec.rb +118 -0
  22. data/aws-flow-core/test/aws/begin_rescue_ensure_spec.rb +665 -0
  23. data/aws-flow-core/test/aws/external_task_spec.rb +197 -0
  24. data/aws-flow-core/test/aws/factories.rb +52 -0
  25. data/aws-flow-core/test/aws/fiber_condition_variable_spec.rb +163 -0
  26. data/aws-flow-core/test/aws/fiber_spec.rb +78 -0
  27. data/aws-flow-core/test/aws/flow_spec.rb +255 -0
  28. data/aws-flow-core/test/aws/future_spec.rb +210 -0
  29. data/aws-flow-core/test/aws/rubyflow.rb +22 -0
  30. data/aws-flow-core/test/aws/simple_dfa_spec.rb +63 -0
  31. data/aws-flow-core/test/aws/spec_helper.rb +36 -0
  32. data/aws-flow.gemspec +13 -0
  33. data/lib/aws/decider.rb +67 -0
  34. data/lib/aws/decider/activity.rb +408 -0
  35. data/lib/aws/decider/activity_definition.rb +111 -0
  36. data/lib/aws/decider/async_decider.rb +673 -0
  37. data/lib/aws/decider/async_retrying_executor.rb +153 -0
  38. data/lib/aws/decider/data_converter.rb +40 -0
  39. data/lib/aws/decider/decider.rb +511 -0
  40. data/lib/aws/decider/decision_context.rb +60 -0
  41. data/lib/aws/decider/exceptions.rb +178 -0
  42. data/lib/aws/decider/executor.rb +149 -0
  43. data/lib/aws/decider/flow_defaults.rb +70 -0
  44. data/lib/aws/decider/generic_client.rb +178 -0
  45. data/lib/aws/decider/history_helper.rb +173 -0
  46. data/lib/aws/decider/implementation.rb +82 -0
  47. data/lib/aws/decider/options.rb +607 -0
  48. data/lib/aws/decider/state_machines.rb +373 -0
  49. data/lib/aws/decider/task_handler.rb +76 -0
  50. data/lib/aws/decider/task_poller.rb +207 -0
  51. data/lib/aws/decider/utilities.rb +187 -0
  52. data/lib/aws/decider/worker.rb +324 -0
  53. data/lib/aws/decider/workflow_client.rb +374 -0
  54. data/lib/aws/decider/workflow_clock.rb +104 -0
  55. data/lib/aws/decider/workflow_definition.rb +101 -0
  56. data/lib/aws/decider/workflow_definition_factory.rb +53 -0
  57. data/lib/aws/decider/workflow_enabled.rb +26 -0
  58. data/test/aws/decider_spec.rb +1299 -0
  59. data/test/aws/factories.rb +45 -0
  60. data/test/aws/integration_spec.rb +3108 -0
  61. data/test/aws/spec_helper.rb +23 -0
  62. metadata +138 -0
@@ -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