backburner-allq 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +29 -0
  4. data/CHANGELOG.md +133 -0
  5. data/CONTRIBUTING.md +37 -0
  6. data/Gemfile +4 -0
  7. data/HOOKS.md +99 -0
  8. data/LICENSE +22 -0
  9. data/README.md +658 -0
  10. data/Rakefile +17 -0
  11. data/TODO +4 -0
  12. data/backburner-allq.gemspec +26 -0
  13. data/bin/backburner +7 -0
  14. data/circle.yml +3 -0
  15. data/deploy.sh +3 -0
  16. data/examples/custom.rb +25 -0
  17. data/examples/demo.rb +60 -0
  18. data/examples/god.rb +46 -0
  19. data/examples/hooked.rb +87 -0
  20. data/examples/retried.rb +31 -0
  21. data/examples/simple.rb +43 -0
  22. data/examples/stress.rb +31 -0
  23. data/lib/backburner.rb +75 -0
  24. data/lib/backburner/allq_wrapper.rb +317 -0
  25. data/lib/backburner/async_proxy.rb +25 -0
  26. data/lib/backburner/cli.rb +53 -0
  27. data/lib/backburner/configuration.rb +48 -0
  28. data/lib/backburner/connection.rb +157 -0
  29. data/lib/backburner/helpers.rb +193 -0
  30. data/lib/backburner/hooks.rb +53 -0
  31. data/lib/backburner/job.rb +118 -0
  32. data/lib/backburner/logger.rb +53 -0
  33. data/lib/backburner/performable.rb +95 -0
  34. data/lib/backburner/queue.rb +145 -0
  35. data/lib/backburner/tasks.rb +54 -0
  36. data/lib/backburner/version.rb +3 -0
  37. data/lib/backburner/worker.rb +221 -0
  38. data/lib/backburner/workers/forking.rb +52 -0
  39. data/lib/backburner/workers/simple.rb +29 -0
  40. data/lib/backburner/workers/threading.rb +163 -0
  41. data/lib/backburner/workers/threads_on_fork.rb +263 -0
  42. data/test/async_proxy_test.rb +36 -0
  43. data/test/back_burner_test.rb +88 -0
  44. data/test/connection_test.rb +179 -0
  45. data/test/fixtures/hooked.rb +122 -0
  46. data/test/fixtures/test_fork_jobs.rb +72 -0
  47. data/test/fixtures/test_forking_jobs.rb +56 -0
  48. data/test/fixtures/test_jobs.rb +87 -0
  49. data/test/fixtures/test_queue_settings.rb +14 -0
  50. data/test/helpers/templogger.rb +22 -0
  51. data/test/helpers_test.rb +278 -0
  52. data/test/hooks_test.rb +112 -0
  53. data/test/job_test.rb +185 -0
  54. data/test/logger_test.rb +44 -0
  55. data/test/performable_test.rb +88 -0
  56. data/test/queue_test.rb +69 -0
  57. data/test/test_helper.rb +128 -0
  58. data/test/worker_test.rb +157 -0
  59. data/test/workers/forking_worker_test.rb +181 -0
  60. data/test/workers/simple_worker_test.rb +350 -0
  61. data/test/workers/threading_worker_test.rb +104 -0
  62. data/test/workers/threads_on_fork_worker_test.rb +484 -0
  63. metadata +217 -0
@@ -0,0 +1,350 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require File.expand_path('../../fixtures/test_jobs', __FILE__)
3
+ require File.expand_path('../../fixtures/hooked', __FILE__)
4
+
5
+ describe "Backburner::Workers::Simple module" do
6
+ before do
7
+ Backburner.default_queues.clear
8
+ @worker_class = Backburner::Workers::Simple
9
+ end
10
+
11
+ describe "for prepare method" do
12
+ it "should make tube names array always unique to avoid duplication" do
13
+ worker = @worker_class.new(["foo", "demo.test.foo"])
14
+ capture_stdout { worker.prepare }
15
+ assert_equal ["demo.test.foo"], worker.tube_names
16
+ end
17
+
18
+ it "should watch specified tubes" do
19
+ worker = @worker_class.new(["foo", "bar"])
20
+ out = capture_stdout { worker.prepare }
21
+ assert_equal ["demo.test.foo", "demo.test.bar"], worker.tube_names
22
+ assert_same_elements ["demo.test.foo", "demo.test.bar"], worker.connection.tubes.watched.map(&:name)
23
+ assert_match(/demo\.test\.foo/, out)
24
+ end # multiple
25
+
26
+ it "should watch single tube" do
27
+ worker = @worker_class.new("foo")
28
+ out = capture_stdout { worker.prepare }
29
+ assert_equal ["demo.test.foo"], worker.tube_names
30
+ assert_same_elements ["demo.test.foo"], worker.connection.tubes.watched.map(&:name)
31
+ assert_match(/demo\.test\.foo/, out)
32
+ end # single
33
+
34
+ it "should respect default_queues settings" do
35
+ Backburner.default_queues.concat(["foo", "bar"])
36
+ worker = @worker_class.new
37
+ out = capture_stdout { worker.prepare }
38
+ assert_equal ["demo.test.foo", "demo.test.bar"], worker.tube_names
39
+ assert_same_elements ["demo.test.foo", "demo.test.bar"], worker.connection.tubes.watched.map(&:name)
40
+ assert_match(/demo\.test\.foo/, out)
41
+ end
42
+
43
+ it "should assign based on all tubes" do
44
+ @worker_class.any_instance.expects(:all_existing_queues).once.returns("bar")
45
+ worker = @worker_class.new
46
+ out = capture_stdout { worker.prepare }
47
+ assert_equal ["demo.test.bar"], worker.tube_names
48
+ assert_same_elements ["demo.test.bar"], worker.connection.tubes.watched.map(&:name)
49
+ assert_match(/demo\.test\.bar/, out)
50
+ end # all assign
51
+
52
+ it "should properly retrieve all tubes" do
53
+ worker = @worker_class.new
54
+ out = capture_stdout { worker.prepare }
55
+ assert_contains worker.tube_names, "demo.test.backburner-jobs"
56
+ assert_contains worker.connection.tubes.watched.map(&:name), "demo.test.backburner-jobs"
57
+ assert_match(/demo\.test\.backburner-jobs/, out)
58
+ end # all read
59
+ end # prepare
60
+
61
+ describe "for work_one_job method" do
62
+ before do
63
+ $worker_test_count = 0
64
+ $worker_success = false
65
+ end
66
+
67
+ it "should work a plain enqueued job" do
68
+ clear_jobs!("foo.bar")
69
+ @worker_class.enqueue TestPlainJob, [1, 2], :queue => "foo.bar"
70
+ silenced(2) do
71
+ worker = @worker_class.new('foo.bar')
72
+ worker.prepare
73
+ worker.work_one_job
74
+ end
75
+ assert_equal 4, $worker_test_count
76
+ end # plain enqueue
77
+
78
+ it "should work an enqueued job" do
79
+ clear_jobs!("foo.bar")
80
+ @worker_class.enqueue TestJob, [1, 2], :queue => "foo.bar"
81
+ silenced(2) do
82
+ worker = @worker_class.new('foo.bar')
83
+ worker.prepare
84
+ worker.work_one_job
85
+ end
86
+ assert_equal 3, $worker_test_count
87
+ end # enqueue
88
+
89
+ it "should fail quietly if there's an argument error" do
90
+ clear_jobs!("foo.bar")
91
+ @worker_class.enqueue TestJob, ["bam", "foo", "bar"], :queue => "foo.bar"
92
+ out = silenced(2) do
93
+ worker = @worker_class.new('foo.bar')
94
+ worker.prepare
95
+ worker.work_one_job
96
+ end
97
+ assert_match(/Exception ArgumentError/, out)
98
+ assert_equal 0, $worker_test_count
99
+ end # fail, argument
100
+
101
+ it "should work an enqueued failing job" do
102
+ # NB: The #bury expectation below leaves the job in the queue (as reserved!)
103
+ # since bury is never actually called on the task. Therefore, clear_jobs!()
104
+ # can't remove it which can break a lot of things depending on the order the
105
+ # tests are run. So we ensure that it's using a unique queue name. Mocha
106
+ # lacks expectations with proxies (where we could actually call bury)
107
+ clear_jobs!('foo.bar.failed')
108
+ @worker_class.enqueue TestFailJob, [1, 2], :queue => 'foo.bar.failed'
109
+ Backburner::Job.any_instance.expects(:bury).once
110
+ out = silenced(2) do
111
+ worker = @worker_class.new('foo.bar.failed')
112
+ worker.prepare
113
+ worker.work_one_job
114
+ end
115
+ assert_match(/Exception RuntimeError/, out)
116
+ assert_equal 0, $worker_test_count
117
+ end # fail, runtime error
118
+
119
+ it "should work an invalid job parsed" do
120
+ Beaneater::Tubes.any_instance.expects(:reserve).returns(stub(:body => "{%$^}", :bury => true))
121
+ out = silenced(2) do
122
+ worker = @worker_class.new('foo.bar')
123
+ worker.prepare
124
+ worker.work_one_job
125
+ end
126
+ assert_match(/Exception Backburner::Job::JobFormatInvalid/, out)
127
+ assert_equal 0, $worker_test_count
128
+ end # fail, runtime error
129
+
130
+ it "should work for an async job" do
131
+ clear_jobs!('foo.bar')
132
+ TestAsyncJob.async(:queue => 'foo.bar').foo(3, 5)
133
+ silenced(2) do
134
+ worker = @worker_class.new('foo.bar')
135
+ worker.prepare
136
+ worker.work_one_job
137
+ end
138
+ assert_equal 15, $worker_test_count
139
+ end # async
140
+
141
+ it "should support retrying jobs and burying" do
142
+ clear_jobs!('foo.bar')
143
+ Backburner.configure { |config| config.max_job_retries = 1; config.retry_delay = 0 }
144
+ @worker_class.enqueue TestRetryJob, ["bam", "foo"], :queue => 'foo.bar'
145
+ out = []
146
+ 2.times do
147
+ out << silenced(2) do
148
+ worker = @worker_class.new('foo.bar')
149
+ worker.prepare
150
+ worker.work_one_job
151
+ end
152
+ end
153
+ assert_match(/attempt 1 of 2, retrying/, out.first)
154
+ assert_match(/Finished TestRetryJob/m, out.last)
155
+ assert_match(/attempt 2 of 2, burying/m, out.last)
156
+ assert_equal 2, $worker_test_count
157
+ assert_equal false, $worker_success
158
+ end # retry, bury
159
+
160
+ it "should support retrying jobs and succeeds" do
161
+ clear_jobs!('foo.bar')
162
+ Backburner.configure { |config| config.max_job_retries = 2; config.retry_delay = 0 }
163
+ @worker_class.enqueue TestRetryJob, ["bam", "foo"], :queue => 'foo.bar'
164
+ out = []
165
+ 3.times do
166
+ out << silenced(2) do
167
+ worker = @worker_class.new('foo.bar')
168
+ worker.prepare
169
+ worker.work_one_job
170
+ end
171
+ end
172
+ assert_match(/attempt 1 of 3, retrying/, out.first)
173
+ assert_match(/attempt 2 of 3, retrying/, out[1])
174
+ assert_match(/Completed TestRetryJob/m, out.last)
175
+ refute_match(/failed/, out.last)
176
+ assert_equal 3, $worker_test_count
177
+ assert_equal true, $worker_success
178
+ end # retrying, succeeds
179
+
180
+ it "should back off retries exponentially" do
181
+ max_job_retries = 3
182
+ clear_jobs!('foo.bar')
183
+ Backburner.configure do |config|
184
+ config.max_job_retries = max_job_retries
185
+ config.retry_delay = 0
186
+ #config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) } # default retry_delay_proc
187
+ end
188
+ @worker_class.enqueue TestConfigurableRetryJob, [max_job_retries], :queue => 'foo.bar'
189
+ out = []
190
+ (max_job_retries + 1).times do
191
+ out << silenced(10) do
192
+ worker = @worker_class.new('foo.bar')
193
+ worker.prepare
194
+ worker.work_one_job
195
+ end
196
+ end
197
+ assert_match(/attempt 1 of 4, retrying in 0/, out.first)
198
+ assert_match(/attempt 2 of 4, retrying in 1/, out[1])
199
+ assert_match(/attempt 3 of 4, retrying in 8/, out[2])
200
+ assert_match(/Completed TestConfigurableRetryJob/m, out.last)
201
+ refute_match(/failed/, out.last)
202
+ assert_equal 4, $worker_test_count
203
+ assert_equal true, $worker_success
204
+ end
205
+
206
+ it "should allow configurable back off retry delays" do
207
+ max_job_retries = 3
208
+ clear_jobs!('foo.bar')
209
+ Backburner.configure do |config|
210
+ config.max_job_retries = max_job_retries
211
+ config.retry_delay = 0
212
+ config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 2) }
213
+ end
214
+ @worker_class.enqueue TestConfigurableRetryJob, [max_job_retries], :queue => 'foo.bar'
215
+ out = []
216
+ (max_job_retries + 1).times do
217
+ out << silenced(5) do
218
+ worker = @worker_class.new('foo.bar')
219
+ worker.prepare
220
+ worker.work_one_job
221
+ end
222
+ end
223
+ assert_match(/attempt 1 of 4, retrying in 0/, out.first)
224
+ assert_match(/attempt 2 of 4, retrying in 1/, out[1])
225
+ assert_match(/attempt 3 of 4, retrying in 4/, out[2])
226
+ assert_match(/Completed TestConfigurableRetryJob/m, out.last)
227
+ refute_match(/failed/, out.last)
228
+ assert_equal 4, $worker_test_count
229
+ assert_equal true, $worker_success
230
+ end
231
+
232
+ it "should allow queue override of retries" do
233
+ max_job_retries = TestRetryWithQueueOverridesJob.queue_max_job_retries
234
+ clear_jobs!('foo.bar')
235
+ Backburner.configure do |config|
236
+ # Config should be overridden by queue overrides
237
+ config.max_job_retries = 20
238
+ config.retry_delay = 60
239
+ #config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) } # default retry_delay_proc
240
+ end
241
+ @worker_class.enqueue TestRetryWithQueueOverridesJob, [max_job_retries], :queue => 'foo.bar'
242
+ out = []
243
+ (max_job_retries + 1).times do
244
+ out << silenced(5) do
245
+ worker = @worker_class.new('foo.bar')
246
+ worker.prepare
247
+ worker.work_one_job
248
+ end
249
+ end
250
+ assert_match(/attempt 1 of 4, retrying in 0/, out.first)
251
+ assert_match(/attempt 2 of 4, retrying in 1/, out[1])
252
+ assert_match(/attempt 3 of 4, retrying in 4/, out[2])
253
+ assert_match(/Completed TestRetryWithQueueOverridesJob/m, out.last)
254
+ refute_match(/failed/, out.last)
255
+ assert_equal 4, $worker_test_count
256
+ assert_equal true, $worker_success
257
+ end
258
+
259
+ it "should support event hooks without retry" do
260
+ $hooked_fail_count = 0
261
+ clear_jobs!('foo.bar.events')
262
+ out = silenced(2) do
263
+ HookedObjectSuccess.async(:queue => 'foo.bar.events').foo(5)
264
+ worker = @worker_class.new('foo.bar.events')
265
+ worker.prepare
266
+ worker.work_one_job
267
+ end
268
+ assert_match(/before_enqueue.*after_enqueue.*Working 1 queues/m, out)
269
+ assert_match(/!!before_enqueue_bar!! \[nil, :foo, 5\]/, out)
270
+ assert_match(/!!after_enqueue_bar!! \[nil, :foo, 5\]/, out)
271
+ assert_match(/!!before_perform_foo!! \[nil, "foo", 5\]/, out)
272
+ assert_match(/!!BEGIN around_perform_bar!! \[nil, "foo", 5\]/, out)
273
+ assert_match(/!!BEGIN around_perform_cat!! \[nil, "foo", 5\]/, out)
274
+ assert_match(/!!on_failure_foo!!.*HookFailError/, out)
275
+ assert_match(/!!on_bury_foo!! \[nil, "foo", 5\]/, out)
276
+ assert_match(/attempt 1 of 1, burying/, out)
277
+ end # event hooks, no retry
278
+
279
+ it "should support event hooks with retry" do
280
+ $hooked_fail_count = 0
281
+ clear_jobs!('foo.bar.events.retry')
282
+ Backburner.configure { |config| config.max_job_retries = 1; config.retry_delay = 0 }
283
+ out = silenced(2) do
284
+ HookedObjectSuccess.async(:queue => 'foo.bar.events.retry').foo(5)
285
+ worker = @worker_class.new('foo.bar.events.retry')
286
+ worker.prepare
287
+ 2.times do
288
+ worker.work_one_job
289
+ end
290
+ end
291
+ assert_match(/before_enqueue.*after_enqueue.*Working 1 queues/m, out)
292
+ assert_match(/!!before_enqueue_bar!! \[nil, :foo, 5\]/, out)
293
+ assert_match(/!!after_enqueue_bar!! \[nil, :foo, 5\]/, out)
294
+ assert_match(/!!before_perform_foo!! \[nil, "foo", 5\]/, out)
295
+ assert_match(/!!BEGIN around_perform_bar!! \[nil, "foo", 5\]/, out)
296
+ assert_match(/!!BEGIN around_perform_cat!! \[nil, "foo", 5\]/, out)
297
+ assert_match(/!!on_failure_foo!!.*HookFailError/, out)
298
+ assert_match(/!!on_failure_foo!!.*retrying.*around_perform_bar.*around_perform_cat/m, out)
299
+ assert_match(/!!on_retry_foo!! 1 0 \[nil, "foo", 5\]/, out)
300
+ assert_match(/attempt 1 of 2, retrying/, out)
301
+ assert_match(/!!before_perform_foo!! \[nil, "foo", 5\]/, out)
302
+ assert_match(/!!END around_perform_bar!! \[nil, "foo", 5\]/, out)
303
+ assert_match(/!!END around_perform_cat!! \[nil, "foo", 5\]/, out)
304
+ assert_match(/!!after_perform_foo!! \[nil, "foo", 5\]/, out)
305
+ assert_match(/Finished HookedObjectSuccess/, out)
306
+ end # event hooks, with retry
307
+
308
+ it "should support event hooks with stopping enqueue" do
309
+ $hooked_fail_count = 0
310
+ worker = @worker_class.new('foo.bar.events.retry2')
311
+ clear_jobs!('foo.bar.events.retry2')
312
+ silenced(2) do
313
+ HookedObjectBeforeEnqueueFail.async(:queue => 'foo.bar.events.retry2').foo(5)
314
+ end
315
+ expanded_tube = [Backburner.configuration.tube_namespace, 'foo.bar.events.retry2'].join(".")
316
+ assert_nil worker.connection.tubes[expanded_tube].peek(:ready)
317
+ end # stopping enqueue
318
+
319
+ it "should support event hooks with stopping perform" do
320
+ $hooked_fail_count = 0
321
+ clear_jobs!('foo.bar.events.retry3')
322
+ [Backburner.configuration.tube_namespace, 'foo.bar.events.retry3'].join(".")
323
+ out = silenced(2) do
324
+ HookedObjectBeforePerformFail.async(:queue => 'foo.bar.events.retry3').foo(10)
325
+ worker = @worker_class.new('foo.bar.events.retry3')
326
+ worker.prepare
327
+ worker.work_one_job
328
+ end
329
+ assert_match(/!!before_perform_foo!! \[nil, "foo", 10\]/, out)
330
+ assert_match(/before_perform_foo.*Completed/m, out)
331
+ refute_match(/Fail ran!!/, out)
332
+ refute_match(/HookFailError/, out)
333
+ end # stopping perform
334
+
335
+ it "should use the connection given as an argument" do
336
+ worker = @worker_class.new('foo.bar')
337
+ connection = mock('connection')
338
+ worker.expects(:reserve_job).with(connection).returns(stub_everything('job'))
339
+ capture_stdout { worker.work_one_job(connection) }
340
+ end
341
+
342
+ after do
343
+ Backburner.configure do |config|
344
+ config.max_job_retries = 0
345
+ config.retry_delay = 5
346
+ config.retry_delay_proc = lambda { |min_retry_delay, num_retries| min_retry_delay + (num_retries ** 3) }
347
+ end
348
+ end
349
+ end # work_one_job
350
+ end # Worker
@@ -0,0 +1,104 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require File.expand_path('../../fixtures/test_jobs', __FILE__)
3
+ require File.expand_path('../../fixtures/hooked', __FILE__)
4
+
5
+ describe "Backburner::Workers::Threading worker" do
6
+ before do
7
+ Backburner.default_queues.clear
8
+ @worker_class = Backburner::Workers::Threading
9
+ @worker_class.shutdown_timeout = 2
10
+ end
11
+
12
+ describe "for prepare method" do
13
+ it "should make tube names array always unique to avoid duplication" do
14
+ worker = @worker_class.new(["foo", "demo.test.foo"])
15
+ capture_stdout { worker.prepare }
16
+ assert_equal ["demo.test.foo"], worker.tube_names
17
+ end
18
+
19
+ it 'creates a thread pool per queue' do
20
+ worker = @worker_class.new(%w(foo bar))
21
+ capture_stdout { worker.prepare }
22
+ assert_equal 2, worker.instance_variable_get("@thread_pools").keys.size
23
+ end
24
+
25
+ it 'uses Concurrent.processor_count if no custom thread count is provided' do
26
+ worker = @worker_class.new("foo")
27
+ capture_stdout { worker.prepare }
28
+ assert_equal ::Concurrent.processor_count, worker.instance_variable_get("@thread_pools")["demo.test.foo"].max_length
29
+ end
30
+ end # prepare
31
+
32
+ describe "for process_tube_names method" do
33
+ it "should interpret the job_name:threads_limit format" do
34
+ worker = @worker_class.new(["foo:4"])
35
+ assert_equal ["foo"], worker.tube_names
36
+ end
37
+
38
+ it "should interpret correctly even if missing values" do
39
+ tubes = %W(foo1:2 foo2)
40
+ worker = @worker_class.new(tubes)
41
+ assert_equal %W(foo1 foo2), worker.tube_names
42
+ end
43
+
44
+ it "should store interpreted values correctly" do
45
+ tubes = %W(foo1 foo2:2)
46
+ worker = @worker_class.new(tubes)
47
+ assert_equal({
48
+ "demo.test.foo1" => { :threads => nil },
49
+ "demo.test.foo2" => { :threads => 2 }
50
+ }, worker.instance_variable_get("@tubes_data"))
51
+ end
52
+ end # process_tube_names
53
+
54
+ describe 'working a queue' do
55
+ before do
56
+ @worker = @worker_class.new(["foo:3"])
57
+ capture_stdout { @worker.prepare }
58
+ $worker_test_count = 0
59
+ $worker_success = false
60
+ end
61
+
62
+ it 'runs work_on_job per thread' do
63
+ clear_jobs!("foo")
64
+ job_count=10
65
+ # TestJob adds the given arguments together and then to $worker_test_count
66
+ job_count.times { @worker_class.enqueue TestJob, [1, 0], :queue => "foo" }
67
+ capture_stdout do
68
+ @worker.start(false) # don't wait for shutdown
69
+ sleep 0.5 # Wait for threads to do their work
70
+ end
71
+ assert_equal job_count, $worker_test_count
72
+ end
73
+ end # working a queue
74
+
75
+ describe 'shutting down' do
76
+ before do
77
+ @thread_count = 3
78
+ @worker = @worker_class.new(["threaded-shutdown:#{@thread_count}"])
79
+ @worker.exit_on_shutdown = false
80
+ $worker_test_count = 0
81
+ clear_jobs!("threaded-shutdown")
82
+ end
83
+
84
+ it 'gracefully exits and completes all in-flight jobs' do
85
+ 6.times { @worker_class.enqueue TestSlowJob, [1, 0], :queue => "threaded-shutdown" }
86
+ Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
87
+ capture_stdout do
88
+ @worker.start
89
+ end
90
+
91
+ assert_equal @thread_count, $worker_test_count
92
+ end
93
+
94
+ it 'forces an exit when a job is stuck' do
95
+ 6.times { @worker_class.enqueue TestStuckJob, [1, 0], :queue => "threaded-shutdown" }
96
+ Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
97
+ capture_stdout do
98
+ @worker.start
99
+ end
100
+
101
+ assert_equal 0, $worker_test_count
102
+ end
103
+ end
104
+ end