concurrent-ruby 0.0.1 → 0.1.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.
@@ -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