concurrent-ruby 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Defer do
6
+
7
+ context '#initialize' do
8
+
9
+ before(:each) do
10
+ $GLOBAL_THREAD_POOL = FixedThreadPool.new(1)
11
+ end
12
+
13
+ it 'raises an exception if no block or operation given' do
14
+ lambda {
15
+ Defer.new
16
+ }.should raise_error(ArgumentError)
17
+ end
18
+
19
+ it 'raises an exception if both a block and an operation given' do
20
+ lambda {
21
+ operation = proc{ nil }
22
+ Defer.new(operation, nil, nil){ nil }
23
+ }.should raise_error(ArgumentError)
24
+ end
25
+
26
+ it 'starts the thread if an operation is given' do
27
+ $GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
28
+ operation = proc{ nil }
29
+ Defer.new(operation, nil, nil)
30
+ end
31
+
32
+ it 'does not start the thread if neither a callback or errorback is given' do
33
+ $GLOBAL_THREAD_POOL.should_not_receive(:post)
34
+ Defer.new(nil, nil, nil){ nil }
35
+ end
36
+
37
+ it 'aliases Kernel#defer' do
38
+ defer{ nil }.should be_a(Defer)
39
+ end
40
+ end
41
+
42
+ context '#then' do
43
+
44
+ it 'raises an exception if no block given' do
45
+ lambda {
46
+ Defer.new{ nil }.then
47
+ }.should raise_error(ArgumentError)
48
+ end
49
+
50
+ it 'raises an exception if called twice' do
51
+ lambda {
52
+ Defer.new{ nil }.then{|result| nil }.then{|result| nil }
53
+ }.should raise_error(IllegalMethodCallError)
54
+ end
55
+
56
+ it 'raises an exception if an operation was provided at construction' do
57
+ lambda {
58
+ operation = proc{ nil }
59
+ Defer.new(operation, nil, nil).then{|result| nil }
60
+ }.should raise_error(IllegalMethodCallError)
61
+ end
62
+
63
+ it 'raises an exception if a callback was provided at construction' do
64
+ lambda {
65
+ callback = proc{|result|nil }
66
+ Defer.new(nil, callback, nil){ nil }.then{|result| nil }
67
+ }.should raise_error(IllegalMethodCallError)
68
+ end
69
+
70
+ it 'returns self' do
71
+ deferred = Defer.new{ nil }
72
+ deferred.then{|result| nil }.should eq deferred
73
+ end
74
+ end
75
+
76
+ context '#rescue' do
77
+
78
+ it 'raises an exception if no block given' do
79
+ lambda {
80
+ Defer.new{ nil }.rescue
81
+ }.should raise_error(ArgumentError)
82
+ end
83
+
84
+ it 'raises an exception if called twice' do
85
+ lambda {
86
+ Defer.new{ nil }.rescue{ nil }.rescue{ nil }
87
+ }.should raise_error(IllegalMethodCallError)
88
+ end
89
+
90
+ it 'raises an exception if an operation was provided at construction' do
91
+ lambda {
92
+ operation = proc{ nil }
93
+ Defer.new(operation, nil, nil).rescue{|ex| nil }
94
+ }.should raise_error(IllegalMethodCallError)
95
+ end
96
+
97
+ it 'raises an exception if an errorback was provided at construction' do
98
+ lambda {
99
+ errorback = proc{|ex| nil }
100
+ Defer.new(nil, nil, errorback){ nil }.rescue{|ex| nil }
101
+ }.should raise_error(IllegalMethodCallError)
102
+ end
103
+
104
+ it 'returns self' do
105
+ deferred = Defer.new{ nil }
106
+ deferred.rescue{|ex| nil }.should eq deferred
107
+ end
108
+
109
+ it 'aliases #catch' do
110
+ lambda {
111
+ Defer.new{ nil }.catch{|ex| nil }
112
+ }.should_not raise_error
113
+ end
114
+
115
+ it 'aliases #on_error' do
116
+ lambda {
117
+ Defer.new{ nil }.on_error{|ex| nil }
118
+ }.should_not raise_error
119
+ end
120
+ end
121
+
122
+ context '#go' do
123
+
124
+ it 'starts the thread if not started' do
125
+ deferred = Defer.new{ nil }
126
+ $GLOBAL_THREAD_POOL.should_receive(:post).once.with(any_args())
127
+ deferred.go
128
+ end
129
+
130
+ it 'does nothing if called more than once' do
131
+ deferred = Defer.new{ nil }
132
+ deferred.go
133
+ $GLOBAL_THREAD_POOL.should_not_receive(:post)
134
+ deferred.go
135
+ end
136
+
137
+ it 'does nothing if thread started at construction' do
138
+ operation = proc{ nil }
139
+ callback = proc{|result| nil }
140
+ errorback = proc{|ex| nil }
141
+ deferred = Defer.new(operation, callback, errorback)
142
+ $GLOBAL_THREAD_POOL.should_not_receive(:post)
143
+ deferred.go
144
+ end
145
+ end
146
+
147
+ context 'fulfillment' do
148
+
149
+ it 'runs the operation' do
150
+ @expected = false
151
+ Defer.new{ @expected = true }.go
152
+ sleep(0.1)
153
+ @expected.should be_true
154
+ end
155
+
156
+ it 'calls the callback when the operation is successful' do
157
+ @expected = false
158
+ Defer.new{ true }.then{|result| @expected = true }.go
159
+ sleep(0.1)
160
+ @expected.should be_true
161
+ end
162
+
163
+ it 'passes the result of the block to the callback' do
164
+ @expected = false
165
+ Defer.new{ 'w00t' }.then{|result| @expected = result }.go
166
+ sleep(0.1)
167
+ @expected.should eq 'w00t'
168
+ end
169
+
170
+ it 'does not call the errorback when the operation is successful' do
171
+ @expected = true
172
+ Defer.new{ nil }.rescue{|ex| @expected = false }.go
173
+ sleep(0.1)
174
+ @expected.should be_true
175
+ end
176
+
177
+ it 'calls the errorback if the operation throws an exception' do
178
+ @expected = false
179
+ Defer.new{ raise StandardError }.rescue{|ex| @expected = true }.go
180
+ sleep(0.1)
181
+ @expected.should be_true
182
+ end
183
+
184
+ it 'passes the exception object to the errorback' do
185
+ @expected = nil
186
+ Defer.new{ raise StandardError }.rescue{|ex| @expected = ex }.go
187
+ sleep(0.1)
188
+ @expected.should be_a(StandardError)
189
+ end
190
+
191
+ it 'does not call the callback when the operation fails' do
192
+ @expected = true
193
+ Defer.new{ raise StandardError }.then{|result| @expected = false }.go
194
+ sleep(0.1)
195
+ @expected.should be_true
196
+ end
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,246 @@
1
+ require 'spec_helper'
2
+
3
+ require 'concurrent/agent'
4
+ require 'concurrent/future'
5
+ require 'concurrent/promise'
6
+
7
+ module Concurrent
8
+
9
+ describe EventMachineDeferProxy do
10
+
11
+ subject { EventMachineDeferProxy.new }
12
+
13
+ context '#post' do
14
+
15
+ it 'proxies a call without arguments' do
16
+ @expected = false
17
+ EventMachine.run do
18
+ subject.post{ @expected = true }
19
+ sleep(0.1)
20
+ EventMachine.stop
21
+ end
22
+ @expected.should eq true
23
+ end
24
+
25
+ it 'proxies a call with arguments' do
26
+ @expected = []
27
+ EventMachine.run do
28
+ subject.post(1,2,3){|*args| @expected = args }
29
+ sleep(0.1)
30
+ EventMachine.stop
31
+ end
32
+ @expected.should eq [1,2,3]
33
+ end
34
+
35
+ it 'aliases #<<' do
36
+ @expected = false
37
+ EventMachine.run do
38
+ subject << proc{ @expected = true }
39
+ sleep(0.1)
40
+ EventMachine.stop
41
+ end
42
+ @expected.should eq true
43
+ end
44
+ end
45
+
46
+ context 'operation' do
47
+
48
+ context 'goroutine' do
49
+
50
+ it 'passes all arguments to the block' do
51
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
52
+
53
+ EventMachine.run do
54
+
55
+ @expected = nil
56
+ go(1, 2, 3){|a, b, c| @expected = [c, b, a] }
57
+ sleep(0.1)
58
+ @expected.should eq [3, 2, 1]
59
+
60
+ EventMachine.stop
61
+ end
62
+ end
63
+ end
64
+
65
+ context Agent do
66
+
67
+ subject { Agent.new(0) }
68
+
69
+ it 'supports fulfillment' do
70
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
71
+
72
+ EventMachine.run do
73
+
74
+ @expected = []
75
+ subject.post{ @expected << 1 }
76
+ subject.post{ @expected << 2 }
77
+ subject.post{ @expected << 3 }
78
+ sleep(0.1)
79
+ @expected.should eq [1,2,3]
80
+
81
+ EventMachine.stop
82
+ end
83
+ end
84
+
85
+ it 'supports validation' do
86
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
87
+
88
+ EventMachine.run do
89
+
90
+ @expected = nil
91
+ subject.validate{ @expected = 10; true }
92
+ subject.post{ nil }
93
+ sleep(0.1)
94
+ @expected.should eq 10
95
+
96
+ EventMachine.stop
97
+ end
98
+ end
99
+
100
+ it 'supports rejection' do
101
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
102
+
103
+ EventMachine.run do
104
+
105
+ @expected = nil
106
+ subject.
107
+ on_error(StandardError){|ex| @expected = 1 }.
108
+ on_error(StandardError){|ex| @expected = 2 }.
109
+ on_error(StandardError){|ex| @expected = 3 }
110
+ subject.post{ raise StandardError }
111
+ sleep(0.1)
112
+ @expected.should eq 1
113
+
114
+ EventMachine.stop
115
+ end
116
+ end
117
+ end
118
+
119
+ context Future do
120
+
121
+ it 'supports fulfillment' do
122
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
123
+
124
+ EventMachine.run do
125
+
126
+ @a = @b = @c = nil
127
+ f = Future.new(1, 2, 3) do |a, b, c|
128
+ @a, @b, @c = a, b, c
129
+ end
130
+ sleep(0.1)
131
+ [@a, @b, @c].should eq [1, 2, 3]
132
+
133
+ sleep(0.1)
134
+ EventMachine.stop
135
+ end
136
+ end
137
+ end
138
+
139
+ context Promise do
140
+
141
+ context 'fulfillment' do
142
+
143
+ it 'passes all arguments to the first promise in the chain' do
144
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
145
+
146
+ EventMachine.run do
147
+
148
+ @a = @b = @c = nil
149
+ p = Promise.new(1, 2, 3) do |a, b, c|
150
+ @a, @b, @c = a, b, c
151
+ end
152
+ sleep(0.1)
153
+ [@a, @b, @c].should eq [1, 2, 3]
154
+
155
+ sleep(0.1)
156
+ EventMachine.stop
157
+ end
158
+ end
159
+
160
+ it 'passes the result of each block to all its children' do
161
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
162
+
163
+ EventMachine.run do
164
+ @expected = nil
165
+ Promise.new(10){|a| a * 2 }.then{|result| @expected = result}
166
+ sleep(0.1)
167
+ @expected.should eq 20
168
+
169
+ sleep(0.1)
170
+ EventMachine.stop
171
+ end
172
+ end
173
+
174
+ it 'sets the promise value to the result if its block' do
175
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
176
+
177
+ EventMachine.run do
178
+
179
+ p = Promise.new(10){|a| a * 2 }.then{|result| result * 2}
180
+ sleep(0.1)
181
+ p.value.should eq 40
182
+
183
+ sleep(0.1)
184
+ EventMachine.stop
185
+ end
186
+ end
187
+ end
188
+
189
+ context 'rejection' do
190
+
191
+ it 'sets the promise reason and error on exception' do
192
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
193
+
194
+ EventMachine.run do
195
+
196
+ p = Promise.new{ raise StandardError.new('Boom!') }
197
+ sleep(0.1)
198
+ p.reason.should be_a(Exception)
199
+ p.reason.should.to_s =~ /Boom!/
200
+ p.should be_rejected
201
+
202
+ sleep(0.1)
203
+ EventMachine.stop
204
+ end
205
+ end
206
+
207
+ it 'calls the first exception block with a matching class' do
208
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
209
+
210
+ EventMachine.run do
211
+
212
+ @expected = nil
213
+ Promise.new{ raise StandardError }.
214
+ on_error(StandardError){|ex| @expected = 1 }.
215
+ on_error(StandardError){|ex| @expected = 2 }.
216
+ on_error(StandardError){|ex| @expected = 3 }
217
+ sleep(0.1)
218
+ @expected.should eq 1
219
+
220
+ sleep(0.1)
221
+ EventMachine.stop
222
+ end
223
+ end
224
+
225
+ it 'passes the exception object to the matched block' do
226
+ $GLOBAL_THREAD_POOL = EventMachineDeferProxy.new
227
+
228
+ EventMachine.run do
229
+
230
+ @expected = nil
231
+ Promise.new{ raise StandardError }.
232
+ on_error(ArgumentError){|ex| @expected = ex }.
233
+ on_error(LoadError){|ex| @expected = ex }.
234
+ on_error(Exception){|ex| @expected = ex }
235
+ sleep(0.1)
236
+ @expected.should be_a(StandardError)
237
+
238
+ sleep(0.1)
239
+ EventMachine.stop
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+
3
+ module Concurrent
4
+
5
+ describe Event do
6
+
7
+ subject{ Event.new }
8
+
9
+ context '#initialize' do
10
+
11
+ it 'sets the state to unset' do
12
+ subject.should_not be_set
13
+ end
14
+ end
15
+
16
+ context '#set?' do
17
+
18
+ it 'returns true when the event has been set' do
19
+ subject.set
20
+ subject.should be_set
21
+ end
22
+
23
+ it 'returns false if the event is unset' do
24
+ subject.reset
25
+ subject.should_not be_set
26
+ end
27
+ end
28
+
29
+ context '#set' do
30
+
31
+ it 'triggers the event' do
32
+ subject.reset
33
+ @expected = false
34
+ Thread.new{ subject.wait; @expected = true }
35
+ sleep(0.1)
36
+ subject.set
37
+ sleep(0.1)
38
+ @expected.should be_true
39
+ end
40
+
41
+ it 'sets the state to set' do
42
+ subject.set
43
+ subject.should be_set
44
+ end
45
+ end
46
+
47
+ context '#reset' do
48
+
49
+ it 'sets the state to unset' do
50
+ subject.set
51
+ subject.should be_set
52
+ subject.reset
53
+ subject.should_not be_set
54
+ end
55
+ end
56
+
57
+ context '#pulse' do
58
+
59
+ it 'triggers the event' do
60
+ subject.reset
61
+ @expected = false
62
+ Thread.new{ subject.wait; @expected = true }
63
+ sleep(0.1)
64
+ subject.pulse
65
+ sleep(0.1)
66
+ @expected.should be_true
67
+ end
68
+
69
+ it 'sets the state to unset' do
70
+ subject.pulse
71
+ sleep(0.1)
72
+ subject.should_not be_set
73
+ end
74
+ end
75
+
76
+ context '#wait' do
77
+
78
+ it 'returns immediately when the event has been set' do
79
+ subject.reset
80
+ @expected = false
81
+ subject.set
82
+ Thread.new{ subject.wait(1000); @expected = true}
83
+ sleep(1)
84
+ @expected.should be_true
85
+ end
86
+
87
+ it 'returns true once the event is set' do
88
+ subject.set
89
+ subject.wait.should be_true
90
+ end
91
+
92
+ it 'blocks indefinitely when the timer is nil' do
93
+ subject.reset
94
+ @expected = false
95
+ Thread.new{ subject.wait; @expected = true}
96
+ subject.set
97
+ sleep(1)
98
+ @expected.should be_true
99
+ end
100
+
101
+ it 'stops waiting when the timer expires' do
102
+ subject.reset
103
+ @expected = false
104
+ Thread.new{ subject.wait(0.5); @expected = true}
105
+ sleep(1)
106
+ @expected.should be_true
107
+ end
108
+
109
+ it 'returns false when the timer expires' do
110
+ subject.reset
111
+ subject.wait(1).should be_false
112
+ end
113
+
114
+ it 'triggers multiple waiting threads' do
115
+ subject.reset
116
+ @expected = []
117
+ 5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
118
+ subject.set
119
+ sleep(1)
120
+ @expected.length.should eq 5
121
+ end
122
+
123
+ it 'behaves appropriately if wait begins while #set is processing' do
124
+ subject.reset
125
+ @expected = []
126
+ 5.times{ Thread.new{ subject.wait(5) } }
127
+ subject.set
128
+ 5.times{ Thread.new{ subject.wait; @expected << Thread.current.object_id } }
129
+ sleep(1)
130
+ @expected.length.should eq 5
131
+ end
132
+ end
133
+ end
134
+ end