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,255 @@
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 'pp'
17
+
18
+ # TODO Split out fibers-related tests into a fibers_spec and
19
+ # put the interface-related tests into something like interface_spec
20
+ # Or maybe they can stay in flow_spec
21
+
22
+
23
+ # Doesn't make sense to have this for daemon tasks, as the BRE will close before
24
+ # it raises
25
+
26
+ {:DaemonTask => lambda { |&x| daemon_task(&x) },
27
+ :Task => lambda { |&x| task(&x) } }.each_pair do |name, task_creation|
28
+
29
+
30
+
31
+
32
+ describe "#{name} method" do
33
+ let(:condition) { FiberConditionVariable.new }
34
+ # TODO Reinstate this test once we figure out how to fix Fibers in 1.8 sharing state
35
+
36
+ it "fails when called from outside of a context" do
37
+ expect do
38
+ task_creation.call do
39
+ "Invalid call"
40
+ end
41
+ end.to raise_error(NoContextException)
42
+ end
43
+
44
+ it "should execute #{name} children recursively"do
45
+ @executed = []
46
+ def recursive(i, task_creation)
47
+ return if i == 0
48
+ task_creation.call do
49
+ recursive(i - 1, task_creation)
50
+ @executed << i
51
+ end
52
+ end
53
+ scope = AsyncScope.new do
54
+ task { condition.wait }
55
+ recursive(100, task_creation)
56
+ end
57
+ @executed.length.should == 0
58
+ scope.eventLoop
59
+ @executed.length.should == 100
60
+ end
61
+
62
+
63
+
64
+ context 'tests that #{name} do things in the right order' do
65
+ let(:trace) { [] }
66
+ let(:condition) { FiberConditionVariable.new }
67
+ it "executes the block asynchronously" do
68
+ trace << :outside_task
69
+ scope = AsyncScope.new do
70
+ task_creation.call { trace << :inside_task; condition.signal }
71
+ task { condition.wait }
72
+ end
73
+ scope.eventLoop
74
+ trace.should == [:outside_task, :inside_task]
75
+ end
76
+
77
+ it "returns something which contains the block's return value" do
78
+ result = nil
79
+ scope = AsyncScope.new do
80
+ result = task_creation.call { condition.signal; :task_executed }
81
+ task { condition.wait }
82
+ end
83
+ scope.eventLoop
84
+ result.get.should == :task_executed
85
+ end
86
+
87
+ it "executes multiple #{name}s in order" do
88
+ scope = AsyncScope.new do
89
+ task_creation.call { trace << :first_task }
90
+ task_creation.call { trace << :second_task; condition.signal }
91
+ task { condition.wait }
92
+ end
93
+ scope.eventLoop
94
+ trace.should == [:first_task, :second_task]
95
+ end
96
+
97
+ it "executes nested #{name} after the parent task" do
98
+
99
+ scope = AsyncScope.new do
100
+ task_creation.call do
101
+ task_creation.call { trace << :inside_task; condition.signal }
102
+ trace << :outside_task
103
+ end
104
+ task { condition.wait }
105
+ end
106
+ scope.eventLoop
107
+ trace.should == [:outside_task, :inside_task]
108
+ end
109
+
110
+ it "executes nested #{name}" do
111
+ scope = AsyncScope.new do
112
+ task_creation.call do
113
+ trace << :outside_task
114
+ task_creation.call { trace << :inside_task; condition.signal }
115
+ end
116
+ task { condition.wait }
117
+ end
118
+ scope.eventLoop
119
+ trace.should == [:outside_task, :inside_task]
120
+ end
121
+ end
122
+ end
123
+ end
124
+ {:Task => lambda { |x, &y| Task.new(x, &y) },
125
+ :DaemonTask => lambda { |x, &y| DaemonTask.new(x, &y) }}.each_pair do |name, task_creation|
126
+
127
+ describe name do
128
+ let(:trace) { [] }
129
+ let(:scope) { AsyncScope.new }
130
+ let(:task) { task_creation.call(scope.root_context) { trace << :inner } }
131
+
132
+ context "result for a task that is not run" do
133
+ let(:result) { task.result }
134
+ subject { result }
135
+ its(:class) { should == Future }
136
+ it { should_not be_set }
137
+ end
138
+
139
+ it "executes a block asynchronously" do
140
+ trace << :outer
141
+ task.resume
142
+ trace.should == [:outer, :inner]
143
+ end
144
+
145
+ it "is the currently running Fiber when the block executes" do
146
+ task = task_creation.call(scope.root_context) do
147
+ fiber = ::Fiber.current
148
+ fiber.should == task
149
+ trace << :executed
150
+ end
151
+ task.resume
152
+ trace.should == [:executed]
153
+ end
154
+ it "doesn't let the block return from the block's parent method" do
155
+ def create_and_run_task(task_creation)
156
+ t = task_creation.call(scope.root_context) { return :inner_return }
157
+ t.resume
158
+ :outer_return
159
+ end
160
+ result = create_and_run_task(task_creation)
161
+ result.should == :outer_return
162
+ end
163
+
164
+ # context "cancelled Task" do
165
+ # it "makes sure that a cancelled task does not run" do
166
+ # task = task_creation.call(scope.root_context) { trace << :should_never_happen }
167
+ # scope << task
168
+ # task.cancel(StandardError)
169
+
170
+ # trace.should == []
171
+ # end
172
+ # end
173
+
174
+ context "Dead Fiber" do
175
+ let(:dead_task) { t = task_creation.call(scope.root_context) { 1 + 1 }; t.resume; t }
176
+ subject { dead_task }
177
+ it { should_not be_alive }
178
+ it "should raise FiberError on resume" do
179
+ expect { dead_task.resume }.to raise_error FiberError
180
+ end
181
+ end
182
+
183
+ it "makes sure that tasks return a Future" do
184
+ a = task_creation.call(scope.root_context) { trace << :first }
185
+ b = task_creation.call(scope.root_context) do
186
+ a.result.get
187
+ trace << :second
188
+ end
189
+ scope << a
190
+ scope << b
191
+ scope.eventLoop
192
+ trace.should == [:first, :second]
193
+ end
194
+ end
195
+ end
196
+
197
+ {:Fiber => lambda {|&block| Fiber.new &block},
198
+ :Task => lambda {|&block| Task.new(AsyncScope.new.root_context, &block)},
199
+ }.each_pair do |klass, initialize|
200
+ describe "#{klass}#alive?" do
201
+ let(:simple_unit) { initialize.call { 2 + 2 } }
202
+ let(:waiting_unit) { initialize.call { Fiber.yield } }
203
+ it "tells you the #{klass} is alive before the #{klass} starts" do
204
+ simple_unit.alive?.should == true
205
+ end
206
+
207
+ it "tells you the #{klass} is not alive once the #{klass} has exited" do
208
+ simple_unit.resume
209
+ simple_unit.alive?.should == false
210
+ end
211
+
212
+ it "tells you the #{klass} is alive if the #{klass} is partially executed" do
213
+ waiting_unit.resume
214
+ # TODO: Discuss whether this should fail or not
215
+ waiting_unit.alive?.should == true
216
+ end
217
+ end
218
+ end
219
+
220
+ describe Task do
221
+
222
+ it "ensures that raising an error in a task is handled correctly" do
223
+ scope = AsyncScope.new do
224
+ task do
225
+ raise "Boo"
226
+ end
227
+ end
228
+ begin
229
+ scope.eventLoop
230
+ rescue Exception => e
231
+ e.message.should =~ /Boo.*/
232
+ end
233
+ end
234
+
235
+ it "ensures that cancelling a task will not have it remove itself twice " do
236
+ condition = FiberConditionVariable.new
237
+ scope = AsyncScope.new do
238
+ error_handler do |t|
239
+ t.begin do
240
+ task do
241
+ condition.wait
242
+ end
243
+ task do
244
+ condition.signal
245
+ # i.e.,
246
+ raise "simulated error"
247
+ end
248
+ end
249
+ end
250
+ end
251
+ expect { scope.eventLoop }.to raise_error "simulated error"
252
+ end
253
+
254
+
255
+ end
@@ -0,0 +1,210 @@
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 Future do
17
+ let(:future) { Future.new }
18
+ let(:trace) { [] }
19
+ it "throws an exception when set multiple times" do
20
+ future.set(:foo)
21
+ expect { future.set(:bar) }.to raise_error AlreadySetException
22
+ end
23
+
24
+ it "returns the value which was set" do
25
+ value = :foo
26
+ future.set(value)
27
+ future.get.should == value
28
+ end
29
+
30
+ it "blocks a task until a value is ready" do
31
+ scope = AsyncScope.new do
32
+ task do
33
+ trace << :first
34
+ future.get
35
+ # TODO technically this should only be checked in a TaskContext test;
36
+ # a Future test would just make sure that this comes after :second
37
+ trace << :fourth
38
+ end
39
+
40
+ task do
41
+ trace << :second
42
+ future.set(:foo)
43
+ trace << :third
44
+ end
45
+ end
46
+
47
+ scope.eventLoop
48
+ trace.should == [:first, :second, :third, :fourth]
49
+ end
50
+
51
+ it "blocks multiple Fibers until a value is ready" do
52
+ scope = AsyncScope.new do
53
+ task do
54
+ trace << :first
55
+ future.get
56
+ trace << :fifth
57
+ end
58
+
59
+ task do
60
+ trace << :second
61
+ future.get
62
+ trace << :sixth
63
+ end
64
+
65
+ task do
66
+ trace << :third
67
+ future.set(:foo)
68
+ trace << :fourth
69
+ end
70
+ end
71
+ scope.eventLoop
72
+ trace.should == [:first, :second, :third, :fourth, :fifth, :sixth]
73
+ end
74
+
75
+ it "supports wait_for_any" do
76
+ scope = AsyncScope.new do
77
+ f1 = Future.new
78
+ f2 = Future.new
79
+ f3 = Future.new
80
+ trace << :before_task
81
+ task do
82
+ trace << :before_set
83
+ f2.set("bar")
84
+ trace << :after_set
85
+ end
86
+ trace << :before_wait
87
+
88
+ f = Future.wait_for_any(f1, f2, f3).first
89
+ trace << :after_wait
90
+ f.should == f2
91
+ f.get.should == "bar"
92
+ end
93
+ trace.length.should == 0
94
+ scope.eventLoop
95
+ trace.should == [:before_task, :before_wait, :before_set, :after_set, :after_wait]
96
+ end
97
+
98
+ it "supports wait_for_any with all set" do
99
+ scope = AsyncScope.new do
100
+ f1 = Future.new
101
+ f2 = Future.new
102
+ f3 = Future.new
103
+ trace << :before_task
104
+ task do
105
+ trace << :before_set
106
+ f2.set("bar")
107
+ f1.set("baz")
108
+ f3.set("foo")
109
+ trace << :after_set
110
+ end
111
+ trace << :before_wait
112
+ r1, r2, r3 = Future.wait_for_any(f1, f2, f3)
113
+ trace << :after_wait
114
+ r1.should == f2
115
+ r1.get.should == "bar"
116
+ r2.should == f1
117
+ r2.get.should == "baz"
118
+ r3.should == f3
119
+ r3.get.should == "foo"
120
+ end
121
+ trace.length.should == 0
122
+ scope.eventLoop
123
+ trace.should == [:before_task, :before_wait, :before_set, :after_set, :after_wait]
124
+ end
125
+
126
+ it "supports wait_for_all" do
127
+ scope = AsyncScope.new do
128
+ f1 = Future.new
129
+ f2 = Future.new
130
+ f3 = Future.new
131
+ trace << :before_task
132
+ task do
133
+ trace << :before_f2_set
134
+ f2.set("bar")
135
+ task do
136
+ trace << :before_f1_set
137
+ f1.set("baz")
138
+ task do
139
+ trace << :before_f3_set
140
+ f3.set("foo")
141
+ trace << :after_set
142
+ end
143
+ end
144
+ end
145
+ trace << :before_wait
146
+ r1, r2, r3 = Future.wait_for_all(f1, f2, f3)
147
+ trace << :after_wait
148
+ r1.should == f2
149
+ r1.get.should == "bar"
150
+ r2.should == f1
151
+ r2.get.should == "baz"
152
+ r3.should == f3
153
+ r3.get.should == "foo"
154
+ end
155
+ trace.length.should == 0
156
+ scope.eventLoop
157
+ trace.should == [:before_task, :before_wait, :before_f2_set, :before_f1_set, :before_f3_set, :after_set, :after_wait]
158
+ end
159
+
160
+ it "supports wait_for_all with no futures" do
161
+ scope = AsyncScope.new do
162
+ task do
163
+ wait_for_all
164
+ end
165
+ end
166
+ scope.eventLoop
167
+ scope.is_complete?.should == true
168
+ end
169
+
170
+ it "supports wait_for_any with no futures" do
171
+ scope = AsyncScope.new do
172
+ task do
173
+ wait_for_any
174
+ end
175
+ end
176
+ scope.eventLoop
177
+ scope.is_complete?.should == true
178
+ end
179
+
180
+ it "supports wait_for_all with a set future" do
181
+ scope = AsyncScope.new do
182
+ future = Future.new.set
183
+ wait_for_all(future)
184
+ end
185
+ scope.eventLoop
186
+ scope.is_complete?.should == true
187
+ end
188
+
189
+ it "supports wait_for_any with a set future" do
190
+ scope = AsyncScope.new do
191
+ future = Future.new.set
192
+ wait_for_any(future)
193
+ end
194
+ scope.eventLoop
195
+ scope.is_complete?.should == true
196
+ end
197
+
198
+ it "supports wait_for_all with one set future and one not set" do
199
+ future2 = Future.new
200
+ scope = AsyncScope.new do
201
+ future = Future.new.set
202
+ wait_for_all(future, future2)
203
+ end
204
+ scope.eventLoop
205
+ future2.set
206
+ scope.eventLoop
207
+ scope.is_complete?.should == true
208
+ end
209
+
210
+ end