concurrent-ruby 0.2.2 → 0.3.0.pre.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 +4 -4
- data/README.md +45 -42
- data/lib/concurrent.rb +5 -6
- data/lib/concurrent/agent.rb +29 -33
- data/lib/concurrent/cached_thread_pool.rb +26 -105
- data/lib/concurrent/channel.rb +94 -0
- data/lib/concurrent/event.rb +8 -17
- data/lib/concurrent/executor.rb +68 -72
- data/lib/concurrent/fixed_thread_pool.rb +15 -83
- data/lib/concurrent/functions.rb +7 -22
- data/lib/concurrent/future.rb +29 -9
- data/lib/concurrent/null_thread_pool.rb +5 -2
- data/lib/concurrent/obligation.rb +6 -16
- data/lib/concurrent/promise.rb +9 -10
- data/lib/concurrent/runnable.rb +103 -0
- data/lib/concurrent/supervisor.rb +271 -44
- data/lib/concurrent/thread_pool.rb +112 -39
- data/lib/concurrent/version.rb +1 -1
- data/md/executor.md +9 -3
- data/md/goroutine.md +11 -9
- data/md/reactor.md +32 -0
- data/md/supervisor.md +43 -0
- data/spec/concurrent/agent_spec.rb +128 -51
- data/spec/concurrent/cached_thread_pool_spec.rb +33 -47
- data/spec/concurrent/channel_spec.rb +446 -0
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +3 -1
- data/spec/concurrent/event_spec.rb +0 -19
- data/spec/concurrent/executor_spec.rb +167 -119
- data/spec/concurrent/fixed_thread_pool_spec.rb +40 -30
- data/spec/concurrent/functions_spec.rb +0 -20
- data/spec/concurrent/future_spec.rb +88 -0
- data/spec/concurrent/null_thread_pool_spec.rb +23 -2
- data/spec/concurrent/obligation_shared.rb +0 -5
- data/spec/concurrent/promise_spec.rb +9 -10
- data/spec/concurrent/runnable_shared.rb +62 -0
- data/spec/concurrent/runnable_spec.rb +233 -0
- data/spec/concurrent/supervisor_spec.rb +912 -47
- data/spec/concurrent/thread_pool_shared.rb +18 -31
- data/spec/spec_helper.rb +10 -3
- metadata +17 -23
- data/lib/concurrent/defer.rb +0 -65
- data/lib/concurrent/reactor.rb +0 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +0 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +0 -131
- data/lib/concurrent/utilities.rb +0 -32
- data/md/defer.md +0 -174
- data/spec/concurrent/defer_spec.rb +0 -199
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +0 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +0 -410
- data/spec/concurrent/reactor_spec.rb +0 -364
- data/spec/concurrent/utilities_spec.rb +0 -74
@@ -167,26 +167,6 @@ module Concurrent
|
|
167
167
|
end
|
168
168
|
end
|
169
169
|
|
170
|
-
describe Defer do
|
171
|
-
|
172
|
-
before(:each) do
|
173
|
-
Defer.thread_pool = FixedThreadPool.new(1)
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'aliases Kernel#defer' do
|
177
|
-
defer{ nil }.should be_a(Defer)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
describe Executor do
|
182
|
-
|
183
|
-
it 'aliases Kernel#executor' do
|
184
|
-
ex = executor('executor'){ nil }
|
185
|
-
ex.should be_a(Executor::ExecutionContext)
|
186
|
-
ex.kill
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
170
|
describe Future do
|
191
171
|
|
192
172
|
before(:each) do
|
@@ -108,5 +108,93 @@ module Concurrent
|
|
108
108
|
end
|
109
109
|
end
|
110
110
|
end
|
111
|
+
|
112
|
+
context 'observation' do
|
113
|
+
|
114
|
+
let(:clazz) do
|
115
|
+
Class.new do
|
116
|
+
attr_reader :value
|
117
|
+
attr_reader :reason
|
118
|
+
attr_reader :count
|
119
|
+
define_method(:update) do |time, value, reason|
|
120
|
+
@count = @count.to_i + 1
|
121
|
+
@value = value
|
122
|
+
@reason = reason
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:observer) { clazz.new }
|
128
|
+
|
129
|
+
it 'notifies all observers on fulfillment' do
|
130
|
+
future = Future.new{ sleep(0.1); 42 }
|
131
|
+
future.add_observer(observer)
|
132
|
+
future.value.should == 42
|
133
|
+
future.reason.should be_nil
|
134
|
+
sleep(0.1)
|
135
|
+
observer.value.should == 42
|
136
|
+
observer.reason.should be_nil
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'notifies all observers on rejection' do
|
140
|
+
future = Future.new{ sleep(0.1); raise StandardError }
|
141
|
+
future.add_observer(observer)
|
142
|
+
future.value.should be_nil
|
143
|
+
future.reason.should be_a(StandardError)
|
144
|
+
sleep(0.1)
|
145
|
+
observer.value.should be_nil
|
146
|
+
observer.reason.should be_a(StandardError)
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'notifies an observer added after fulfillment' do
|
150
|
+
future = Future.new{ 42 }
|
151
|
+
sleep(0.1)
|
152
|
+
future.value.should == 42
|
153
|
+
future.add_observer(observer)
|
154
|
+
observer.value.should be_nil
|
155
|
+
sleep(0.1)
|
156
|
+
observer.value.should == 42
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'notifies an observer added after rejection' do
|
160
|
+
future = Future.new{ raise StandardError }
|
161
|
+
sleep(0.1)
|
162
|
+
future.reason.should be_a(StandardError)
|
163
|
+
future.add_observer(observer)
|
164
|
+
observer.value.should be_nil
|
165
|
+
sleep(0.1)
|
166
|
+
observer.reason.should be_a(StandardError)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'does not notify existing observers when a new observer added after fulfillment' do
|
170
|
+
future = Future.new{ 42 }
|
171
|
+
future.add_observer(observer)
|
172
|
+
sleep(0.1)
|
173
|
+
future.value.should == 42
|
174
|
+
observer.count.should == 1
|
175
|
+
|
176
|
+
o2 = clazz.new
|
177
|
+
future.add_observer(o2)
|
178
|
+
sleep(0.1)
|
179
|
+
|
180
|
+
observer.count.should == 1
|
181
|
+
o2.value.should == 42
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'does not notify existing observers when a new observer added after rejection' do
|
185
|
+
future = Future.new{ raise StandardError }
|
186
|
+
future.add_observer(observer)
|
187
|
+
sleep(0.1)
|
188
|
+
future.reason.should be_a(StandardError)
|
189
|
+
observer.count.should == 1
|
190
|
+
|
191
|
+
o2 = clazz.new
|
192
|
+
future.add_observer(o2)
|
193
|
+
sleep(0.1)
|
194
|
+
|
195
|
+
observer.count.should == 1
|
196
|
+
o2.reason.should be_a(StandardError)
|
197
|
+
end
|
198
|
+
end
|
111
199
|
end
|
112
200
|
end
|
@@ -14,20 +14,41 @@ module Concurrent
|
|
14
14
|
|
15
15
|
context '#post' do
|
16
16
|
|
17
|
-
it '
|
17
|
+
it 'creates a new thread for a call without arguments' do
|
18
18
|
thread = Thread.new{ nil }
|
19
19
|
Thread.should_receive(:new).with(no_args()).and_return(thread)
|
20
20
|
$GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
|
21
21
|
subject.post{ nil }
|
22
22
|
end
|
23
23
|
|
24
|
-
it '
|
24
|
+
it 'executes a call without arguments' do
|
25
|
+
@expected = false
|
26
|
+
subject.post{ @expected = true }
|
27
|
+
sleep(0.1)
|
28
|
+
@expected.should be_true
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'creates a new thread for a call with arguments' do
|
25
32
|
thread = Thread.new{ nil }
|
26
33
|
Thread.should_receive(:new).with(1,2,3).and_return(thread)
|
27
34
|
$GLOBAL_THREAD_POOL.should_not_receive(:post).with(any_args())
|
28
35
|
subject.post(1,2,3){ nil }
|
29
36
|
end
|
30
37
|
|
38
|
+
it 'executes a call with one argument' do
|
39
|
+
@expected = 0
|
40
|
+
subject.post(1){|one| @expected = one }
|
41
|
+
sleep(0.1)
|
42
|
+
@expected.should == 1
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'executes a call with multiple arguments' do
|
46
|
+
@expected = nil
|
47
|
+
subject.post(1,2,3,4,5){|*args| @expected = args }
|
48
|
+
sleep(0.1)
|
49
|
+
@expected.should eq [1,2,3,4,5]
|
50
|
+
end
|
51
|
+
|
31
52
|
it 'aliases #<<' do
|
32
53
|
thread = Thread.new{ nil }
|
33
54
|
Thread.should_receive(:new).with(no_args()).and_return(thread)
|
@@ -27,14 +27,12 @@ share_examples_for Concurrent::Obligation do
|
|
27
27
|
|
28
28
|
it 'blocks the caller when :pending and timeout is nil' do
|
29
29
|
f = pending_subject
|
30
|
-
sleep(0.1)
|
31
30
|
f.value.should be_true
|
32
31
|
f.should be_fulfilled
|
33
32
|
end
|
34
33
|
|
35
34
|
it 'returns nil when reaching the optional timeout value' do
|
36
35
|
f = pending_subject
|
37
|
-
sleep(0.1)
|
38
36
|
f.value(0).should be_nil
|
39
37
|
f.should be_pending
|
40
38
|
end
|
@@ -42,21 +40,18 @@ share_examples_for Concurrent::Obligation do
|
|
42
40
|
it 'returns immediately when timeout is zero' do
|
43
41
|
Timeout.should_not_receive(:timeout).with(any_args())
|
44
42
|
f = pending_subject
|
45
|
-
sleep(0.1)
|
46
43
|
f.value(0).should be_nil
|
47
44
|
f.should be_pending
|
48
45
|
end
|
49
46
|
|
50
47
|
it 'returns the value when fulfilled before timeout' do
|
51
48
|
f = pending_subject
|
52
|
-
sleep(0.1)
|
53
49
|
f.value(10).should be_true
|
54
50
|
f.should be_fulfilled
|
55
51
|
end
|
56
52
|
|
57
53
|
it 'returns nil when timeout reached' do
|
58
54
|
f = pending_subject
|
59
|
-
sleep(0.1)
|
60
55
|
f.value(0.1).should be_nil
|
61
56
|
f.should be_pending
|
62
57
|
end
|
@@ -156,7 +156,7 @@ module Concurrent
|
|
156
156
|
it 'sets the promise reason the error object on exception' do
|
157
157
|
p = Promise.new{ raise StandardError.new('Boom!') }
|
158
158
|
sleep(0.1)
|
159
|
-
p.reason.should be_a(
|
159
|
+
p.reason.should be_a(StandardError)
|
160
160
|
p.reason.should.to_s =~ /Boom!/
|
161
161
|
end
|
162
162
|
|
@@ -207,7 +207,7 @@ module Concurrent
|
|
207
207
|
Promise.new{ raise ArgumentError }.
|
208
208
|
rescue(ArgumentError){|ex| @expected = 1 }.
|
209
209
|
rescue(LoadError){|ex| @expected = 2 }.
|
210
|
-
rescue(
|
210
|
+
rescue(StandardError){|ex| @expected = 3 }
|
211
211
|
sleep(0.1)
|
212
212
|
@expected.should eq 1
|
213
213
|
|
@@ -215,7 +215,7 @@ module Concurrent
|
|
215
215
|
Promise.new{ raise LoadError }.
|
216
216
|
rescue(ArgumentError){|ex| @expected = 1 }.
|
217
217
|
rescue(LoadError){|ex| @expected = 2 }.
|
218
|
-
rescue(
|
218
|
+
rescue(StandardError){|ex| @expected = 3 }
|
219
219
|
sleep(0.1)
|
220
220
|
@expected.should eq 2
|
221
221
|
|
@@ -223,7 +223,7 @@ module Concurrent
|
|
223
223
|
Promise.new{ raise StandardError }.
|
224
224
|
rescue(ArgumentError){|ex| @expected = 1 }.
|
225
225
|
rescue(LoadError){|ex| @expected = 2 }.
|
226
|
-
rescue(
|
226
|
+
rescue(StandardError){|ex| @expected = 3 }
|
227
227
|
sleep(0.1)
|
228
228
|
@expected.should eq 3
|
229
229
|
end
|
@@ -233,7 +233,7 @@ module Concurrent
|
|
233
233
|
Promise.new{ raise StandardError }.
|
234
234
|
rescue(ArgumentError){|ex| @expected = ex }.
|
235
235
|
rescue(LoadError){|ex| @expected = ex }.
|
236
|
-
rescue(
|
236
|
+
rescue(StandardError){|ex| @expected = ex }
|
237
237
|
sleep(0.1)
|
238
238
|
@expected.should be_a(StandardError)
|
239
239
|
end
|
@@ -242,8 +242,7 @@ module Concurrent
|
|
242
242
|
@expected = nil
|
243
243
|
Promise.new{ raise StandardError }.
|
244
244
|
rescue(StandardError).
|
245
|
-
rescue(StandardError){|ex| @expected = ex }
|
246
|
-
rescue(Exception){|ex| @expected = ex }
|
245
|
+
rescue(StandardError){|ex| @expected = ex }
|
247
246
|
sleep(0.1)
|
248
247
|
@expected.should be_a(StandardError)
|
249
248
|
end
|
@@ -252,8 +251,8 @@ module Concurrent
|
|
252
251
|
lambda {
|
253
252
|
Promise.new{ raise StandardError }.
|
254
253
|
rescue(ArgumentError){|ex| @expected = ex }.
|
255
|
-
rescue(
|
256
|
-
rescue(
|
254
|
+
rescue(NotImplementedError){|ex| @expected = ex }.
|
255
|
+
rescue(NoMethodError){|ex| @expected = ex }
|
257
256
|
sleep(0.1)
|
258
257
|
}.should_not raise_error
|
259
258
|
end
|
@@ -261,7 +260,7 @@ module Concurrent
|
|
261
260
|
it 'supresses exceptions thrown from rescue handlers' do
|
262
261
|
lambda {
|
263
262
|
Promise.new{ raise ArgumentError }.
|
264
|
-
rescue(
|
263
|
+
rescue(StandardError){ raise StandardError }
|
265
264
|
sleep(0.1)
|
266
265
|
}.should_not raise_error
|
267
266
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
share_examples_for :runnable do
|
4
|
+
|
5
|
+
after(:each) do
|
6
|
+
subject.stop
|
7
|
+
@thread.kill unless @thread.nil?
|
8
|
+
sleep(0.1)
|
9
|
+
end
|
10
|
+
|
11
|
+
context '#run' do
|
12
|
+
|
13
|
+
it 'starts the (blocking) runner on the current thread when stopped' do
|
14
|
+
@thread = Thread.new { subject.run }
|
15
|
+
@thread.join(1).should be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'raises an exception when already running' do
|
19
|
+
@thread = Thread.new { subject.run }
|
20
|
+
@thread.join(0.1)
|
21
|
+
expect {
|
22
|
+
subject.run
|
23
|
+
}.to raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns true when stopped normally' do
|
27
|
+
@expected = false
|
28
|
+
@thread = Thread.new { @expected = subject.run }
|
29
|
+
@thread.join(0.1)
|
30
|
+
subject.stop
|
31
|
+
@thread.join(1)
|
32
|
+
@expected.should be_true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context '#stop' do
|
37
|
+
|
38
|
+
it 'returns true when not running' do
|
39
|
+
subject.stop.should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'returns true when successfully stopped' do
|
43
|
+
@thread = Thread.new { subject.run }
|
44
|
+
@thread.join(0.1)
|
45
|
+
subject.stop.should be_true
|
46
|
+
subject.should_not be_running
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context '#running?' do
|
51
|
+
|
52
|
+
it 'returns true when running' do
|
53
|
+
@thread = Thread.new { subject.run }
|
54
|
+
@thread.join(0.1)
|
55
|
+
subject.should be_running
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'returns false when not running' do
|
59
|
+
subject.should_not be_running
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'runnable_shared'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
describe Runnable do
|
7
|
+
|
8
|
+
let(:runnable) do
|
9
|
+
Class.new {
|
10
|
+
include Runnable
|
11
|
+
attr_reader :thread
|
12
|
+
def initialize(*args, &block)
|
13
|
+
yield if block_given?
|
14
|
+
end
|
15
|
+
def on_task
|
16
|
+
@thread = Thread.current
|
17
|
+
sleep(0.1)
|
18
|
+
end
|
19
|
+
def on_run() return true; end
|
20
|
+
def after_run() return true; end
|
21
|
+
def on_stop() return true; end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
subject { runnable.new }
|
26
|
+
|
27
|
+
it_should_behave_like :runnable
|
28
|
+
|
29
|
+
after(:each) do
|
30
|
+
subject.stop
|
31
|
+
@thread.kill unless @thread.nil?
|
32
|
+
sleep(0.1)
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#run' do
|
36
|
+
|
37
|
+
it 'calls #on_run when implemented' do
|
38
|
+
subject.should_receive(:on_run).with(no_args())
|
39
|
+
@thread = Thread.new { subject.run }
|
40
|
+
sleep(0.1)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'does not attempt to call #on_run when not implemented' do
|
44
|
+
subject.class.send(:remove_method, :on_run)
|
45
|
+
@thread = Thread.new do
|
46
|
+
expect {
|
47
|
+
subject.run
|
48
|
+
}.not_to raise_error
|
49
|
+
end
|
50
|
+
sleep(0.1)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'return false when #on_run raises an exception' do
|
54
|
+
@expected = true
|
55
|
+
subject.stub(:on_run).and_raise(StandardError)
|
56
|
+
@thread = Thread.new do
|
57
|
+
@expected = subject.run
|
58
|
+
end
|
59
|
+
sleep(0.1)
|
60
|
+
@expected.should be_false
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'calls #on_task in an infinite loop' do
|
64
|
+
subject.should_receive(:on_task).with(no_args()).at_least(1)
|
65
|
+
@thread = Thread.new { subject.run }
|
66
|
+
@thread.join(1)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'raises an exception if the #on_task callback is not implemented' do
|
70
|
+
runner = Class.new { include Runnable }.new
|
71
|
+
expect {
|
72
|
+
runner.run
|
73
|
+
}.to raise_error(Runnable::LifecycleError)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns false when the task loop raises an exception' do
|
77
|
+
@expected = false
|
78
|
+
subject.stub(:on_task).and_raise(StandardError)
|
79
|
+
@thread = Thread.new { @expected = subject.run }
|
80
|
+
@thread.join(0.1)
|
81
|
+
@expected.should be_false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context '#stop' do
|
86
|
+
|
87
|
+
it 'calls #on_stop when implemented' do
|
88
|
+
subject.should_receive(:on_stop).with(no_args())
|
89
|
+
@thread = Thread.new { subject.run }
|
90
|
+
sleep(0.1)
|
91
|
+
subject.stop
|
92
|
+
sleep(0.1)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'does not attempt to call #on_stop when not implemented' do
|
96
|
+
subject.class.send(:remove_method, :on_stop)
|
97
|
+
@thread = Thread.new { subject.run }
|
98
|
+
sleep(0.1)
|
99
|
+
expect {
|
100
|
+
subject.stop
|
101
|
+
sleep(0.1)
|
102
|
+
}.not_to raise_error
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'return false when #on_stop raises an exception' do
|
106
|
+
subject.stub(:on_stop).and_raise(StandardError)
|
107
|
+
@thread = Thread.new { subject.run }
|
108
|
+
sleep(0.1)
|
109
|
+
subject.stop.should be_false
|
110
|
+
subject.should_not be_running
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'calls #after_run when implemented' do
|
114
|
+
subject.should_receive(:after_run).with(no_args())
|
115
|
+
@thread = Thread.new { subject.run }
|
116
|
+
sleep(0.1)
|
117
|
+
subject.stop
|
118
|
+
sleep(0.2)
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'does not attempt to call #after_run when not implemented' do
|
122
|
+
subject.class.send(:remove_method, :after_run)
|
123
|
+
@thread = Thread.new { subject.run }
|
124
|
+
sleep(0.1)
|
125
|
+
expect {
|
126
|
+
subject.stop
|
127
|
+
sleep(0.2)
|
128
|
+
}.not_to raise_error
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context '#running?' do
|
133
|
+
|
134
|
+
it 'returns false if runner abends' do
|
135
|
+
subject.stub(:on_task).and_raise(StandardError)
|
136
|
+
@thread = Thread.new { subject.run }
|
137
|
+
@thread.join(0.1)
|
138
|
+
subject.should_not be_running
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'instance #run!' do
|
143
|
+
|
144
|
+
let(:clazz) do
|
145
|
+
Class.new { include Runnable }
|
146
|
+
end
|
147
|
+
|
148
|
+
subject { clazz.new }
|
149
|
+
|
150
|
+
after(:each) do
|
151
|
+
@context.runner.stop if @context && @context.runner
|
152
|
+
@context.thread.kill if @context && @context.thread
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'creates a new thread' do
|
156
|
+
Thread.should_receive(:new).with(any_args()).and_return(nil)
|
157
|
+
@context = subject.run!
|
158
|
+
sleep(0.1)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'runs the runner on the new thread' do
|
162
|
+
@context = subject.run!
|
163
|
+
sleep(0.1)
|
164
|
+
@context.thread.should_not eq Thread.current
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'returns a context object on success' do
|
168
|
+
@context = subject.run!
|
169
|
+
sleep(0.1)
|
170
|
+
@context.should be_a(Running::Context)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'module #run!' do
|
175
|
+
|
176
|
+
let(:clazz) do
|
177
|
+
Class.new { include Runnable }
|
178
|
+
end
|
179
|
+
|
180
|
+
after(:each) do
|
181
|
+
@context.runner.stop if @context && @context.runner
|
182
|
+
@context.thread.kill if @context && @context.thread
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'creates a new runner' do
|
186
|
+
clazz.should_receive(:new).once.with(no_args())
|
187
|
+
@context = clazz.run!
|
188
|
+
sleep(0.1)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'passes all args to the runner constructor' do
|
192
|
+
args = [1, 2, :three, :four]
|
193
|
+
clazz.should_receive(:new).once.with(*args)
|
194
|
+
@context = clazz.run!(*args)
|
195
|
+
sleep(0.1)
|
196
|
+
end
|
197
|
+
|
198
|
+
it 'passes a block argument to the runner constructor' do
|
199
|
+
@expected = false
|
200
|
+
@context = runnable.run!{ @expected = true }
|
201
|
+
sleep(0.1)
|
202
|
+
@expected.should be_true
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'creates a new thread' do
|
206
|
+
Thread.should_receive(:new).with(any_args()).and_return(nil)
|
207
|
+
@context = runnable.run!
|
208
|
+
sleep(0.1)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'runs the runner on the new thread' do
|
212
|
+
@context = runnable.run!
|
213
|
+
sleep(0.1)
|
214
|
+
@context.runner.thread.should_not eq Thread.current
|
215
|
+
@context.runner.thread.should eq @context.thread
|
216
|
+
@context.thread.should_not eq Thread.current
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'returns a context object on success' do
|
220
|
+
@context = runnable.run!
|
221
|
+
sleep(0.1)
|
222
|
+
@context.should be_a(Running::Context)
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'returns nil on failure' do
|
226
|
+
Thread.stub(:new).with(any_args()).and_raise(StandardError)
|
227
|
+
@context = runnable.run!
|
228
|
+
sleep(0.1)
|
229
|
+
@context.should be_nil
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|