aws-flow 1.0.5 → 1.0.6

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