dat-worker-pool 0.5.0 → 0.6.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.
- data/Gemfile +1 -1
- data/bench/report.rb +130 -0
- data/bench/report.txt +7 -0
- data/dat-worker-pool.gemspec +1 -1
- data/lib/dat-worker-pool.rb +38 -187
- data/lib/dat-worker-pool/default_queue.rb +60 -0
- data/lib/dat-worker-pool/locked_object.rb +60 -0
- data/lib/dat-worker-pool/queue.rb +71 -48
- data/lib/dat-worker-pool/runner.rb +196 -0
- data/lib/dat-worker-pool/version.rb +1 -1
- data/lib/dat-worker-pool/worker.rb +251 -72
- data/lib/dat-worker-pool/worker_pool_spy.rb +39 -53
- data/test/helper.rb +13 -0
- data/test/support/factory.rb +15 -0
- data/test/support/thread_spies.rb +83 -0
- data/test/system/dat-worker-pool_tests.rb +399 -0
- data/test/unit/dat-worker-pool_tests.rb +132 -255
- data/test/unit/default_queue_tests.rb +217 -0
- data/test/unit/locked_object_tests.rb +260 -0
- data/test/unit/queue_tests.rb +95 -72
- data/test/unit/runner_tests.rb +365 -0
- data/test/unit/worker_pool_spy_tests.rb +95 -102
- data/test/unit/worker_tests.rb +819 -153
- metadata +27 -12
- data/test/system/use_worker_pool_tests.rb +0 -34
@@ -1,142 +1,135 @@
|
|
1
1
|
require 'assert'
|
2
2
|
require 'dat-worker-pool/worker_pool_spy'
|
3
3
|
|
4
|
+
require 'dat-worker-pool'
|
5
|
+
require 'dat-worker-pool/default_queue'
|
6
|
+
require 'dat-worker-pool/worker'
|
7
|
+
|
4
8
|
class DatWorkerPool::WorkerPoolSpy
|
5
9
|
|
6
10
|
class UnitTests < Assert::Context
|
7
11
|
desc "DatWorkerPool::WorkerPoolSpy"
|
8
12
|
setup do
|
9
|
-
@
|
10
|
-
|
13
|
+
@spy_class = DatWorkerPool::WorkerPoolSpy
|
14
|
+
end
|
15
|
+
subject{ @spy_class }
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class InitTests < UnitTests
|
20
|
+
desc "when init"
|
21
|
+
setup do
|
22
|
+
@worker_class = Class.new{ include DatWorkerPool::Worker }
|
23
|
+
@options = {
|
24
|
+
:num_workers => Factory.integer,
|
25
|
+
:logger => TEST_LOGGER,
|
26
|
+
:queue => DatWorkerPool::DefaultQueue.new,
|
27
|
+
:worker_params => { Factory.string => Factory.string }
|
28
|
+
}
|
29
|
+
|
30
|
+
@worker_pool_spy = @spy_class.new(@worker_class, @options)
|
11
31
|
end
|
12
32
|
subject{ @worker_pool_spy }
|
13
33
|
|
14
|
-
should have_readers :
|
34
|
+
should have_readers :logger, :queue
|
35
|
+
should have_readers :options, :num_workers, :worker_class, :worker_params
|
15
36
|
should have_readers :start_called, :shutdown_called, :shutdown_timeout
|
16
|
-
should
|
17
|
-
should
|
18
|
-
should
|
19
|
-
should
|
20
|
-
|
21
|
-
should
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
37
|
+
should have_accessors :available_worker_count, :worker_available
|
38
|
+
should have_imeths :start, :shutdown
|
39
|
+
should have_imeths :add_work, :push, :work_items
|
40
|
+
should have_imeths :worker_available?
|
41
|
+
|
42
|
+
should "know its attributes" do
|
43
|
+
assert_equal @worker_class, subject.worker_class
|
44
|
+
assert_equal @options, subject.options
|
45
|
+
assert_equal @options[:num_workers], subject.num_workers
|
46
|
+
assert_equal @options[:logger], subject.logger
|
47
|
+
assert_equal @options[:queue], subject.queue
|
48
|
+
assert_equal @options[:worker_params], subject.worker_params
|
49
|
+
assert_equal 0, subject.available_worker_count
|
50
|
+
|
51
|
+
assert_false subject.worker_available?
|
52
|
+
assert_false subject.start_called
|
53
|
+
assert_false subject.shutdown_called
|
54
|
+
assert_nil subject.shutdown_timeout
|
32
55
|
end
|
33
56
|
|
34
|
-
should "
|
35
|
-
|
57
|
+
should "default its attributes" do
|
58
|
+
worker_pool_spy = @spy_class.new(@worker_class)
|
59
|
+
assert_instance_of DatWorkerPool::DefaultQueue, worker_pool_spy.queue
|
60
|
+
assert_equal DatWorkerPool::DEFAULT_NUM_WORKERS, worker_pool_spy.num_workers
|
36
61
|
end
|
37
62
|
|
38
|
-
should "
|
39
|
-
|
40
|
-
|
63
|
+
should "allow setting its available worker count" do
|
64
|
+
integer = Factory.integer
|
65
|
+
subject.available_worker_count = integer
|
66
|
+
assert_equal integer, subject.available_worker_count
|
41
67
|
end
|
42
68
|
|
43
|
-
should "
|
44
|
-
|
69
|
+
should "allow setting whether a worker is available" do
|
70
|
+
subject.worker_available = true
|
71
|
+
assert_true subject.worker_available?
|
72
|
+
subject.worker_available = false
|
73
|
+
assert_false subject.worker_available?
|
45
74
|
end
|
46
75
|
|
47
|
-
should "
|
48
|
-
|
76
|
+
should "know if it's been started" do
|
77
|
+
assert_false subject.start_called
|
78
|
+
subject.start
|
79
|
+
assert_true subject.start_called
|
49
80
|
end
|
50
81
|
|
51
|
-
should "
|
52
|
-
|
82
|
+
should "start its queue when it's been started" do
|
83
|
+
assert_false subject.queue.running?
|
84
|
+
subject.start
|
85
|
+
assert_true subject.queue.running?
|
53
86
|
end
|
54
87
|
|
55
|
-
should "
|
56
|
-
subject.
|
57
|
-
|
58
|
-
|
88
|
+
should "know if it's been shutdown" do
|
89
|
+
assert_false subject.shutdown_called
|
90
|
+
subject.shutdown
|
91
|
+
assert_true subject.shutdown_called
|
92
|
+
assert_nil subject.shutdown_timeout
|
59
93
|
end
|
60
94
|
|
61
|
-
|
62
|
-
subject.add_work 'work'
|
63
|
-
assert_equal 1, subject.work_items.size
|
64
|
-
assert_includes 'work', subject.work_items
|
65
|
-
end
|
95
|
+
end
|
66
96
|
|
67
|
-
|
68
|
-
|
69
|
-
|
97
|
+
class StartedTests < InitTests
|
98
|
+
desc "and started"
|
99
|
+
setup do
|
100
|
+
@worker_pool_spy.start
|
70
101
|
end
|
71
102
|
|
72
|
-
should "
|
73
|
-
|
74
|
-
subject.
|
75
|
-
|
103
|
+
should "shutdown its queue when it's been shutdown" do
|
104
|
+
assert_false subject.queue.shutdown?
|
105
|
+
subject.shutdown
|
106
|
+
assert_true subject.queue.shutdown?
|
76
107
|
end
|
77
108
|
|
78
|
-
should "know
|
79
|
-
|
80
|
-
|
109
|
+
should "know if it's been shutdown with a timeout" do
|
110
|
+
timeout = Factory.integer
|
111
|
+
subject.shutdown(timeout)
|
112
|
+
assert_equal timeout, subject.shutdown_timeout
|
81
113
|
end
|
82
114
|
|
83
|
-
should "
|
84
|
-
|
85
|
-
|
86
|
-
assert_equal
|
115
|
+
should "allow adding work to its queue" do
|
116
|
+
work_item = Factory.string
|
117
|
+
subject.add_work(work_item)
|
118
|
+
assert_equal work_item, subject.queue.work_items.last
|
119
|
+
|
120
|
+
work_item = Factory.string
|
121
|
+
subject.push(work_item)
|
122
|
+
assert_equal work_item, subject.queue.work_items.last
|
87
123
|
end
|
88
124
|
|
89
|
-
should "allow
|
90
|
-
subject.
|
91
|
-
|
92
|
-
assert_nil subject.shutdown_timeout
|
125
|
+
should "not allow adding `nil` work" do
|
126
|
+
subject.add_work(nil)
|
127
|
+
assert_equal 0, subject.queue.work_items.size
|
93
128
|
end
|
94
129
|
|
95
|
-
should "know its
|
96
|
-
|
97
|
-
|
98
|
-
subject.on_queue_pop(&callback)
|
99
|
-
assert_equal [callback], subject.on_queue_pop_callbacks
|
100
|
-
|
101
|
-
assert_equal [], subject.on_queue_push_callbacks
|
102
|
-
callback = proc{ }
|
103
|
-
subject.on_queue_push(&callback)
|
104
|
-
assert_equal [callback], subject.on_queue_push_callbacks
|
105
|
-
|
106
|
-
assert_equal [], subject.on_worker_error_callbacks
|
107
|
-
callback = proc{ }
|
108
|
-
subject.on_worker_error(&callback)
|
109
|
-
assert_equal [callback], subject.on_worker_error_callbacks
|
110
|
-
|
111
|
-
assert_equal [], subject.on_worker_start_callbacks
|
112
|
-
callback = proc{ }
|
113
|
-
subject.on_worker_start(&callback)
|
114
|
-
assert_equal [callback], subject.on_worker_start_callbacks
|
115
|
-
|
116
|
-
assert_equal [], subject.on_worker_shutdown_callbacks
|
117
|
-
callback = proc{ }
|
118
|
-
subject.on_worker_shutdown(&callback)
|
119
|
-
assert_equal [callback], subject.on_worker_shutdown_callbacks
|
120
|
-
|
121
|
-
assert_equal [], subject.on_worker_sleep_callbacks
|
122
|
-
callback = proc{ }
|
123
|
-
subject.on_worker_sleep(&callback)
|
124
|
-
assert_equal [callback], subject.on_worker_sleep_callbacks
|
125
|
-
|
126
|
-
assert_equal [], subject.on_worker_wakeup_callbacks
|
127
|
-
callback = proc{ }
|
128
|
-
subject.on_worker_wakeup(&callback)
|
129
|
-
assert_equal [callback], subject.on_worker_wakeup_callbacks
|
130
|
-
|
131
|
-
assert_equal [], subject.before_work_callbacks
|
132
|
-
callback = proc{ }
|
133
|
-
subject.before_work(&callback)
|
134
|
-
assert_equal [callback], subject.before_work_callbacks
|
135
|
-
|
136
|
-
assert_equal [], subject.after_work_callbacks
|
137
|
-
callback = proc{ }
|
138
|
-
subject.after_work(&callback)
|
139
|
-
assert_equal [callback], subject.after_work_callbacks
|
130
|
+
should "know its queues work items" do
|
131
|
+
Factory.integer(3).times{ subject.queue.dwp_push(Factory.string) }
|
132
|
+
assert_equal subject.queue.work_items, subject.work_items
|
140
133
|
end
|
141
134
|
|
142
135
|
end
|
data/test/unit/worker_tests.rb
CHANGED
@@ -1,199 +1,865 @@
|
|
1
1
|
require 'assert'
|
2
2
|
require 'dat-worker-pool/worker'
|
3
3
|
|
4
|
-
require '
|
5
|
-
require 'dat-worker-pool/
|
4
|
+
require 'system_timer'
|
5
|
+
require 'dat-worker-pool/default_queue'
|
6
|
+
require 'dat-worker-pool/runner'
|
7
|
+
require 'test/support/thread_spies'
|
6
8
|
|
7
|
-
|
9
|
+
module DatWorkerPool::Worker
|
8
10
|
|
9
11
|
class UnitTests < Assert::Context
|
10
12
|
desc "DatWorkerPool::Worker"
|
11
13
|
setup do
|
12
|
-
@
|
13
|
-
|
14
|
-
@worker = DatWorkerPool::Worker.new(@queue).tap do |w|
|
15
|
-
w.on_work = proc{ |worker, work| @work_done << work }
|
14
|
+
@worker_class = Class.new do
|
15
|
+
include DatWorkerPool::Worker
|
16
16
|
end
|
17
17
|
end
|
18
|
+
subject{ @worker_class }
|
19
|
+
|
20
|
+
should have_imeths :on_start_callbacks, :on_shutdown_callbacks
|
21
|
+
should have_imeths :on_available_callbacks, :on_unavailable_callbacks
|
22
|
+
should have_imeths :on_error_callbacks
|
23
|
+
should have_imeths :before_work_callbacks, :after_work_callbacks
|
24
|
+
should have_imeths :on_start, :on_shutdown
|
25
|
+
should have_imeths :on_available, :on_unavailable
|
26
|
+
should have_imeths :on_error
|
27
|
+
should have_imeths :before_work, :after_work
|
28
|
+
should have_imeths :prepend_on_start, :prepend_on_shutdown
|
29
|
+
should have_imeths :prepend_on_available, :prepend_on_unavailable
|
30
|
+
should have_imeths :prepend_on_error
|
31
|
+
should have_imeths :prepend_before_work, :prepend_after_work
|
32
|
+
|
33
|
+
should "not have any callbacks by default" do
|
34
|
+
assert_equal [], subject.on_start_callbacks
|
35
|
+
assert_equal [], subject.on_shutdown_callbacks
|
36
|
+
assert_equal [], subject.on_available_callbacks
|
37
|
+
assert_equal [], subject.on_unavailable_callbacks
|
38
|
+
assert_equal [], subject.on_error_callbacks
|
39
|
+
assert_equal [], subject.before_work_callbacks
|
40
|
+
assert_equal [], subject.after_work_callbacks
|
41
|
+
end
|
42
|
+
|
43
|
+
should "allow appending callbacks" do
|
44
|
+
callback = proc{ Factory.string }
|
45
|
+
# add a callback to each type to show we are appending
|
46
|
+
subject.on_start_callbacks << proc{ Factory.string }
|
47
|
+
subject.on_shutdown_callbacks << proc{ Factory.string }
|
48
|
+
subject.on_available_callbacks << proc{ Factory.string }
|
49
|
+
subject.on_unavailable_callbacks << proc{ Factory.string }
|
50
|
+
subject.on_error_callbacks << proc{ Factory.string }
|
51
|
+
subject.before_work_callbacks << proc{ Factory.string }
|
52
|
+
subject.after_work_callbacks << proc{ Factory.string }
|
53
|
+
|
54
|
+
subject.on_start(&callback)
|
55
|
+
assert_equal callback, subject.on_start_callbacks.last
|
56
|
+
|
57
|
+
subject.on_shutdown(&callback)
|
58
|
+
assert_equal callback, subject.on_shutdown_callbacks.last
|
59
|
+
|
60
|
+
subject.on_available(&callback)
|
61
|
+
assert_equal callback, subject.on_available_callbacks.last
|
62
|
+
|
63
|
+
subject.on_unavailable(&callback)
|
64
|
+
assert_equal callback, subject.on_unavailable_callbacks.last
|
65
|
+
|
66
|
+
subject.on_error(&callback)
|
67
|
+
assert_equal callback, subject.on_error_callbacks.last
|
68
|
+
|
69
|
+
subject.before_work(&callback)
|
70
|
+
assert_equal callback, subject.before_work_callbacks.last
|
71
|
+
|
72
|
+
subject.after_work(&callback)
|
73
|
+
assert_equal callback, subject.after_work_callbacks.last
|
74
|
+
end
|
75
|
+
|
76
|
+
should "allow prepending callbacks" do
|
77
|
+
callback = proc{ Factory.string }
|
78
|
+
# add a callback to each type to show we are appending
|
79
|
+
subject.on_start_callbacks << proc{ Factory.string }
|
80
|
+
subject.on_shutdown_callbacks << proc{ Factory.string }
|
81
|
+
subject.on_available_callbacks << proc{ Factory.string }
|
82
|
+
subject.on_unavailable_callbacks << proc{ Factory.string }
|
83
|
+
subject.on_error_callbacks << proc{ Factory.string }
|
84
|
+
subject.before_work_callbacks << proc{ Factory.string }
|
85
|
+
subject.after_work_callbacks << proc{ Factory.string }
|
86
|
+
|
87
|
+
subject.prepend_on_start(&callback)
|
88
|
+
assert_equal callback, subject.on_start_callbacks.first
|
89
|
+
|
90
|
+
subject.prepend_on_shutdown(&callback)
|
91
|
+
assert_equal callback, subject.on_shutdown_callbacks.first
|
92
|
+
|
93
|
+
subject.prepend_on_available(&callback)
|
94
|
+
assert_equal callback, subject.on_available_callbacks.first
|
95
|
+
|
96
|
+
subject.prepend_on_unavailable(&callback)
|
97
|
+
assert_equal callback, subject.on_unavailable_callbacks.first
|
98
|
+
|
99
|
+
subject.prepend_on_error(&callback)
|
100
|
+
assert_equal callback, subject.on_error_callbacks.first
|
101
|
+
|
102
|
+
subject.prepend_before_work(&callback)
|
103
|
+
assert_equal callback, subject.before_work_callbacks.first
|
104
|
+
|
105
|
+
subject.prepend_after_work(&callback)
|
106
|
+
assert_equal callback, subject.after_work_callbacks.first
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
class InitTests < UnitTests
|
112
|
+
desc "when init"
|
113
|
+
setup do
|
114
|
+
@mutex = Mutex.new
|
115
|
+
@cond_var = ConditionVariable.new
|
116
|
+
|
117
|
+
@queue = DatWorkerPool::DefaultQueue.new.tap(&:dwp_start)
|
118
|
+
@runner = DatWorkerPool::Runner.new({
|
119
|
+
:logger => TEST_LOGGER,
|
120
|
+
:queue => @queue,
|
121
|
+
:worker_params => {
|
122
|
+
:mutex => @mutex,
|
123
|
+
:cond_var => @cond_var
|
124
|
+
}
|
125
|
+
})
|
126
|
+
@number = Factory.integer(10)
|
127
|
+
|
128
|
+
@thread_spy = ThreadSpy.new
|
129
|
+
Assert.stub(Thread, :new) do |&block|
|
130
|
+
@thread_spy.tap{ |s| s.block = block }
|
131
|
+
end
|
132
|
+
|
133
|
+
@available_worker = nil
|
134
|
+
Assert.stub(@runner, :make_worker_available){ |w| @available_worker = w }
|
135
|
+
@unavailable_worker = nil
|
136
|
+
Assert.stub(@runner, :make_worker_unavailable){ |w| @unavailable_worker = w }
|
137
|
+
|
138
|
+
@worker = TestWorker.new(@runner, @queue, @number)
|
139
|
+
end
|
18
140
|
teardown do
|
19
|
-
|
20
|
-
@queue.shutdown
|
21
|
-
@worker.join
|
141
|
+
shutdown_worker_queue_and_wait_for_thread_to_stop
|
22
142
|
end
|
23
143
|
subject{ @worker }
|
24
144
|
|
25
|
-
should
|
26
|
-
should
|
27
|
-
should
|
28
|
-
should
|
29
|
-
should have_imeths :start, :shutdown, :join, :raise, :running?
|
145
|
+
should have_readers :dwp_number
|
146
|
+
should have_imeths :dwp_start, :dwp_signal_shutdown
|
147
|
+
should have_imeths :dwp_running?, :dwp_shutdown?
|
148
|
+
should have_imeths :dwp_thread_alive?, :dwp_join, :dwp_raise
|
30
149
|
|
31
|
-
should "
|
32
|
-
|
33
|
-
assert_equal
|
34
|
-
assert_equal
|
35
|
-
assert_equal [], worker.on_shutdown_callbacks
|
36
|
-
assert_equal [], worker.on_sleep_callbacks
|
37
|
-
assert_equal [], worker.on_wakeup_callbacks
|
38
|
-
assert_equal [], worker.before_work_callbacks
|
39
|
-
assert_equal [], worker.after_work_callbacks
|
150
|
+
should "know its queue and params" do
|
151
|
+
assert_equal @number, subject.instance_eval{ number }
|
152
|
+
assert_equal @runner.worker_params, subject.instance_eval{ params }
|
153
|
+
assert_equal @queue, subject.instance_eval{ queue }
|
40
154
|
end
|
41
155
|
|
42
|
-
should "start a thread
|
43
|
-
thread =
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
156
|
+
should "start a thread when its started" do
|
157
|
+
thread = subject.dwp_start
|
158
|
+
wait_for_worker_to_be_available
|
159
|
+
|
160
|
+
assert_same @thread_spy, thread
|
161
|
+
assert_true thread.alive?
|
48
162
|
end
|
49
163
|
|
50
|
-
should "
|
51
|
-
subject.
|
52
|
-
|
53
|
-
|
54
|
-
@
|
55
|
-
subject.
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
should "
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
subject.
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
164
|
+
should "make itself available when started" do
|
165
|
+
subject.dwp_start
|
166
|
+
wait_for_worker_to_be_available
|
167
|
+
|
168
|
+
assert_same subject, @available_worker
|
169
|
+
assert_not_nil subject.first_on_available_call_order
|
170
|
+
assert_not_nil subject.second_on_available_call_order
|
171
|
+
end
|
172
|
+
|
173
|
+
should "know if its running and if its thread is alive or not" do
|
174
|
+
assert_false subject.dwp_running?
|
175
|
+
assert_false subject.dwp_thread_alive?
|
176
|
+
|
177
|
+
subject.dwp_start
|
178
|
+
wait_for_worker_to_be_available
|
179
|
+
|
180
|
+
assert_true subject.dwp_running?
|
181
|
+
assert_true subject.dwp_thread_alive?
|
182
|
+
|
183
|
+
subject.dwp_signal_shutdown
|
184
|
+
assert_false subject.dwp_running?
|
185
|
+
assert_true subject.dwp_thread_alive?
|
186
|
+
|
187
|
+
shutdown_worker_queue_and_wait_for_thread_to_stop
|
188
|
+
assert_false subject.dwp_running?
|
189
|
+
assert_false subject.dwp_thread_alive?
|
190
|
+
end
|
191
|
+
|
192
|
+
should "make itself unavailable when its thread stops" do
|
193
|
+
subject.dwp_start
|
194
|
+
wait_for_worker_to_be_available
|
195
|
+
|
196
|
+
shutdown_worker_queue_and_wait_for_thread_to_stop
|
197
|
+
assert_same subject, @unavailable_worker
|
198
|
+
assert_not_nil subject.first_on_unavailable_call_order
|
199
|
+
assert_not_nil subject.second_on_unavailable_call_order
|
200
|
+
end
|
201
|
+
|
202
|
+
should "allow joining and raising on its thread" do
|
203
|
+
subject.dwp_start
|
204
|
+
wait_for_worker_to_be_available
|
205
|
+
|
206
|
+
subject.dwp_join(0.1)
|
207
|
+
assert_equal 0.1, @thread_spy.join_seconds
|
208
|
+
assert_true @thread_spy.join_called
|
209
|
+
|
210
|
+
exception = Factory.exception
|
211
|
+
subject.dwp_raise(exception)
|
212
|
+
assert_equal exception, @thread_spy.raised_exception
|
213
|
+
end
|
214
|
+
|
215
|
+
should "not allow joining or raising if it hasn't been started" do
|
216
|
+
assert_nothing_raised{ subject.dwp_join(Factory.integer) }
|
217
|
+
assert_false @thread_spy.join_called
|
218
|
+
assert_nothing_raised{ subject.dwp_raise(Factory.exception) }
|
219
|
+
assert_nil @thread_spy.raised_exception
|
220
|
+
end
|
221
|
+
|
222
|
+
should "make itself available and unavailable while running" do
|
223
|
+
subject.dwp_start
|
224
|
+
wait_for_worker_to_be_available
|
225
|
+
|
226
|
+
@queue.dwp_push(Factory.string)
|
227
|
+
wait_for_worker_to_work_and_then_be_available
|
228
|
+
|
229
|
+
assert_same subject, @unavailable_worker
|
230
|
+
assert_same subject, @available_worker
|
231
|
+
end
|
232
|
+
|
233
|
+
should "call its on-available and on-unavailable callbacks while running" do
|
234
|
+
subject.dwp_start
|
235
|
+
wait_for_worker_to_be_available
|
236
|
+
|
237
|
+
@queue.dwp_push(Factory.string)
|
238
|
+
wait_for_worker_to_work_and_then_be_available
|
239
|
+
|
240
|
+
assert_not_nil subject.first_on_unavailable_call_order
|
241
|
+
assert_not_nil subject.second_on_unavailable_call_order
|
242
|
+
assert_not_nil subject.first_on_available_call_order
|
243
|
+
assert_not_nil subject.second_on_available_call_order
|
244
|
+
end
|
245
|
+
|
246
|
+
should "make itself available/unavailable and run callbacks if it errors" do
|
247
|
+
# these are the only errors that could interfere with it
|
248
|
+
error_method = [:on_unavailable_error, :work_error].choice
|
249
|
+
exception = Factory.exception
|
250
|
+
subject.send("#{error_method}=", exception)
|
251
|
+
|
252
|
+
subject.dwp_start
|
253
|
+
wait_for_worker_to_be_available
|
254
|
+
|
255
|
+
@queue.dwp_push(Factory.string)
|
256
|
+
wait_for_worker_to_error_and_then_be_available
|
257
|
+
|
258
|
+
assert_equal exception, subject.on_error_exception
|
259
|
+
assert_same subject, @unavailable_worker
|
260
|
+
assert_not_nil subject.first_on_unavailable_call_order
|
261
|
+
assert_same subject, @available_worker
|
262
|
+
assert_not_nil subject.first_on_available_call_order
|
263
|
+
end
|
264
|
+
|
265
|
+
should "call its `work` method on any pushed items while running" do
|
266
|
+
assert_nil subject.item_worked_on
|
267
|
+
subject.dwp_start
|
268
|
+
wait_for_worker_to_be_available
|
269
|
+
assert_nil subject.item_worked_on
|
270
|
+
|
271
|
+
work_item = Factory.string
|
272
|
+
@queue.dwp_push(work_item)
|
273
|
+
wait_for_worker_to_work_and_then_be_available
|
274
|
+
|
275
|
+
assert_same work_item, subject.before_work_item_worked_on
|
276
|
+
assert_same work_item, subject.item_worked_on
|
277
|
+
assert_same work_item, subject.after_work_item_worked_on
|
278
|
+
end
|
279
|
+
|
280
|
+
should "not call its `work` method if it pops a `nil` work item" do
|
281
|
+
subject.dwp_start
|
282
|
+
wait_for_worker_to_be_available
|
283
|
+
|
284
|
+
@queue.dwp_push(nil)
|
285
|
+
# we don't have any event to listen for because it should ignore `nil`
|
286
|
+
# work items
|
287
|
+
@thread_spy.join(JOIN_SECONDS)
|
288
|
+
assert_false subject.work_called
|
289
|
+
|
290
|
+
# when the queue is shutdown it returns `nil`, so we shouldn't call `work`
|
291
|
+
# when shutting down
|
292
|
+
shutdown_worker_queue_and_wait_for_thread_to_stop
|
293
|
+
assert_false subject.work_called
|
294
|
+
end
|
295
|
+
|
296
|
+
should "run its on-error callbacks if it errors while starting" do
|
297
|
+
exception = Factory.exception
|
298
|
+
error_method = [:on_start_error, :on_available_error].choice
|
299
|
+
subject.send("#{error_method}=", exception)
|
300
|
+
|
301
|
+
subject.dwp_start
|
302
|
+
wait_for_worker_thread_to_stop_and_rescue_if_expected_error(exception)
|
303
|
+
|
304
|
+
assert_equal exception, subject.on_error_exception
|
305
|
+
assert_nil subject.on_error_work_item
|
306
|
+
end
|
307
|
+
|
308
|
+
should "stop its thread if it errors while starting" do
|
309
|
+
exception = Factory.exception
|
310
|
+
error_method = [:on_start_error, :on_available_error].choice
|
311
|
+
subject.send("#{error_method}=", exception)
|
312
|
+
|
313
|
+
subject.dwp_start
|
314
|
+
wait_for_worker_thread_to_stop_and_rescue_if_expected_error(exception)
|
315
|
+
|
316
|
+
assert_false subject.dwp_thread_alive?
|
317
|
+
assert_false subject.dwp_running?
|
318
|
+
end
|
319
|
+
|
320
|
+
should "run its on-error callbacks if it errors while shutting down" do
|
321
|
+
exception = Factory.exception
|
322
|
+
error_method = [:on_shutdown_error, :on_unavailable_error].choice
|
323
|
+
subject.send("#{error_method}=", exception)
|
324
|
+
|
325
|
+
subject.dwp_start
|
326
|
+
shutdown_worker_and_queue
|
327
|
+
wait_for_worker_thread_to_stop_and_rescue_if_expected_error(exception)
|
328
|
+
|
329
|
+
assert_equal exception, subject.on_error_exception
|
330
|
+
assert_nil subject.on_error_work_item
|
331
|
+
end
|
332
|
+
|
333
|
+
should "not stop its thread when an error occurs while running" do
|
334
|
+
subject.dwp_start
|
335
|
+
wait_for_worker_to_be_available
|
336
|
+
|
337
|
+
exception = Factory.exception
|
338
|
+
setup_work_loop_to_raise_exception(exception)
|
339
|
+
|
340
|
+
@queue.dwp_push(Factory.string)
|
341
|
+
wait_for_worker_to_error_and_then_be_available
|
342
|
+
|
343
|
+
assert_true subject.dwp_thread_alive?
|
344
|
+
end
|
345
|
+
|
346
|
+
should "run its on-error callbacks if an error occurs while running" do
|
347
|
+
exception = Factory.exception
|
348
|
+
subject.dwp_start
|
349
|
+
wait_for_worker_to_be_available
|
350
|
+
|
351
|
+
setup_work_loop_to_raise_exception(exception)
|
352
|
+
work_item = Factory.string
|
353
|
+
@queue.dwp_push(work_item)
|
354
|
+
wait_for_worker_to_error_and_then_be_available
|
355
|
+
|
356
|
+
assert_equal exception, subject.on_error_exception
|
357
|
+
assert_equal work_item, subject.on_error_work_item
|
358
|
+
end
|
359
|
+
|
360
|
+
should "not stop its thread if an error occurs in an on-error callback" do
|
361
|
+
exception = Factory.exception
|
362
|
+
subject.dwp_start
|
363
|
+
wait_for_worker_to_be_available
|
364
|
+
|
365
|
+
setup_work_loop_to_raise_exception(Factory.exception)
|
366
|
+
subject.on_error_error = exception
|
367
|
+
work_item = Factory.string
|
368
|
+
@queue.dwp_push(work_item)
|
369
|
+
wait_for_worker_to_error_and_then_be_available
|
370
|
+
|
371
|
+
assert_true subject.dwp_thread_alive?
|
372
|
+
end
|
373
|
+
|
374
|
+
should "stop its thread when a shutdown error is raised while running" do
|
375
|
+
exception = Factory.exception(DatWorkerPool::ShutdownError)
|
376
|
+
subject.dwp_start
|
377
|
+
wait_for_worker_to_be_available
|
378
|
+
|
379
|
+
setup_work_loop_to_raise_exception(exception)
|
380
|
+
@queue.dwp_push(Factory.string)
|
381
|
+
|
382
|
+
wait_for_worker_thread_to_stop_and_rescue_if_expected_error(exception)
|
383
|
+
assert_false subject.dwp_thread_alive?
|
384
|
+
end
|
385
|
+
|
386
|
+
should "stop its thread when a shutdown error is raised in an on-error callback" do
|
387
|
+
exception = Factory.exception(DatWorkerPool::ShutdownError)
|
388
|
+
subject.dwp_start
|
389
|
+
wait_for_worker_to_be_available
|
390
|
+
|
391
|
+
setup_work_loop_to_raise_exception(Factory.exception)
|
392
|
+
subject.on_error_error = exception
|
393
|
+
@queue.dwp_push(Factory.string)
|
394
|
+
|
395
|
+
wait_for_worker_thread_to_stop_and_rescue_if_expected_error(exception)
|
396
|
+
assert_false subject.dwp_thread_alive?
|
397
|
+
end
|
398
|
+
|
399
|
+
should "only run its on-error callbacks when shutdown error is raised with a work item" do
|
400
|
+
exception = Factory.exception(DatWorkerPool::ShutdownError)
|
401
|
+
subject.dwp_start
|
402
|
+
wait_for_worker_to_be_available
|
403
|
+
|
404
|
+
error_method = ERROR_METHODS.reject{ |e| e == :on_available_error }.choice
|
405
|
+
subject.send("#{error_method}=", exception)
|
406
|
+
work_item = Factory.string
|
407
|
+
@queue.dwp_push(work_item)
|
408
|
+
wait_for_worker_thread_to_stop_and_rescue_if_expected_error(exception)
|
409
|
+
|
410
|
+
assert_equal exception, subject.on_error_exception
|
411
|
+
assert_equal work_item, subject.on_error_work_item
|
412
|
+
end
|
413
|
+
|
414
|
+
should "run callbacks when its started" do
|
415
|
+
assert_nil subject.first_on_start_call_order
|
416
|
+
assert_nil subject.second_on_start_call_order
|
417
|
+
assert_nil subject.first_on_available_call_order
|
418
|
+
assert_nil subject.second_on_available_call_order
|
419
|
+
|
420
|
+
subject.dwp_start
|
421
|
+
wait_for_worker_to_be_available
|
422
|
+
|
423
|
+
assert_equal 1, subject.first_on_start_call_order
|
424
|
+
assert_equal 2, subject.second_on_start_call_order
|
425
|
+
assert_equal 3, subject.first_on_available_call_order
|
426
|
+
assert_equal 4, subject.second_on_available_call_order
|
427
|
+
end
|
428
|
+
|
429
|
+
should "run its callbacks when work is pushed" do
|
430
|
+
subject.dwp_start
|
431
|
+
wait_for_worker_to_be_available
|
432
|
+
subject.reset_call_order
|
433
|
+
|
434
|
+
assert_nil subject.first_on_unavailable_call_order
|
435
|
+
assert_nil subject.second_on_unavailable_call_order
|
436
|
+
assert_nil subject.first_before_work_call_order
|
437
|
+
assert_nil subject.second_before_work_call_order
|
438
|
+
assert_nil subject.work_call_order
|
439
|
+
assert_nil subject.first_after_work_call_order
|
440
|
+
assert_nil subject.second_after_work_call_order
|
441
|
+
assert_nil subject.first_on_available_call_order
|
442
|
+
assert_nil subject.second_on_available_call_order
|
443
|
+
|
444
|
+
@queue.dwp_push(Factory.string)
|
445
|
+
wait_for_worker_to_work_and_then_be_available
|
446
|
+
|
447
|
+
assert_equal 1, subject.first_on_unavailable_call_order
|
448
|
+
assert_equal 2, subject.second_on_unavailable_call_order
|
449
|
+
assert_equal 3, subject.first_before_work_call_order
|
450
|
+
assert_equal 4, subject.second_before_work_call_order
|
451
|
+
assert_equal 5, subject.work_call_order
|
452
|
+
assert_equal 6, subject.first_after_work_call_order
|
453
|
+
assert_equal 7, subject.second_after_work_call_order
|
454
|
+
assert_equal 8, subject.first_on_available_call_order
|
455
|
+
assert_equal 9, subject.second_on_available_call_order
|
456
|
+
end
|
457
|
+
|
458
|
+
should "run callbacks when its shutdown" do
|
459
|
+
subject.dwp_start
|
460
|
+
wait_for_worker_to_be_available
|
461
|
+
subject.reset_call_order
|
462
|
+
|
463
|
+
assert_nil subject.first_on_unavailable_call_order
|
464
|
+
assert_nil subject.second_on_unavailable_call_order
|
465
|
+
assert_nil subject.first_on_shutdown_call_order
|
466
|
+
assert_nil subject.second_on_shutdown_call_order
|
467
|
+
|
468
|
+
shutdown_worker_queue_and_wait_for_thread_to_stop
|
469
|
+
|
470
|
+
assert_equal 1, subject.first_on_unavailable_call_order
|
471
|
+
assert_equal 2, subject.second_on_unavailable_call_order
|
472
|
+
assert_equal 3, subject.first_on_shutdown_call_order
|
473
|
+
assert_equal 4, subject.second_on_shutdown_call_order
|
474
|
+
end
|
475
|
+
|
476
|
+
should "run its callbacks when an error occurs while making itself unavailable" do
|
477
|
+
subject.dwp_start
|
478
|
+
wait_for_worker_to_be_available
|
479
|
+
subject.reset_call_order
|
480
|
+
|
481
|
+
subject.on_unavailable_error = Factory.exception
|
482
|
+
@queue.dwp_push(Factory.string)
|
483
|
+
wait_for_worker_to_error_and_then_be_available
|
484
|
+
|
485
|
+
assert_equal 1, subject.first_on_unavailable_call_order
|
486
|
+
assert_equal 2, subject.on_error_call_order
|
487
|
+
assert_equal 3, subject.first_on_available_call_order
|
488
|
+
assert_equal 4, subject.second_on_available_call_order
|
489
|
+
assert_nil subject.second_on_unavailable_call_order
|
490
|
+
assert_nil subject.first_before_work_call_order
|
491
|
+
assert_nil subject.second_before_work_call_order
|
492
|
+
assert_nil subject.work_call_order
|
493
|
+
assert_nil subject.first_after_work_call_order
|
494
|
+
assert_nil subject.second_after_work_call_order
|
495
|
+
end
|
496
|
+
|
497
|
+
should "run its callbacks when an error occurs while working" do
|
498
|
+
subject.dwp_start
|
499
|
+
wait_for_worker_to_be_available
|
500
|
+
subject.reset_call_order
|
501
|
+
|
502
|
+
subject.work_error = Factory.exception
|
503
|
+
@queue.dwp_push(Factory.string)
|
504
|
+
wait_for_worker_to_error_and_then_be_available
|
505
|
+
|
506
|
+
assert_equal 1, subject.first_on_unavailable_call_order
|
507
|
+
assert_equal 2, subject.second_on_unavailable_call_order
|
508
|
+
assert_equal 3, subject.first_before_work_call_order
|
509
|
+
assert_equal 4, subject.second_before_work_call_order
|
510
|
+
assert_equal 5, subject.on_error_call_order
|
511
|
+
assert_equal 6, subject.first_on_available_call_order
|
512
|
+
assert_equal 7, subject.second_on_available_call_order
|
513
|
+
assert_nil subject.work_call_order
|
514
|
+
assert_nil subject.first_after_work_call_order
|
515
|
+
assert_nil subject.second_after_work_call_order
|
516
|
+
end
|
517
|
+
|
518
|
+
should "run its callbacks when an error occurs while making itself available" do
|
519
|
+
subject.dwp_start
|
520
|
+
wait_for_worker_to_be_available
|
521
|
+
subject.reset_call_order
|
522
|
+
|
523
|
+
subject.on_available_error = Factory.exception
|
524
|
+
@queue.dwp_push(Factory.string)
|
525
|
+
wait_for_worker_to_error_and_then_be_available
|
526
|
+
|
527
|
+
assert_equal 1, subject.first_on_unavailable_call_order
|
528
|
+
assert_equal 2, subject.second_on_unavailable_call_order
|
529
|
+
assert_equal 3, subject.first_before_work_call_order
|
530
|
+
assert_equal 4, subject.second_before_work_call_order
|
531
|
+
assert_equal 5, subject.work_call_order
|
532
|
+
assert_equal 6, subject.first_after_work_call_order
|
533
|
+
assert_equal 7, subject.second_after_work_call_order
|
534
|
+
assert_equal 8, subject.first_on_available_call_order
|
535
|
+
assert_equal 9, subject.on_error_call_order
|
536
|
+
assert_nil subject.second_on_available_call_order
|
537
|
+
end
|
538
|
+
|
539
|
+
ERROR_METHODS = [
|
540
|
+
:on_available_error,
|
541
|
+
:on_unavailable_error,
|
542
|
+
:before_work_error,
|
543
|
+
:work_error,
|
544
|
+
:after_work_error
|
545
|
+
].freeze
|
546
|
+
def setup_work_loop_to_raise_exception(exception)
|
547
|
+
error_method = ERROR_METHODS.choice
|
548
|
+
@worker.send("#{error_method}=", exception)
|
549
|
+
error_method
|
550
|
+
end
|
551
|
+
|
552
|
+
# this could loop forever so ensure it doesn't by using a timeout; use
|
553
|
+
# timeout instead of system timer because system timer is paranoid about a
|
554
|
+
# deadlock even though its intended to prevent the deadlock because it times
|
555
|
+
# out the block
|
556
|
+
def wait_for_worker(&block)
|
557
|
+
Timeout.timeout(1) do
|
558
|
+
@mutex.synchronize{ @cond_var.wait(@mutex) } while !block.call
|
80
559
|
end
|
81
|
-
|
82
|
-
@queue.push 'a'
|
83
|
-
subject.join 0.1 # trigger the worker's thread to run
|
560
|
+
end
|
84
561
|
|
85
|
-
|
86
|
-
|
87
|
-
|
562
|
+
def wait_for_worker_to_be_available
|
563
|
+
wait_for_worker{ @worker.first_on_available_call_order }
|
564
|
+
end
|
565
|
+
|
566
|
+
def wait_for_worker_to_work_and_then_be_available
|
567
|
+
wait_for_worker do
|
568
|
+
@worker.work_called && @worker.first_on_available_call_order
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
def wait_for_worker_to_error_and_then_be_available
|
573
|
+
wait_for_worker do
|
574
|
+
@worker.on_error_exception && @worker.first_on_available_call_order
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
def shutdown_worker_queue_and_wait_for_thread_to_stop
|
579
|
+
shutdown_worker_and_queue
|
580
|
+
wait_for_worker_thread_to_stop
|
581
|
+
end
|
582
|
+
|
583
|
+
def shutdown_worker_and_queue
|
584
|
+
@worker.dwp_signal_shutdown
|
585
|
+
@queue.dwp_shutdown
|
586
|
+
end
|
587
|
+
|
588
|
+
def wait_for_worker_thread_to_stop
|
589
|
+
return unless @worker.dwp_thread_alive?
|
590
|
+
Timeout.timeout(1){ @worker.dwp_join }
|
591
|
+
end
|
592
|
+
|
593
|
+
# this is needed because errors will be re-raised when the thread is joined
|
594
|
+
# and in some cases we expect these errors because we are manually raising
|
595
|
+
# them, this checks if they are the expected exception and won't re-raise
|
596
|
+
# them; to check if they are the expected exception, we have to use the
|
597
|
+
# class and message because when the thread raises an error on join it is a
|
598
|
+
# different instance with a different backtrace (so we can't use `==`)
|
599
|
+
def wait_for_worker_thread_to_stop_and_rescue_if_expected_error(exception)
|
600
|
+
begin
|
601
|
+
wait_for_worker_thread_to_stop
|
602
|
+
rescue exception.class => caught_exception
|
603
|
+
unless caught_exception.class == exception.class &&
|
604
|
+
caught_exception.message == exception.message
|
605
|
+
raise(caught_exception)
|
606
|
+
end
|
607
|
+
end
|
88
608
|
end
|
89
609
|
|
90
610
|
end
|
91
611
|
|
92
|
-
class
|
93
|
-
desc "
|
612
|
+
class TestHelperTests < UnitTests
|
613
|
+
desc "TestHelpers"
|
94
614
|
setup do
|
95
|
-
@
|
96
|
-
@
|
97
|
-
|
98
|
-
@
|
99
|
-
@
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
@
|
109
|
-
|
110
|
-
w.on_error_callbacks << proc do |*args|
|
111
|
-
@on_error_called_with = args
|
112
|
-
end
|
113
|
-
w.on_start_callbacks << proc do |*args|
|
114
|
-
@on_start_called_with = args
|
115
|
-
@on_start_called_at = (@call_counter += 1)
|
116
|
-
end
|
117
|
-
w.on_shutdown_callbacks << proc do |*args|
|
118
|
-
@on_shutdown_called_with = args
|
119
|
-
@on_shutdown_called_at = (@call_counter += 1)
|
120
|
-
end
|
121
|
-
w.on_sleep_callbacks << proc do |*args|
|
122
|
-
@on_sleep_called_with = args
|
123
|
-
@on_sleep_called_at = (@call_counter += 1)
|
124
|
-
end
|
125
|
-
w.on_wakeup_callbacks << proc do |*args|
|
126
|
-
@on_wakeup_called_with = args
|
127
|
-
@on_wakeup_called_at = (@call_counter += 1)
|
128
|
-
end
|
129
|
-
w.before_work_callbacks << proc do |*args|
|
130
|
-
@before_work_called_with = args
|
131
|
-
@before_work_called_at = (@call_counter += 1)
|
132
|
-
end
|
133
|
-
w.after_work_callbacks << proc do |*args|
|
134
|
-
@after_work_called_with = args
|
135
|
-
@after_work_called_at = (@call_counter += 1)
|
136
|
-
end
|
615
|
+
@mutex = Mutex.new
|
616
|
+
@cond_var = ConditionVariable.new
|
617
|
+
|
618
|
+
@worker_class = TestWorker
|
619
|
+
@options = {
|
620
|
+
:logger => TEST_LOGGER || Logger.new("/dev/null"),
|
621
|
+
:queue => DatWorkerPool::DefaultQueue.new,
|
622
|
+
:params => {
|
623
|
+
:mutex => @mutex,
|
624
|
+
:cond_var => @cond_var
|
625
|
+
}
|
626
|
+
}
|
627
|
+
|
628
|
+
@context_class = Class.new do
|
629
|
+
include DatWorkerPool::Worker::TestHelpers
|
137
630
|
end
|
631
|
+
@context = @context_class.new
|
138
632
|
end
|
633
|
+
subject{ @context }
|
139
634
|
|
140
|
-
should
|
141
|
-
subject.start
|
142
|
-
@queue.push('work')
|
143
|
-
subject.shutdown
|
144
|
-
@queue.shutdown
|
635
|
+
should have_imeths :test_runner
|
145
636
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
637
|
+
should "build a test runner using `test_runner`" do
|
638
|
+
test_runner = subject.test_runner(@worker_class, @options)
|
639
|
+
|
640
|
+
assert_instance_of TestHelpers::TestRunner, test_runner
|
641
|
+
assert_equal @worker_class, test_runner.worker_class
|
642
|
+
assert_equal @options[:queue], test_runner.queue
|
643
|
+
assert_equal @options[:params], test_runner.dwp_runner.worker_params
|
150
644
|
end
|
151
645
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
646
|
+
end
|
647
|
+
|
648
|
+
class TestRunnerTests < TestHelperTests
|
649
|
+
desc "TestRunner"
|
650
|
+
setup do
|
651
|
+
@test_runner = TestHelpers::TestRunner.new(@worker_class, @options)
|
652
|
+
|
653
|
+
dwp_runner = @test_runner.dwp_runner
|
654
|
+
@unavailable_worker = nil
|
655
|
+
Assert.stub(dwp_runner, :make_worker_unavailable){ |w| @unavailable_worker = w }
|
656
|
+
@available_worker = nil
|
657
|
+
Assert.stub(dwp_runner, :make_worker_available){ |w| @available_worker = w }
|
658
|
+
end
|
659
|
+
subject{ @test_runner }
|
660
|
+
|
661
|
+
should have_readers :worker_class, :worker
|
662
|
+
should have_readers :queue, :dwp_runner
|
663
|
+
should have_imeths :run, :work, :error
|
664
|
+
should have_imeths :start, :shutdown
|
665
|
+
should have_imeths :make_unavailable, :make_available
|
666
|
+
|
667
|
+
should "know its attributes" do
|
668
|
+
assert_equal @worker_class, subject.worker_class
|
669
|
+
assert_equal @options[:queue], subject.queue
|
670
|
+
end
|
157
671
|
|
158
|
-
|
159
|
-
|
672
|
+
should "build a dat-worker-pool runner" do
|
673
|
+
dwp_runner = subject.dwp_runner
|
674
|
+
assert_instance_of DatWorkerPool::Runner, dwp_runner
|
675
|
+
assert_equal DatWorkerPool::MIN_WORKERS, dwp_runner.num_workers
|
676
|
+
assert_equal subject.queue, dwp_runner.queue
|
677
|
+
assert_equal @options[:logger], dwp_runner.logger_proxy.logger
|
678
|
+
assert_equal subject.worker_class, dwp_runner.worker_class
|
679
|
+
assert_equal @options[:params], dwp_runner.worker_params
|
160
680
|
end
|
161
681
|
|
162
|
-
should "
|
682
|
+
should "build a worker" do
|
683
|
+
assert_instance_of @worker_class, subject.worker
|
684
|
+
assert_equal 1, subject.worker.dwp_number
|
685
|
+
end
|
686
|
+
|
687
|
+
should "run a workers life-cycle using `run`" do
|
688
|
+
work_item = Factory.string
|
689
|
+
subject.run(work_item)
|
690
|
+
|
691
|
+
worker = subject.worker
|
692
|
+
assert_not_nil worker.first_on_start_call_order
|
693
|
+
assert_same worker, @unavailable_worker
|
694
|
+
assert_not_nil worker.first_on_unavailable_call_order
|
695
|
+
assert_equal work_item, worker.before_work_item_worked_on
|
696
|
+
assert_equal work_item, worker.item_worked_on
|
697
|
+
assert_equal work_item, worker.after_work_item_worked_on
|
698
|
+
assert_same worker, @available_worker
|
699
|
+
assert_not_nil worker.first_on_available_call_order
|
700
|
+
assert_not_nil worker.first_on_shutdown_call_order
|
701
|
+
end
|
702
|
+
|
703
|
+
should "call its workers work method using `work`" do
|
704
|
+
work_item = Factory.string
|
705
|
+
subject.work(work_item)
|
706
|
+
|
707
|
+
worker = subject.worker
|
708
|
+
assert_equal work_item, worker.before_work_item_worked_on
|
709
|
+
assert_equal work_item, worker.item_worked_on
|
710
|
+
assert_equal work_item, worker.after_work_item_worked_on
|
711
|
+
end
|
712
|
+
|
713
|
+
should "call its workers on-error callbacks using `error`" do
|
714
|
+
exception = Factory.exception
|
715
|
+
subject.error(exception)
|
716
|
+
|
717
|
+
worker = subject.worker
|
718
|
+
assert_equal exception, worker.on_error_exception
|
719
|
+
assert_nil worker.on_error_work_item
|
720
|
+
|
721
|
+
work_item = Factory.string
|
722
|
+
subject.error(exception, work_item)
|
723
|
+
assert_equal work_item, worker.on_error_work_item
|
724
|
+
end
|
725
|
+
|
726
|
+
should "call its workers on-start callbacks using `start`" do
|
163
727
|
subject.start
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
assert_equal 4, @before_work_called_at
|
169
|
-
assert_equal 5, @after_work_called_at
|
170
|
-
assert_equal 6, @on_sleep_called_at
|
728
|
+
assert_not_nil subject.worker.first_on_start_call_order
|
729
|
+
end
|
730
|
+
|
731
|
+
should "call its workers on-shutdown callbacks using `shutdown`" do
|
171
732
|
subject.shutdown
|
172
|
-
|
173
|
-
assert_equal 7, @on_wakeup_called_at
|
174
|
-
assert_equal 8, @on_shutdown_called_at
|
733
|
+
assert_not_nil subject.worker.first_on_shutdown_call_order
|
175
734
|
end
|
176
735
|
|
177
|
-
should "call its
|
178
|
-
|
179
|
-
subject.
|
180
|
-
|
181
|
-
@queue.push('work')
|
182
|
-
assert_equal [subject, exception, 'work'], @on_error_called_with
|
183
|
-
assert_true thread.alive?
|
736
|
+
should "call its workers make unavailable method using `make_unavailable`" do
|
737
|
+
subject.make_unavailable
|
738
|
+
assert_same subject.worker, @unavailable_worker
|
739
|
+
assert_not_nil subject.worker.first_on_unavailable_call_order
|
184
740
|
end
|
185
741
|
|
186
|
-
should "call its
|
187
|
-
|
188
|
-
subject.
|
189
|
-
|
190
|
-
@queue.push('work')
|
191
|
-
assert_equal [subject, exception, 'work'], @on_error_called_with
|
192
|
-
assert_false thread.alive?
|
193
|
-
# ensure the shutdown error is handled and isn't thrown when we join
|
194
|
-
assert_nothing_raised{ thread.join }
|
742
|
+
should "call its workers make available method using `make_available`" do
|
743
|
+
subject.make_available
|
744
|
+
assert_same subject.worker, @available_worker
|
745
|
+
assert_not_nil subject.worker.first_on_available_call_order
|
195
746
|
end
|
196
747
|
|
197
748
|
end
|
198
749
|
|
750
|
+
class TestWorker
|
751
|
+
include DatWorkerPool::Worker
|
752
|
+
|
753
|
+
attr_reader :first_on_start_call_order, :second_on_start_call_order
|
754
|
+
attr_reader :first_on_shutdown_call_order, :second_on_shutdown_call_order
|
755
|
+
attr_reader :first_on_available_call_order, :second_on_available_call_order
|
756
|
+
attr_reader :first_on_unavailable_call_order, :second_on_unavailable_call_order
|
757
|
+
attr_reader :first_before_work_call_order, :second_before_work_call_order
|
758
|
+
attr_reader :first_after_work_call_order, :second_after_work_call_order
|
759
|
+
attr_reader :work_call_order, :on_error_call_order
|
760
|
+
|
761
|
+
attr_reader :before_work_item_worked_on, :after_work_item_worked_on
|
762
|
+
attr_reader :item_worked_on
|
763
|
+
|
764
|
+
attr_accessor :on_start_error, :on_shutdown_error
|
765
|
+
attr_accessor :on_available_error, :on_unavailable_error
|
766
|
+
attr_accessor :before_work_error, :after_work_error
|
767
|
+
attr_accessor :work_error, :on_error_error
|
768
|
+
|
769
|
+
attr_reader :on_error_exception, :on_error_work_item
|
770
|
+
|
771
|
+
on_start{ @first_on_start_call_order = next_call_order }
|
772
|
+
on_start do
|
773
|
+
raise_error_if_set(:on_start)
|
774
|
+
@second_on_start_call_order = next_call_order
|
775
|
+
end
|
776
|
+
|
777
|
+
on_shutdown{ @first_on_shutdown_call_order = next_call_order }
|
778
|
+
on_shutdown do
|
779
|
+
raise_error_if_set(:on_shutdown)
|
780
|
+
@second_on_shutdown_call_order = next_call_order
|
781
|
+
end
|
782
|
+
|
783
|
+
on_available{ @first_on_available_call_order = next_call_order }
|
784
|
+
on_available{ signal_test_suite_thread }
|
785
|
+
on_available do
|
786
|
+
raise_error_if_set(:on_available)
|
787
|
+
@second_on_available_call_order = next_call_order
|
788
|
+
end
|
789
|
+
|
790
|
+
on_unavailable{ @first_on_unavailable_call_order = next_call_order }
|
791
|
+
on_unavailable do
|
792
|
+
raise_error_if_set(:on_unavailable)
|
793
|
+
@second_on_unavailable_call_order = next_call_order
|
794
|
+
end
|
795
|
+
|
796
|
+
before_work{ |work_item| @first_before_work_call_order = next_call_order }
|
797
|
+
before_work do |work_item|
|
798
|
+
raise_error_if_set(:before_work)
|
799
|
+
@before_work_item_worked_on = work_item
|
800
|
+
@second_before_work_call_order = next_call_order
|
801
|
+
end
|
802
|
+
|
803
|
+
after_work{ |work_item| @first_after_work_call_order = next_call_order }
|
804
|
+
after_work do |work_item|
|
805
|
+
raise_error_if_set(:after_work)
|
806
|
+
@after_work_item_worked_on = work_item
|
807
|
+
@second_after_work_call_order = next_call_order
|
808
|
+
end
|
809
|
+
|
810
|
+
on_error do |exception, work_item|
|
811
|
+
@on_error_exception = exception
|
812
|
+
@on_error_work_item = work_item
|
813
|
+
end
|
814
|
+
on_error{ signal_test_suite_thread }
|
815
|
+
on_error do
|
816
|
+
raise_error_if_set(:on_error)
|
817
|
+
@on_error_call_order = next_call_order
|
818
|
+
end
|
819
|
+
|
820
|
+
|
821
|
+
def work_called; !!@work_called; end
|
822
|
+
|
823
|
+
def reset_call_order
|
824
|
+
@order = 0
|
825
|
+
@first_on_start_call_order = nil
|
826
|
+
@second_on_start_call_order = nil
|
827
|
+
@first_on_shutdown_call_order = nil
|
828
|
+
@second_on_shutdown_call_order = nil
|
829
|
+
@first_on_available_call_order = nil
|
830
|
+
@second_on_available_call_order = nil
|
831
|
+
@first_on_unavailable_call_order = nil
|
832
|
+
@second_on_unavailable_call_order = nil
|
833
|
+
@first_before_work_call_order = nil
|
834
|
+
@second_before_work_call_order = nil
|
835
|
+
@first_after_work_call_order = nil
|
836
|
+
@second_after_work_call_order = nil
|
837
|
+
@work_call_order = nil
|
838
|
+
end
|
839
|
+
|
840
|
+
private
|
841
|
+
|
842
|
+
def work!(work_item)
|
843
|
+
raise_error_if_set(:work)
|
844
|
+
@work_called = true
|
845
|
+
@item_worked_on = work_item
|
846
|
+
@work_call_order = next_call_order
|
847
|
+
end
|
848
|
+
|
849
|
+
def next_call_order; @order = (@order || 0) + 1; end
|
850
|
+
|
851
|
+
# we want to unset the error method if its set to avoid the thread looping
|
852
|
+
# very quickly because it doesn't available on `queue.pop`
|
853
|
+
def raise_error_if_set(type)
|
854
|
+
if (error = self.send("#{type}_error"))
|
855
|
+
self.send("#{type}_error=", nil)
|
856
|
+
Thread.current.raise error
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
def signal_test_suite_thread
|
861
|
+
params[:mutex].synchronize{ params[:cond_var].signal }
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
199
865
|
end
|