aws-flow-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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