exceptional_synchrony 1.0.1 → 1.1.1
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.
- checksums.yaml +5 -5
- data/.dependabot/config.yml +10 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +10 -1
- data/Gemfile.lock +104 -101
- data/Rakefile +10 -0
- data/exceptional_synchrony.gemspec +20 -22
- data/lib/exceptional_synchrony/callback_exceptions.rb +36 -4
- data/lib/exceptional_synchrony/event_machine_proxy.rb +18 -0
- data/lib/exceptional_synchrony/limited_work_queue.rb +29 -5
- data/lib/exceptional_synchrony/parallel_sync.rb +1 -0
- data/lib/exceptional_synchrony/version.rb +1 -1
- data/lib/exceptional_synchrony.rb +0 -1
- data/semaphore_ci/setup.sh +3 -0
- data/test/test_helper.rb +7 -0
- data/test/unit/callback_exceptions_test.rb +67 -15
- data/test/unit/event_machine_proxy_test.rb +43 -5
- data/test/unit/limited_work_queue_test.rb +221 -23
- data/test/unit/parallel_sync_test.rb +6 -6
- metadata +35 -87
- data/Thorfile +0 -42
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative '../test_helper'
|
2
|
+
require 'pry'
|
2
3
|
|
3
4
|
describe ExceptionalSynchrony::CallbackExceptions do
|
4
5
|
describe "ensure_callback" do
|
@@ -60,14 +61,14 @@ describe ExceptionalSynchrony::CallbackExceptions do
|
|
60
61
|
deferrable = EM::DefaultDeferrable.new
|
61
62
|
deferrable.succeed(12)
|
62
63
|
result = ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
63
|
-
result.must_equal
|
64
|
+
expect(result).must_equal(12)
|
64
65
|
end
|
65
66
|
|
66
67
|
it "should map success values to an array" do
|
67
68
|
deferrable = EM::DefaultDeferrable.new
|
68
69
|
deferrable.succeed(12, 13, 14)
|
69
70
|
result = ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
70
|
-
result.must_equal
|
71
|
+
expect(result).must_equal([12, 13, 14])
|
71
72
|
end
|
72
73
|
|
73
74
|
it "should map success exception values to raise" do
|
@@ -77,7 +78,7 @@ describe ExceptionalSynchrony::CallbackExceptions do
|
|
77
78
|
result = assert_raises(ArgumentError) do
|
78
79
|
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
79
80
|
end
|
80
|
-
result.must_equal
|
81
|
+
expect(result).must_equal(exception)
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
@@ -87,8 +88,18 @@ describe ExceptionalSynchrony::CallbackExceptions do
|
|
87
88
|
deferrable.fail(first: "a", last: "b")
|
88
89
|
result = assert_raises(ExceptionalSynchrony::CallbackExceptions::Failure) do
|
89
90
|
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
90
|
-
end
|
91
|
-
result.
|
91
|
+
end.message
|
92
|
+
expect(result).must_equal("RESULT = {:first=>\"a\", :last=>\"b\"}")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should truncate long failures" do
|
96
|
+
deferrable = EM::DefaultDeferrable.new
|
97
|
+
deferrable.fail('a'*75 + 'b'*75)
|
98
|
+
result = assert_raises(ExceptionalSynchrony::CallbackExceptions::Failure) do
|
99
|
+
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
100
|
+
end.message
|
101
|
+
expected_message = "RESULT = \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb...TRUNC"
|
102
|
+
expect(result).must_equal(expected_message)
|
92
103
|
end
|
93
104
|
|
94
105
|
it "should map failure exceptions to raise" do
|
@@ -97,9 +108,9 @@ describe ExceptionalSynchrony::CallbackExceptions do
|
|
97
108
|
deferrable.fail(exception)
|
98
109
|
result = assert_raises(ExceptionalSynchrony::CallbackExceptions::Failure) do
|
99
110
|
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
100
|
-
end
|
101
|
-
result.
|
102
|
-
result.
|
111
|
+
end.message
|
112
|
+
expect(result).must_match(/ArgumentError/)
|
113
|
+
expect(result).must_match(/Wrong argument!/)
|
103
114
|
end
|
104
115
|
|
105
116
|
it "should map timeout failure to raise TimeoutError" do
|
@@ -114,6 +125,47 @@ describe ExceptionalSynchrony::CallbackExceptions do
|
|
114
125
|
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
115
126
|
end
|
116
127
|
end
|
128
|
+
|
129
|
+
it "should map Errno::ETIMEDOUT to TimeoutError" do
|
130
|
+
deferrable = EM::DefaultDeferrable.new
|
131
|
+
|
132
|
+
def deferrable.error
|
133
|
+
Errno::ETIMEDOUT
|
134
|
+
end
|
135
|
+
|
136
|
+
deferrable.fail(deferrable)
|
137
|
+
assert_raises(Timeout::Error) do
|
138
|
+
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should map other SystemCallError exceptions to Failures with the error in the message" do
|
143
|
+
deferrable = EM::DefaultDeferrable.new
|
144
|
+
|
145
|
+
def deferrable.error
|
146
|
+
Errno::ECONNREFUSED
|
147
|
+
end
|
148
|
+
|
149
|
+
deferrable.fail(deferrable)
|
150
|
+
result = assert_raises(ExceptionalSynchrony::CallbackExceptions::Failure) do
|
151
|
+
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
152
|
+
end
|
153
|
+
expect(result.message).must_match(/\AERROR = Errno::ECONNREFUSED; RESULT = #<EventMachine::DefaultDeferrable/)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should map any other errors to Failure with the error in the message" do
|
157
|
+
deferrable = EM::DefaultDeferrable.new
|
158
|
+
|
159
|
+
def deferrable.error
|
160
|
+
ArgumentError.new("Some errror")
|
161
|
+
end
|
162
|
+
|
163
|
+
deferrable.fail(deferrable)
|
164
|
+
result = assert_raises(ExceptionalSynchrony::CallbackExceptions::Failure) do
|
165
|
+
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
166
|
+
end
|
167
|
+
expect(result.message).must_match(/\AERROR = #<ArgumentError: Some errror>; RESULT = #<EventMachine::DefaultDeferrable/)
|
168
|
+
end
|
117
169
|
end
|
118
170
|
|
119
171
|
describe "no status" do
|
@@ -122,29 +174,29 @@ describe ExceptionalSynchrony::CallbackExceptions do
|
|
122
174
|
result = assert_raises(ArgumentError) do
|
123
175
|
ExceptionalSynchrony::CallbackExceptions.map_deferred_result(deferrable)
|
124
176
|
end
|
125
|
-
result.message.must_match
|
177
|
+
expect(result.message).must_match(/no deferred status set yet/i)
|
126
178
|
end
|
127
179
|
end
|
128
180
|
end
|
129
181
|
|
130
182
|
describe "return_exception" do
|
131
183
|
it "should return the value if no exception" do
|
132
|
-
ExceptionalSynchrony::CallbackExceptions.return_exception do
|
184
|
+
expect(ExceptionalSynchrony::CallbackExceptions.return_exception do
|
133
185
|
14
|
134
|
-
end.must_equal
|
186
|
+
end).must_equal(14)
|
135
187
|
end
|
136
188
|
|
137
189
|
it "should yield its args" do
|
138
|
-
ExceptionalSynchrony::CallbackExceptions.return_exception(0, 1) do |a, b|
|
190
|
+
expect(ExceptionalSynchrony::CallbackExceptions.return_exception(0, 1) do |a, b|
|
139
191
|
assert_equal [0, 1], [a, b]
|
140
192
|
14
|
141
|
-
end.must_equal
|
193
|
+
end).must_equal(14)
|
142
194
|
end
|
143
195
|
|
144
196
|
it "should rescue any exception that was raised and return it" do
|
145
|
-
ExceptionalSynchrony::CallbackExceptions.return_exception do
|
197
|
+
expect(ExceptionalSynchrony::CallbackExceptions.return_exception do
|
146
198
|
raise ArgumentError, "An argument error occurred"
|
147
|
-
end.inspect.must_equal
|
199
|
+
end.inspect).must_equal(ArgumentError.new("An argument error occurred").inspect)
|
148
200
|
end
|
149
201
|
end
|
150
202
|
end
|
@@ -39,7 +39,45 @@ describe ExceptionalSynchrony::EventMachineProxy do
|
|
39
39
|
ServerClass = Class.new
|
40
40
|
mock(EventMachine).connect(ServerClass, 8080, :handler, :extra_arg).yields(:called)
|
41
41
|
@em.connect(ServerClass, 8080, :handler, :extra_arg, &@block)
|
42
|
-
@yielded_value.must_equal
|
42
|
+
expect(@yielded_value).must_equal(:called)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#yield_to_reactor" do
|
46
|
+
it "should give control to other threads when the reactor is running" do
|
47
|
+
mock(@em).reactor_running? { true }
|
48
|
+
mock(EventMachine::Synchrony).sleep(0)
|
49
|
+
@em.yield_to_reactor
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should be a no-op if the reactor is not running" do
|
53
|
+
mock(@em).reactor_running? { false }
|
54
|
+
stub(EventMachine::Synchrony).sleep(0) { raise "Should not sleep!" }
|
55
|
+
@em.yield_to_reactor
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#defer" do
|
60
|
+
it "should output its block's output when it doesn't raise an error" do
|
61
|
+
ExceptionHandling.logger = Logger.new(STDERR)
|
62
|
+
|
63
|
+
@em.run do
|
64
|
+
assert_equal 12, @em.defer("#defer success") { 12 }
|
65
|
+
@em.stop
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should raise an error when its block raises an error" do
|
70
|
+
ExceptionHandling.logger = Logger.new(STDERR)
|
71
|
+
|
72
|
+
@em.run do
|
73
|
+
ex = assert_raises(ArgumentError) do
|
74
|
+
@em.defer("#defer raising an error") { raise ArgumentError, "!!!" }
|
75
|
+
end
|
76
|
+
|
77
|
+
assert_equal "!!!", ex.message
|
78
|
+
@em.stop
|
79
|
+
end
|
80
|
+
end
|
43
81
|
end
|
44
82
|
|
45
83
|
EXCEPTION = ArgumentError.new('in block')
|
@@ -50,19 +88,19 @@ describe ExceptionalSynchrony::EventMachineProxy do
|
|
50
88
|
end
|
51
89
|
|
52
90
|
it "add_timer" do
|
53
|
-
mock(ExceptionHandling).log_error(EXCEPTION, "add_timer")
|
91
|
+
mock(ExceptionHandling).log_error(EXCEPTION, "add_timer", {})
|
54
92
|
mock(EventMachine::Synchrony).add_timer(10) { |duration, *args| args.first.call }
|
55
93
|
@em.add_timer(10) { raise EXCEPTION }
|
56
94
|
end
|
57
95
|
|
58
96
|
it "add_periodic_timer" do
|
59
|
-
mock(ExceptionHandling).log_error(EXCEPTION, "add_periodic_timer")
|
97
|
+
mock(ExceptionHandling).log_error(EXCEPTION, "add_periodic_timer", {})
|
60
98
|
mock(EventMachine::Synchrony).add_periodic_timer(10) { |duration, *args| args.first.call }
|
61
99
|
@em.add_periodic_timer(10) { raise EXCEPTION }
|
62
100
|
end
|
63
101
|
|
64
102
|
it "next_tick" do
|
65
|
-
mock(ExceptionHandling).log_error(EXCEPTION, "next_tick")
|
103
|
+
mock(ExceptionHandling).log_error(EXCEPTION, "next_tick", {})
|
66
104
|
mock(EventMachine::Synchrony).next_tick { |*args| args.first.call }
|
67
105
|
@em.next_tick { raise EXCEPTION }
|
68
106
|
end
|
@@ -88,7 +126,7 @@ describe ExceptionalSynchrony::EventMachineProxy do
|
|
88
126
|
proxy = ExceptionalSynchrony::EventMachineProxy.new(proxy_mock, nil)
|
89
127
|
|
90
128
|
proxy.run(&@block)
|
91
|
-
@yielded_value.must_equal
|
129
|
+
expect(@yielded_value).must_equal(synchrony ? :synchrony : :run)
|
92
130
|
end
|
93
131
|
end
|
94
132
|
end
|
@@ -4,13 +4,13 @@ describe ExceptionalSynchrony::LimitedWorkQueue do
|
|
4
4
|
end
|
5
5
|
|
6
6
|
it "should raise an exception if created with a limit < 1" do
|
7
|
-
assert_raises(ArgumentError) do
|
7
|
+
expect(assert_raises(ArgumentError) do
|
8
8
|
ExceptionalSynchrony::LimitedWorkQueue.new(@em, 0)
|
9
|
-
end.message.must_match
|
9
|
+
end.message).must_match(/must be positive/)
|
10
10
|
|
11
|
-
assert_raises(ArgumentError) do
|
11
|
+
expect(assert_raises(ArgumentError) do
|
12
12
|
ExceptionalSynchrony::LimitedWorkQueue.new(@em, -2)
|
13
|
-
end.message.must_match
|
13
|
+
end.message).must_match(/must be positive/)
|
14
14
|
end
|
15
15
|
|
16
16
|
describe "when created" do
|
@@ -21,9 +21,9 @@ describe ExceptionalSynchrony::LimitedWorkQueue do
|
|
21
21
|
it "should run non-blocking jobs immediately" do
|
22
22
|
c = 0
|
23
23
|
ExceptionalSynchrony::EMP.run_and_stop do
|
24
|
-
@queue.add { c+=1 }
|
25
|
-
@queue.add { c+=1 }
|
26
|
-
@queue.add { c+=1 }
|
24
|
+
@queue.add! { c+=1 }
|
25
|
+
@queue.add! { c+=1 }
|
26
|
+
@queue.add! { c+=1 }
|
27
27
|
end
|
28
28
|
assert_equal 3, c
|
29
29
|
end
|
@@ -46,9 +46,9 @@ describe ExceptionalSynchrony::LimitedWorkQueue do
|
|
46
46
|
it "should allow objects to be queued instead of Procs" do
|
47
47
|
c = 0
|
48
48
|
ExceptionalSynchrony::EMP.run_and_stop do
|
49
|
-
@queue.add(LWQTestProc.new { c+=1 })
|
50
|
-
@queue.add(LWQTestProc.new { c+=1 })
|
51
|
-
@queue.add(LWQTestProc.new { c+=1 })
|
49
|
+
@queue.add!(LWQTestProc.new { c+=1 })
|
50
|
+
@queue.add!(LWQTestProc.new { c+=1 })
|
51
|
+
@queue.add!(LWQTestProc.new { c+=1 })
|
52
52
|
end
|
53
53
|
assert_equal 3, c
|
54
54
|
end
|
@@ -71,17 +71,17 @@ describe ExceptionalSynchrony::LimitedWorkQueue do
|
|
71
71
|
end
|
72
72
|
|
73
73
|
ExceptionalSynchrony::EMP.run_and_stop do
|
74
|
-
@queue.add(job_proc)
|
74
|
+
@queue.add!(job_proc)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
78
|
it "should allow objects to merge themselves into the queue (canceling itself)" do
|
79
79
|
c = 0
|
80
80
|
ExceptionalSynchrony::EMP.run_and_stop do
|
81
|
-
@queue.add(LWQTestProc.new { @em.sleep(0.001); c+=1 })
|
82
|
-
@queue.add(LWQTestProc.new { @em.sleep(0.001); c+=2 })
|
83
|
-
@queue.add(LWQTestProcWithMergeDrop.new(-> { c+=4 }) { @em.sleep(0.001); c+=8 })
|
84
|
-
@queue.add(LWQTestProcWithMergeDrop.new(-> { c+=16 }) { @em.sleep(0.001); c+=32 }) # will get merged (by canceling self)
|
81
|
+
@queue.add!(LWQTestProc.new { @em.sleep(0.001); c+=1 })
|
82
|
+
@queue.add!(LWQTestProc.new { @em.sleep(0.001); c+=2 })
|
83
|
+
@queue.add!(LWQTestProcWithMergeDrop.new(-> { c+=4 }) { @em.sleep(0.001); c+=8 })
|
84
|
+
@queue.add!(LWQTestProcWithMergeDrop.new(-> { c+=16 }) { @em.sleep(0.001); c+=32 }) # will get merged (by canceling self)
|
85
85
|
@em.sleep(0.050)
|
86
86
|
end
|
87
87
|
assert_equal 1+2+8+16, c
|
@@ -99,15 +99,35 @@ describe ExceptionalSynchrony::LimitedWorkQueue do
|
|
99
99
|
it "should allow objects to merge themselves into the queue (canceling/replacing earlier)" do
|
100
100
|
c = 0
|
101
101
|
ExceptionalSynchrony::EMP.run_and_stop do
|
102
|
-
@queue.add(LWQTestProc.new { @em.sleep(0.001); c+=1 })
|
103
|
-
@queue.add(LWQTestProc.new { @em.sleep(0.001); c+=2 })
|
104
|
-
@queue.add(LWQTestProcWithMergeReplace.new(-> { c+=4 }) { @em.sleep(0.001); c+=8 })
|
105
|
-
@queue.add(LWQTestProcWithMergeReplace.new(-> { c+=16 }) { @em.sleep(0.001); c+=32 }) # will get merged with above (replacing above)
|
102
|
+
@queue.add!(LWQTestProc.new { @em.sleep(0.001); c+=1 })
|
103
|
+
@queue.add!(LWQTestProc.new { @em.sleep(0.001); c+=2 })
|
104
|
+
@queue.add!(LWQTestProcWithMergeReplace.new(-> { c+=4 }) { @em.sleep(0.001); c+=8 })
|
105
|
+
@queue.add!(LWQTestProcWithMergeReplace.new(-> { c+=16 }) { @em.sleep(0.001); c+=32 }) # will get merged with above (replacing above)
|
106
106
|
@em.sleep(0.050)
|
107
107
|
end
|
108
108
|
assert_equal 1+2+4+32, c
|
109
109
|
end
|
110
110
|
|
111
|
+
it "should ensure that the queue continues to function even when an exception is raised" do
|
112
|
+
mock(ExceptionHandling).log_error(anything, /LimitedWorkQueue encountered an exception/).twice
|
113
|
+
ExceptionalSynchrony::EMP.run_and_stop do
|
114
|
+
c = 0
|
115
|
+
last_start = nil; end_0 = nil; end_1 = nil
|
116
|
+
@queue.add! { c += 1; @em.sleep(0.001); end_0 = c+=1; raise "Boom" }
|
117
|
+
@queue.add! { c += 1; @em.sleep(0.001); end_1 = c+=1; raise "Boom" }
|
118
|
+
@queue.add! { last_start = c+= 1; @em.sleep(0.001); c+=1 }
|
119
|
+
|
120
|
+
3.times do
|
121
|
+
@em.sleep(0.030)
|
122
|
+
break if c == 6
|
123
|
+
end
|
124
|
+
|
125
|
+
assert last_start && last_start > 3, "Unexpected value for last_start: #{last_start.inspect}"
|
126
|
+
assert [end_0, end_1].any? {|e| last_start > e }
|
127
|
+
assert_equal 6, c
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
111
131
|
it "should run 2 blocking tasks in parallel and only start 3rd when one of the first 2 finishes" do
|
112
132
|
stub_request(:get, "http://www.google.com/").
|
113
133
|
to_return(:status => 200, :body => "1", :headers => {})
|
@@ -121,12 +141,12 @@ describe ExceptionalSynchrony::LimitedWorkQueue do
|
|
121
141
|
ExceptionalSynchrony::EMP.run_and_stop do
|
122
142
|
c = -1
|
123
143
|
started2 = nil; ended0 = nil; ended1 = nil
|
124
|
-
@queue.add { c+=1; @em.sleep(0.001); ExceptionalSynchrony::EMP.connection.new("http://www.google.com").get; ended0 = c+=1 }
|
125
|
-
@queue.add { c+=1; @em.sleep(0.001); ExceptionalSynchrony::EMP.connection.new("http://www.cnn.com").get; ended1 = c+=1 }
|
126
|
-
@queue.add { started2 = c+=1; ExceptionalSynchrony::EMP.connection.new("http://news.ycombinator.com").get; c+=1 }
|
144
|
+
@queue.add! { c+=1; @em.sleep(0.001); ExceptionalSynchrony::EMP.connection.new("http://www.google.com").get; ended0 = c+=1 }
|
145
|
+
@queue.add! { c+=1; @em.sleep(0.001); ExceptionalSynchrony::EMP.connection.new("http://www.cnn.com").get; ended1 = c+=1 }
|
146
|
+
@queue.add! { started2 = c+=1; ExceptionalSynchrony::EMP.connection.new("http://news.ycombinator.com").get; c+=1 }
|
127
147
|
|
128
148
|
3.times do
|
129
|
-
@em.sleep(0.
|
149
|
+
@em.sleep(0.030)
|
130
150
|
break if c == 5
|
131
151
|
end
|
132
152
|
|
@@ -135,5 +155,183 @@ describe ExceptionalSynchrony::LimitedWorkQueue do
|
|
135
155
|
assert started2 > ended0 || started2 > ended1, [ended0, ended1, started2].inspect
|
136
156
|
end
|
137
157
|
end
|
158
|
+
|
159
|
+
describe '#paused?' do
|
160
|
+
it 'should return false when @paused is false' do
|
161
|
+
@queue.instance_variable_set(:@paused, false)
|
162
|
+
assert_equal false, @queue.paused?
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should return true when @paused is true' do
|
166
|
+
@queue.instance_variable_set(:@paused, false)
|
167
|
+
assert_equal false, @queue.paused?
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe '#pause!' do
|
172
|
+
it 'should set @paused to true if @paused is currently false' do
|
173
|
+
assert_equal false, @queue.instance_variable_get('@paused')
|
174
|
+
@queue.pause!
|
175
|
+
assert_equal true, @queue.instance_variable_get('@paused')
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should set @paused to true if @paused is currently true' do
|
179
|
+
@queue.instance_variable_set(:@paused, true)
|
180
|
+
assert_equal true, @queue.instance_variable_get('@paused')
|
181
|
+
@queue.pause!
|
182
|
+
assert_equal true, @queue.instance_variable_get('@paused')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '#unpause!' do
|
187
|
+
it 'should set @paused to false if @paused is currently false' do
|
188
|
+
assert_equal false, @queue.instance_variable_get('@paused')
|
189
|
+
@queue.unpause!
|
190
|
+
assert_equal false, @queue.instance_variable_get('@paused')
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should set @paused to false if @paused is currently true' do
|
194
|
+
@queue.instance_variable_set(:@paused, true)
|
195
|
+
assert_equal true, @queue.instance_variable_get('@paused')
|
196
|
+
@queue.unpause!
|
197
|
+
assert_equal false, @queue.instance_variable_get('@paused')
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#add!' do
|
202
|
+
it 'should run jobs added to the queue when paused? is false' do
|
203
|
+
assert_equal false, @queue.paused? # queue#paused is false be default
|
204
|
+
counter = 0
|
205
|
+
ExceptionalSynchrony::EMP.run_and_stop do
|
206
|
+
3.times { @queue.add! { counter+=1 } }
|
207
|
+
end
|
208
|
+
|
209
|
+
assert_equal 0, @queue.instance_variable_get("@job_procs").size
|
210
|
+
assert_equal 3, counter
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should not run jobs added to the queue when paused? is true' do
|
214
|
+
@queue.pause!
|
215
|
+
assert_equal true, @queue.paused?
|
216
|
+
counter = 0
|
217
|
+
ExceptionalSynchrony::EMP.run_and_stop do
|
218
|
+
3.times { @queue.add! { counter+=1 } }
|
219
|
+
end
|
220
|
+
|
221
|
+
mock(@queue).work!.never
|
222
|
+
assert_equal 3, @queue.instance_variable_get("@job_procs").size
|
223
|
+
assert_equal 0, counter
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should have a method alias to #add for callers using the previous #add method' do
|
227
|
+
assert_equal @queue.method(:add!), @queue.method(:add)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe '#items' do
|
232
|
+
it 'should return job_procs array' do
|
233
|
+
assert_equal 0, @queue.items.size
|
234
|
+
job_procs = Array.new(3) { Proc.new { puts 'Hello World' } }
|
235
|
+
@queue.instance_variable_set(:@job_procs, job_procs)
|
236
|
+
assert_equal 3, @queue.items.size
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
describe '#work!' do
|
241
|
+
before do
|
242
|
+
@queue = ExceptionalSynchrony::LimitedWorkQueue.new(@em, 2)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'should run items in queue' do
|
246
|
+
counter = 0
|
247
|
+
job_procs = Array.new(3) { Proc.new { counter += 1} }
|
248
|
+
@queue.instance_variable_set(:@job_procs, job_procs)
|
249
|
+
mock.proxy(Fiber).new.with_any_args.times(3)
|
250
|
+
@queue.work!
|
251
|
+
|
252
|
+
assert_equal 0, @queue.instance_variable_get("@job_procs").size
|
253
|
+
assert_equal 3, counter
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should run items already in queue, even if queue is paused' do
|
257
|
+
@queue.pause!
|
258
|
+
counter = 4
|
259
|
+
job_procs = Array.new(3) { Proc.new { counter += 1} }
|
260
|
+
@queue.instance_variable_set(:@job_procs, job_procs)
|
261
|
+
assert_equal 3, @queue.instance_variable_get("@job_procs").size
|
262
|
+
mock.proxy(Fiber).new.with_any_args.times(3)
|
263
|
+
|
264
|
+
@queue.work!
|
265
|
+
|
266
|
+
assert_equal 7, counter
|
267
|
+
assert_equal 0, @queue.instance_variable_get("@job_procs").size
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'should not run if queue_empty' do
|
271
|
+
stub(@queue).queue_empty? { true }
|
272
|
+
counter = 0
|
273
|
+
job_procs = Array.new(3) { Proc.new { counter += 1} }
|
274
|
+
@queue.instance_variable_set(:@job_procs, job_procs)
|
275
|
+
assert_equal 3, @queue.instance_variable_get("@job_procs").size
|
276
|
+
mock(Fiber).new.never
|
277
|
+
|
278
|
+
@queue.work!
|
279
|
+
assert_equal 0, counter
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'should not run if workers are full' do
|
283
|
+
stub(@queue).workers_full? { true }
|
284
|
+
counter = 0
|
285
|
+
job_procs = Array.new(3) { Proc.new { counter += 1} }
|
286
|
+
@queue.instance_variable_set(:@job_procs, job_procs)
|
287
|
+
|
288
|
+
assert_equal 3, @queue.instance_variable_get("@job_procs").size
|
289
|
+
mock(Fiber).new.never
|
290
|
+
|
291
|
+
@queue.work!
|
292
|
+
assert_equal 0, counter
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe '#workers_full?' do
|
297
|
+
before do
|
298
|
+
@queue = ExceptionalSynchrony::LimitedWorkQueue.new(@em, 2) # limit the number of active workers to 2 max
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'should return true if the number of current workers is greater than the queue\'s defined worker limit' do
|
302
|
+
@queue.instance_variable_set(:@worker_count, 20)
|
303
|
+
@queue.instance_variable_set(:@limit, 10)
|
304
|
+
assert_equal true, @queue.workers_full?
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'should return true if the current number of workers is equal to the queue\'s defined worker limit' do
|
308
|
+
@queue.instance_variable_set(:@worker_count, 10)
|
309
|
+
@queue.instance_variable_set(:@limit, 10)
|
310
|
+
assert_equal true, @queue.workers_full?
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'should return false if the current number of workers is less than the queue\'s defined worker limit' do
|
314
|
+
@queue.instance_variable_set(:@worker_count, 5)
|
315
|
+
@queue.instance_variable_set(:@limit, 10)
|
316
|
+
assert_equal false, @queue.workers_full?
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
describe '#queue_empty?' do
|
321
|
+
before do
|
322
|
+
@queue = ExceptionalSynchrony::LimitedWorkQueue.new(@em, 2)
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'should return true if queue is empty' do
|
326
|
+
@queue.instance_variable_set(:@job_procs, [])
|
327
|
+
assert_equal true, @queue.queue_empty?
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'should return false if queue is not empty' do
|
331
|
+
@queue.instance_variable_set(:@job_procs, [Proc.new { 'hello'}])
|
332
|
+
assert_equal false, @queue.queue_empty?
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
138
336
|
end
|
139
337
|
end
|
@@ -56,15 +56,15 @@ describe ExceptionalSynchrony::ParallelSync do
|
|
56
56
|
stub_request(:get, "http://news.ycombinator.com/").
|
57
57
|
to_return(:status => 200, :body => "3", :headers => {})
|
58
58
|
|
59
|
-
assert_raises(NotImplementedError) do
|
59
|
+
expect(assert_raises(NotImplementedError) do
|
60
60
|
ExceptionalSynchrony::EMP.run_and_stop do
|
61
|
-
|
61
|
+
ExceptionalSynchrony::ParallelSync.parallel(@em) do |parallel|
|
62
62
|
parallel.add { ExceptionalSynchrony::EMP.connection.new("http://www.google.com").get; raise NotImplementedError, "Not implemented!" }
|
63
63
|
parallel.add { ExceptionalSynchrony::EMP.connection.new("http://www.cnn.com").get }
|
64
64
|
parallel.add { ExceptionalSynchrony::EMP.connection.new("http://news.ycombinator.com").get }
|
65
65
|
end
|
66
66
|
end
|
67
|
-
end.to_s.must_match(/Not implemented!/)
|
67
|
+
end.to_s).must_match(/Not implemented!/)
|
68
68
|
end
|
69
69
|
|
70
70
|
it "should pass several exceptions through and raise them on the other side" do
|
@@ -77,15 +77,15 @@ describe ExceptionalSynchrony::ParallelSync do
|
|
77
77
|
stub_request(:get, "http://news.ycombinator.com/").
|
78
78
|
to_return(:status => 200, :body => "3", :headers => {})
|
79
79
|
|
80
|
-
assert_raises(NotImplementedError) do
|
80
|
+
expect(assert_raises(NotImplementedError) do
|
81
81
|
ExceptionalSynchrony::EMP.run_and_stop do
|
82
|
-
|
82
|
+
ExceptionalSynchrony::ParallelSync.parallel(@em) do |parallel|
|
83
83
|
parallel.add { ExceptionalSynchrony::EMP.connection.new("http://www.google.com").get; raise NotImplementedError, "Not implemented!" }
|
84
84
|
parallel.add { ExceptionalSynchrony::EMP.connection.new("http://www.cnn.com").get; raise LoadError, "A load error occurred" }
|
85
85
|
parallel.add { ExceptionalSynchrony::EMP.connection.new("http://news.ycombinator.com").get; raise IndexError, "An index error occurred" }
|
86
86
|
end
|
87
87
|
end
|
88
|
-
end.message.must_match(/Not implemented!.*LoadError: A load error occurred.*IndexError: An index error occurred/m)
|
88
|
+
end.message).must_match(/Not implemented!.*LoadError: A load error occurred.*IndexError: An index error occurred/m)
|
89
89
|
end
|
90
90
|
|
91
91
|
class TestProc
|