aws-flow 1.0.5 → 1.0.6

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