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.
@@ -0,0 +1,197 @@
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 "ExternalTask#alive?" do
17
+ let(:scope) { AsyncScope.new }
18
+ let(:simple_unit) do
19
+ ExternalTask.new(:parent => scope ) do |t|
20
+ t.initiate_task { |h| 2 + 2; h.complete }
21
+ end
22
+ end
23
+ it "tells you that ExternalTask is alive before the ExternalTask starts" do
24
+ simple_unit.alive?.should == true
25
+ end
26
+ it "tells you that ExternalTask is not alive once ExternalTask has exited" do
27
+ task_context = TaskContext.new(:parent => scope.get_closest_containing_scope, :task => simple_unit)
28
+ simple_unit.resume
29
+ simple_unit.alive?.should == false
30
+ end
31
+ end
32
+
33
+ describe ExternalTask do
34
+ let(:trace) { [] }
35
+
36
+ before(:each) do
37
+
38
+ class ExternalTaskException < Exception; end
39
+ @this_scope = AsyncScope.new() do
40
+ end
41
+ @this_task = ExternalTask.new(:parent => @this_scope) do |t|
42
+ t.cancellation_handler { |h, cause| h.fail(cause) }
43
+ t.initiate_task { |h| trace << :task_started; h.complete }
44
+ end
45
+ task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
46
+ @this_scope << @this_task
47
+ end
48
+
49
+ it "should complete okay" do
50
+
51
+ @this_scope.eventLoop
52
+ trace.should == [:task_started]
53
+ end
54
+
55
+ it "cancelling should cause an exception to be raised" do
56
+ @this_task.cancel(ExternalTaskException.new)
57
+ expect { @this_scope.eventLoop }.to raise_error ExternalTaskException
58
+ end
59
+
60
+ it "shouldn't cancel an already completed task" do
61
+ @this_scope.eventLoop
62
+ @this_task.cancel(Exception)
63
+ end
64
+
65
+ describe "cancelling an external task without a cancellation handler" do
66
+ before(:each) do
67
+ @this_scope = AsyncScope.new() do
68
+
69
+ end
70
+ @this_task = ExternalTask.new() do |t|
71
+ t.initiate_task { |h| trace << :task_started; h.complete }
72
+ end
73
+ @this_scope << @this_task
74
+ end
75
+ it "ensures that calling cancel results in no error" do
76
+ task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
77
+ @this_task.cancel(ExternalTaskException)
78
+ end
79
+ it "ensures that attempting to run a cancelled task has no effect" do
80
+ task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
81
+ @this_task.cancel(ExternalTaskException)
82
+ @this_scope.eventLoop
83
+ trace.should == []
84
+ end
85
+ end
86
+
87
+ it "ensures that raising in the cancellation handler is handled " do
88
+ scope = AsyncScope.new
89
+ external_task = ExternalTask.new() do |t|
90
+ t.initiate_task { |h| h.complete }
91
+ t.cancellation_handler { |h, cause| raise "Oh no!" }
92
+ end
93
+ scope << external_task
94
+ task_context = TaskContext.new(:parent => scope.get_closest_containing_scope, :task => external_task)
95
+ external_task.cancel(Exception)
96
+ expect { scope.eventLoop }.to raise_error /Oh no!/
97
+ end
98
+
99
+ it "ensures that cancelling a completed task is handled" do
100
+ @this_scope.eventLoop
101
+ @this_task.cancel(Exception)
102
+ end
103
+
104
+ it "ensures that cancelling a cancelled task is handled" do
105
+ @this_scope.eventLoop
106
+ @this_task.cancel(Exception)
107
+ @this_task.cancel(Exception)
108
+ end
109
+
110
+ it "ensures that failing a task after completion raises an error" do
111
+ @this_scope = AsyncScope.new() do
112
+ end
113
+ @this_task = ExternalTask.new() do |t|
114
+ t.cancellation_handler { |h| h.fail }
115
+ t.initiate_task { |h| trace << :task_started; h.complete; h.fail(Exception) }
116
+ end
117
+ task_context = TaskContext.new(:parent => @this_scope.get_closest_containing_scope, :task => @this_task)
118
+ @this_scope << @this_task
119
+ expect { @this_scope.eventLoop }.to raise_error /Already completed/
120
+ end
121
+
122
+ it "ensures that completing an external_task allows the asyncScope to move forward" do
123
+ @handle = nil
124
+ @this_scope = AsyncScope.new() do
125
+ external_task do |t|
126
+ t.cancellation_handler {|h| h.fail}
127
+ t.initiate_task {|h| @handle = h }
128
+ end
129
+ end
130
+ @this_scope.eventLoop
131
+ @this_scope.is_complete?.should == false
132
+ @handle.complete
133
+ @this_scope.eventLoop
134
+ @this_scope.is_complete?.should == true
135
+ end
136
+ end
137
+
138
+ describe "external_task function" do
139
+
140
+ it "ensures that cancelling will raise the externalTasks cancellation handler" do
141
+ trace = []
142
+ this_scope = AsyncScope.new() do
143
+ trace << :async_start
144
+ external_task do |t|
145
+ t.cancellation_handler {|h, cause| trace << :cancellation_handler; h.complete }
146
+ t.initiate_task { |h| trace << :external_task}
147
+ end
148
+ task { trace << :task; raise IllegalStateException }
149
+ trace << :async_done
150
+ end
151
+ expect { this_scope.eventLoop }.to raise_error IllegalStateException
152
+ trace.should == [:async_start, :async_done, :external_task, :task, :cancellation_handler]
153
+ end
154
+
155
+ it "tests explicit cancellation of BRE cancelling externalTask correctly" do
156
+ handle_future = Future.new
157
+ trace = []
158
+
159
+ scope = AsyncScope.new do
160
+ trace << :async_start
161
+ bre = error_handler do |t|
162
+ t.begin do
163
+ external_task do |t|
164
+ trace << :external_task;
165
+ t.cancellation_handler {|h, cause| trace << :cancellation_handler; h.complete}
166
+ t.initiate_task { |h| handle_future.set(h) }
167
+ end
168
+
169
+ task do
170
+ trace << :in_the_cancel_task
171
+ handle_future.get
172
+ bre.cancel(nil)
173
+ end
174
+ end
175
+ t.rescue(Exception) { |e| e.class.should <= CancellationException }
176
+ end
177
+
178
+ trace << :async_done
179
+ end
180
+ scope.eventLoop
181
+ trace.should == [:async_start, :async_done, :external_task, :in_the_cancel_task, :cancellation_handler]
182
+ end
183
+
184
+ it "ensures that having an external_task within an AsyncScope works" do
185
+ trace = []
186
+ this_scope = AsyncScope.new() do
187
+ external_task do |t|
188
+ t.cancellation_handler { |h| h.fail }
189
+ t.initiate_task { |h| trace << :task_started; h.complete; }
190
+ end
191
+ end
192
+ trace.should == []
193
+ this_scope.eventLoop
194
+ trace.should == [:task_started]
195
+ end
196
+
197
+ end
@@ -0,0 +1,52 @@
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
+ require 'rubygems'
17
+ require 'factory_girl'
18
+ require 'aws/flow'
19
+ include AWS::Flow::Core
20
+
21
+
22
+ class FlowFactory
23
+
24
+ attr_accessor :async_scope
25
+ def generate_BRE(options = {})
26
+ scope = generate_scope
27
+ bre = BeginRescueEnsure.new(:parent => scope.root_context)
28
+ begin_statement = options[:begin] ? options[:begin] : lambda {}
29
+ rescue_statement = options[:rescue] ? options[:rescue] : lambda {|x| }
30
+ rescue_exception = options[:rescue_exceptions] ? options[:rescue_exceptions] : StandardError
31
+ ensure_statement = options[:ensure] ? options[:ensure] : lambda {}
32
+ bre.begin begin_statement
33
+ bre.rescue(rescue_exception, rescue_statement)
34
+ bre.ensure ensure_statement
35
+ scope << bre
36
+ bre
37
+ end
38
+
39
+ def generate_scope(options = {})
40
+ lambda = options[:lambda] || lambda {}
41
+ @async_scope ||= AsyncScope.new do
42
+ lambda.call
43
+ end
44
+ return @async_scope
45
+ end
46
+
47
+ def generate_daemon_task(options = {})
48
+ scope = generate_scope
49
+ task = DaemonTask.new
50
+
51
+ end
52
+ end
@@ -0,0 +1,163 @@
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 FiberConditionVariable do
17
+ let(:condition) { FiberConditionVariable.new }
18
+ let(:trace) { [] }
19
+
20
+ it "blocks a Fiber and wakes it up on signal" do
21
+ scope = AsyncScope.new do
22
+ task do
23
+ trace << :first
24
+ condition.wait
25
+ # TODO technically this should only be checked in a TaskContext test;
26
+ # a Future test would just make sure that this comes after :second
27
+ trace << :fourth
28
+ end
29
+
30
+ task do
31
+ trace << :second
32
+ condition.signal
33
+ trace << :third
34
+ end
35
+ end
36
+
37
+ scope.eventLoop
38
+ trace.should == [:first, :second, :third, :fourth]
39
+ end
40
+
41
+ it "blocks multiple Fibers and wakes them up on signal" do
42
+ scope = AsyncScope.new do
43
+ task do
44
+ trace << :first
45
+ condition.wait
46
+ # TODO technically this should only be checked in a TaskContext test;
47
+ # a Future test would just make sure that this comes after :second
48
+ trace << :sixth
49
+ end
50
+
51
+ task do
52
+ trace << :second
53
+ condition.wait
54
+ trace << :seventh
55
+ end
56
+
57
+ task do
58
+ trace << :third
59
+ condition.signal
60
+ trace << :fourth
61
+ condition.signal
62
+ trace << :fifth
63
+ end
64
+ end
65
+
66
+ scope.eventLoop
67
+ trace.should == [:first, :second, :third, :fourth, :fifth, :sixth, :seventh]
68
+ end
69
+
70
+ it "blocks a Fiber and wakes it up on broadcast" do
71
+ scope = AsyncScope.new do
72
+ task do
73
+ trace << :first
74
+ condition.wait
75
+ # TODO technically this should only be checked in a TaskContext test;
76
+ # a Future test would just make sure that this comes after :second
77
+ trace << :fourth
78
+ end
79
+
80
+ task do
81
+ trace << :second
82
+ condition.broadcast
83
+ trace << :third
84
+ end
85
+ end
86
+ scope.eventLoop
87
+ trace.should == [:first, :second, :third, :fourth]
88
+ end
89
+
90
+ it "blocks multiple Fibers and wakes them up on broadcast" do
91
+ scope = AsyncScope.new do
92
+ task do
93
+ trace << :first
94
+ condition.wait
95
+ # TODO technically this should only be checked in a TaskContext test;
96
+ # a Future test would just make sure that this comes after :second
97
+ trace << :fifth
98
+ end
99
+
100
+ task do
101
+ trace << :second
102
+ condition.wait
103
+ trace << :sixth
104
+ end
105
+
106
+ task do
107
+ trace << :third
108
+ condition.broadcast
109
+ trace << :fourth
110
+ end
111
+ end
112
+
113
+ scope.eventLoop
114
+ trace.should == [:first, :second, :third, :fourth, :fifth, :sixth]
115
+ end
116
+
117
+
118
+
119
+ [:broadcast, :signal].each do |method_to_test|
120
+ it "ensures that multiple broadcasts cannot cause multiple runs of fibers" do
121
+ trace = []
122
+ scope = AsyncScope.new do
123
+ task do
124
+ trace << :first
125
+ condition.wait
126
+ trace << :fourth
127
+ end
128
+ task do
129
+ trace << :second
130
+ condition.send method_to_test
131
+ condition.send method_to_test
132
+ trace << :third
133
+ end
134
+ end
135
+ scope.eventLoop
136
+ trace.should == [:first, :second, :third, :fourth]
137
+ end
138
+
139
+ it "ensures that calling #{method_to_test} with no waiters doesn't error" do
140
+ condition.send method_to_test
141
+ end
142
+
143
+ it "ensures that #{method_to_test}ing a task that is dead has no effect" do
144
+ condition = FiberConditionVariable.new
145
+ trace = []
146
+ scope = AsyncScope.new
147
+ a = Task.new(scope.root_context) do
148
+ trace << :first
149
+ condition.wait
150
+ trace << :should_never_be_hit
151
+ end
152
+ b = Task.new(scope.root_context) do
153
+ trace << :second
154
+ condition.send(method_to_test)
155
+ trace << :third
156
+ end
157
+ a.resume
158
+ a.should_receive(:alive?).and_return(false)
159
+ b.resume
160
+ trace.should == [:first, :second, :third]
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,78 @@
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 Fiber do
17
+ it "makes sure that Fibers behave the same" do
18
+ root_fiber = Fiber.current
19
+ scope = AsyncScope.new() do
20
+ p "yay"
21
+ end
22
+ Fiber.current.should == root_fiber
23
+ end
24
+ it "makes sure that FiberLocal variables inherit correctly" do
25
+ scope = AsyncScope.new() do
26
+ FlowFiber.current[:test] = 5
27
+ FlowFiber.current[:test].should == 5
28
+ task do
29
+ FlowFiber.current[:test].should == 5
30
+ end
31
+ end
32
+ scope.eventLoop
33
+ end
34
+
35
+ it "makes sure that fiber values get unset correctly" do
36
+ first_scope = AsyncScope.new do
37
+ FlowFiber.current[:test] = 5
38
+ FlowFiber.current[:test].should == 5
39
+ end
40
+ second_scope = AsyncScope.new do
41
+ @current_thread = FlowFiber.current
42
+ FlowFiber.current[:test] = 5
43
+ FlowFiber.current[:test].should == 5
44
+ task do
45
+ task do
46
+ FlowFiber.unset(FlowFiber.current, :test)
47
+ end
48
+ end
49
+ FlowFiber.current[:test]
50
+ end
51
+ first_scope.eventLoop
52
+ second_scope.eventLoop
53
+ FlowFiber[@current_thread][:test].should == nil
54
+ end
55
+
56
+ it "makes sure that fibers pass by reference" do
57
+ class CustomObject
58
+ attr_accessor :this_stuff
59
+ end
60
+ x = CustomObject.new
61
+ @trace = []
62
+ first_fiber = FlowFiber.new do
63
+ x.this_stuff = 6
64
+ Fiber.yield
65
+ @trace << x.this_stuff
66
+ end
67
+
68
+ second_fiber = FlowFiber.new do
69
+ x.this_stuff = x.this_stuff * 2
70
+ @trace << x.this_stuff
71
+ end
72
+ first_fiber.resume
73
+ second_fiber.resume
74
+ first_fiber.resume
75
+ @trace.select{|x| x == 12}.length == 2
76
+
77
+ end
78
+ end