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,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