concurrent-ruby 0.2.0 → 0.2.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.
Files changed (57) hide show
  1. data/LICENSE +21 -21
  2. data/README.md +275 -275
  3. data/lib/concurrent.rb +28 -28
  4. data/lib/concurrent/agent.rb +114 -114
  5. data/lib/concurrent/cached_thread_pool.rb +131 -129
  6. data/lib/concurrent/defer.rb +65 -65
  7. data/lib/concurrent/event.rb +60 -60
  8. data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
  9. data/lib/concurrent/executor.rb +96 -95
  10. data/lib/concurrent/fixed_thread_pool.rb +99 -95
  11. data/lib/concurrent/functions.rb +120 -120
  12. data/lib/concurrent/future.rb +42 -42
  13. data/lib/concurrent/global_thread_pool.rb +16 -16
  14. data/lib/concurrent/goroutine.rb +29 -29
  15. data/lib/concurrent/null_thread_pool.rb +22 -22
  16. data/lib/concurrent/obligation.rb +67 -67
  17. data/lib/concurrent/promise.rb +174 -174
  18. data/lib/concurrent/reactor.rb +166 -166
  19. data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
  20. data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
  21. data/lib/concurrent/supervisor.rb +105 -100
  22. data/lib/concurrent/thread_pool.rb +76 -76
  23. data/lib/concurrent/utilities.rb +32 -32
  24. data/lib/concurrent/version.rb +3 -3
  25. data/lib/concurrent_ruby.rb +1 -1
  26. data/md/agent.md +123 -123
  27. data/md/defer.md +174 -174
  28. data/md/event.md +32 -32
  29. data/md/executor.md +187 -187
  30. data/md/future.md +83 -83
  31. data/md/goroutine.md +52 -52
  32. data/md/obligation.md +32 -32
  33. data/md/promise.md +227 -227
  34. data/md/thread_pool.md +224 -224
  35. data/spec/concurrent/agent_spec.rb +386 -386
  36. data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
  37. data/spec/concurrent/defer_spec.rb +195 -195
  38. data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
  39. data/spec/concurrent/event_spec.rb +134 -134
  40. data/spec/concurrent/executor_spec.rb +200 -200
  41. data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
  42. data/spec/concurrent/functions_spec.rb +217 -217
  43. data/spec/concurrent/future_spec.rb +108 -108
  44. data/spec/concurrent/global_thread_pool_spec.rb +38 -38
  45. data/spec/concurrent/goroutine_spec.rb +67 -67
  46. data/spec/concurrent/null_thread_pool_spec.rb +57 -54
  47. data/spec/concurrent/obligation_shared.rb +132 -132
  48. data/spec/concurrent/promise_spec.rb +312 -312
  49. data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
  50. data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
  51. data/spec/concurrent/reactor_spec.rb +364 -364
  52. data/spec/concurrent/supervisor_spec.rb +269 -258
  53. data/spec/concurrent/thread_pool_shared.rb +204 -204
  54. data/spec/concurrent/utilities_spec.rb +74 -74
  55. data/spec/spec_helper.rb +32 -32
  56. metadata +20 -16
  57. checksums.yaml +0 -7
@@ -1,196 +1,196 @@
1
- require 'spec_helper'
2
- require 'faker'
3
-
4
- module Concurrent
5
- class Reactor
6
-
7
- describe DRbAsyncDemux, not_on_travis: true do
8
-
9
- subject{ DRbAsyncDemux.new }
10
-
11
- after(:each) do
12
- DRb.stop_service
13
- end
14
-
15
- def post_event(demux, event, *args)
16
- there = DRbObject.new_with_uri(DRbAsyncDemux::DEFAULT_URI)
17
- there.send(event, *args)
18
- end
19
-
20
- context '#initialize' do
21
-
22
- it 'sets the initial state to :stopped' do
23
- subject.should_not be_running
24
- end
25
-
26
- it 'uses the given URI' do
27
- uri = 'druby://concurrent-ruby.com:4242'
28
- demux = DRbAsyncDemux.new(uri: uri)
29
- demux.uri.should eq uri
30
- end
31
-
32
- it 'uses the default URI when none given' do
33
- demux = DRbAsyncDemux.new
34
- demux.uri.should eq DRbAsyncDemux::DEFAULT_URI
35
- end
36
-
37
- it 'uses the given ACL' do
38
- acl = %w[deny all]
39
- ACL.should_receive(:new).with(acl).and_return(acl)
40
- demux = DRbAsyncDemux.new(acl: acl)
41
- demux.acl.should eq acl
42
- end
43
-
44
- it 'uses the default ACL when given' do
45
- acl = DRbAsyncDemux::DEFAULT_ACL
46
- ACL.should_receive(:new).with(acl).and_return(acl)
47
- demux = DRbAsyncDemux.new
48
- demux.acl.should eq acl
49
- end
50
- end
51
-
52
- context '#run' do
53
-
54
- it 'raises an exception if already runed' do
55
- subject.run
56
-
57
- lambda {
58
- subject.run
59
- }.should raise_error(StandardError)
60
- end
61
-
62
- it 'installs the ACL' do
63
- acl = %w[deny all]
64
- ACL.should_receive(:new).once.with(acl).and_return(acl)
65
- DRb.should_receive(:install_acl).once.with(acl)
66
- demux = DRbAsyncDemux.new(acl: acl)
67
- reactor = Concurrent::Reactor.new(demux)
68
- Thread.new{ reactor.run }
69
- sleep(0.1)
70
- reactor.stop
71
- end
72
-
73
- it 'runs DRb' do
74
- uri = DRbAsyncDemux::DEFAULT_URI
75
- DRb.should_receive(:start_service).with(uri, anything())
76
- demux = DRbAsyncDemux.new(uri: uri)
77
- reactor = Concurrent::Reactor.new(demux)
78
- Thread.new{ reactor.run }
79
- sleep(0.1)
80
- reactor.stop
81
- end
82
- end
83
-
84
- context '#stop' do
85
-
86
- it 'stops DRb' do
87
- DRb.should_receive(:stop_service).at_least(1).times
88
- subject.run
89
- sleep(0.1)
90
- subject.stop
91
- end
92
- end
93
-
94
- context '#running?' do
95
-
96
- it 'returns false when stopped' do
97
- subject.run
98
- sleep(0.1)
99
- subject.stop
100
- sleep(0.1)
101
- subject.should_not be_running
102
- end
103
-
104
- it 'returns true when running' do
105
- subject.run
106
- sleep(0.1)
107
- subject.should be_running
108
- end
109
- end
110
-
111
- context '#set_reactor' do
112
-
113
- it 'raises an exception when given an invalid reactor' do
114
- lambda {
115
- subject.set_reactor(Concurrent::Reactor.new)
116
- }.should_not raise_error
117
-
118
- lambda {
119
- subject.set_reactor('bogus')
120
- }.should raise_error(ArgumentError)
121
- end
122
- end
123
-
124
- context 'event handling' do
125
-
126
- it 'should post events to the reactor' do
127
- demux = subject
128
- reactor = Concurrent::Reactor.new(demux)
129
- reactor.add_handler(:foo){ nil }
130
- reactor.should_receive(:handle).with(:foo, 1,2,3).and_return([:ok, nil])
131
-
132
- Thread.new { reactor.run }
133
- sleep(0.1)
134
-
135
- post_event(demux, :foo, 1, 2, 3)
136
-
137
- reactor.stop
138
- sleep(0.1)
139
- end
140
- end
141
-
142
- specify 'integration', not_on_travis: true do
143
-
144
- # server
145
- demux = Concurrent::Reactor::DRbAsyncDemux.new
146
- reactor = Concurrent::Reactor.new(demux)
147
-
148
- reactor.add_handler(:echo) {|message| message }
149
- reactor.add_handler(:error) {|message| raise StandardError.new(message) }
150
- reactor.add_handler(:unknown) {|message| message }
151
- reactor.add_handler(:abend) {|message| message }
152
-
153
- t = Thread.new { reactor.run }
154
- sleep(0.1)
155
-
156
- # client
157
- there = DRbObject.new_with_uri(DRbAsyncDemux::DEFAULT_URI)
158
-
159
- # test :ok
160
- 10.times do
161
- message = Faker::Company.bs
162
- echo = there.echo(message)
163
- echo.should eq message
164
- end
165
-
166
- # test :ex
167
- lambda {
168
- echo = there.error('error')
169
- }.should raise_error(StandardError, 'error')
170
-
171
- # test :noop
172
- lambda {
173
- echo = there.bogus('bogus')
174
- }.should raise_error(NoMethodError)
175
-
176
- # test unknown respone code
177
- reactor.should_receive(:handle).with(:unknown).and_return([:unknown, nil])
178
- lambda {
179
- echo = there.unknown
180
- }.should raise_error(DRb::DRbError)
181
-
182
- # test handler error
183
- reactor.should_receive(:handle).with(:abend).and_raise(ArgumentError)
184
- lambda {
185
- echo = there.abend
186
- }.should raise_error(DRb::DRbRemoteError)
187
-
188
- # cleanup
189
- reactor.stop
190
- sleep(0.1)
191
- Thread.kill(t)
192
- sleep(0.1)
193
- end
194
- end
195
- end
196
- end
1
+ require 'spec_helper'
2
+ require 'faker'
3
+
4
+ module Concurrent
5
+ class Reactor
6
+
7
+ describe DRbAsyncDemux, not_on_travis: true do
8
+
9
+ subject{ DRbAsyncDemux.new }
10
+
11
+ after(:each) do
12
+ DRb.stop_service
13
+ end
14
+
15
+ def post_event(demux, event, *args)
16
+ there = DRbObject.new_with_uri(DRbAsyncDemux::DEFAULT_URI)
17
+ there.send(event, *args)
18
+ end
19
+
20
+ context '#initialize' do
21
+
22
+ it 'sets the initial state to :stopped' do
23
+ subject.should_not be_running
24
+ end
25
+
26
+ it 'uses the given URI' do
27
+ uri = 'druby://concurrent-ruby.com:4242'
28
+ demux = DRbAsyncDemux.new(uri: uri)
29
+ demux.uri.should eq uri
30
+ end
31
+
32
+ it 'uses the default URI when none given' do
33
+ demux = DRbAsyncDemux.new
34
+ demux.uri.should eq DRbAsyncDemux::DEFAULT_URI
35
+ end
36
+
37
+ it 'uses the given ACL' do
38
+ acl = %w[deny all]
39
+ ACL.should_receive(:new).with(acl).and_return(acl)
40
+ demux = DRbAsyncDemux.new(acl: acl)
41
+ demux.acl.should eq acl
42
+ end
43
+
44
+ it 'uses the default ACL when given' do
45
+ acl = DRbAsyncDemux::DEFAULT_ACL
46
+ ACL.should_receive(:new).with(acl).and_return(acl)
47
+ demux = DRbAsyncDemux.new
48
+ demux.acl.should eq acl
49
+ end
50
+ end
51
+
52
+ context '#run' do
53
+
54
+ it 'raises an exception if already runed' do
55
+ subject.run
56
+
57
+ lambda {
58
+ subject.run
59
+ }.should raise_error(StandardError)
60
+ end
61
+
62
+ it 'installs the ACL' do
63
+ acl = %w[deny all]
64
+ ACL.should_receive(:new).once.with(acl).and_return(acl)
65
+ DRb.should_receive(:install_acl).once.with(acl)
66
+ demux = DRbAsyncDemux.new(acl: acl)
67
+ reactor = Concurrent::Reactor.new(demux)
68
+ Thread.new{ reactor.run }
69
+ sleep(0.1)
70
+ reactor.stop
71
+ end
72
+
73
+ it 'runs DRb' do
74
+ uri = DRbAsyncDemux::DEFAULT_URI
75
+ DRb.should_receive(:start_service).with(uri, anything())
76
+ demux = DRbAsyncDemux.new(uri: uri)
77
+ reactor = Concurrent::Reactor.new(demux)
78
+ Thread.new{ reactor.run }
79
+ sleep(0.1)
80
+ reactor.stop
81
+ end
82
+ end
83
+
84
+ context '#stop' do
85
+
86
+ it 'stops DRb' do
87
+ DRb.should_receive(:stop_service).at_least(1).times
88
+ subject.run
89
+ sleep(0.1)
90
+ subject.stop
91
+ end
92
+ end
93
+
94
+ context '#running?' do
95
+
96
+ it 'returns false when stopped' do
97
+ subject.run
98
+ sleep(0.1)
99
+ subject.stop
100
+ sleep(0.1)
101
+ subject.should_not be_running
102
+ end
103
+
104
+ it 'returns true when running' do
105
+ subject.run
106
+ sleep(0.1)
107
+ subject.should be_running
108
+ end
109
+ end
110
+
111
+ context '#set_reactor' do
112
+
113
+ it 'raises an exception when given an invalid reactor' do
114
+ lambda {
115
+ subject.set_reactor(Concurrent::Reactor.new)
116
+ }.should_not raise_error
117
+
118
+ lambda {
119
+ subject.set_reactor('bogus')
120
+ }.should raise_error(ArgumentError)
121
+ end
122
+ end
123
+
124
+ context 'event handling' do
125
+
126
+ it 'should post events to the reactor' do
127
+ demux = subject
128
+ reactor = Concurrent::Reactor.new(demux)
129
+ reactor.add_handler(:foo){ nil }
130
+ reactor.should_receive(:handle).with(:foo, 1,2,3).and_return([:ok, nil])
131
+
132
+ Thread.new { reactor.run }
133
+ sleep(0.1)
134
+
135
+ post_event(demux, :foo, 1, 2, 3)
136
+
137
+ reactor.stop
138
+ sleep(0.1)
139
+ end
140
+ end
141
+
142
+ specify 'integration', not_on_travis: true do
143
+
144
+ # server
145
+ demux = Concurrent::Reactor::DRbAsyncDemux.new
146
+ reactor = Concurrent::Reactor.new(demux)
147
+
148
+ reactor.add_handler(:echo) {|message| message }
149
+ reactor.add_handler(:error) {|message| raise StandardError.new(message) }
150
+ reactor.add_handler(:unknown) {|message| message }
151
+ reactor.add_handler(:abend) {|message| message }
152
+
153
+ t = Thread.new { reactor.run }
154
+ sleep(0.1)
155
+
156
+ # client
157
+ there = DRbObject.new_with_uri(DRbAsyncDemux::DEFAULT_URI)
158
+
159
+ # test :ok
160
+ 10.times do
161
+ message = Faker::Company.bs
162
+ echo = there.echo(message)
163
+ echo.should eq message
164
+ end
165
+
166
+ # test :ex
167
+ lambda {
168
+ echo = there.error('error')
169
+ }.should raise_error(StandardError, 'error')
170
+
171
+ # test :noop
172
+ lambda {
173
+ echo = there.bogus('bogus')
174
+ }.should raise_error(NoMethodError)
175
+
176
+ # test unknown respone code
177
+ reactor.should_receive(:handle).with(:unknown).and_return([:unknown, nil])
178
+ lambda {
179
+ echo = there.unknown
180
+ }.should raise_error(DRb::DRbError)
181
+
182
+ # test handler error
183
+ reactor.should_receive(:handle).with(:abend).and_raise(ArgumentError)
184
+ lambda {
185
+ echo = there.abend
186
+ }.should raise_error(DRb::DRbRemoteError)
187
+
188
+ # cleanup
189
+ reactor.stop
190
+ sleep(0.1)
191
+ Thread.kill(t)
192
+ sleep(0.1)
193
+ end
194
+ end
195
+ end
196
+ end
@@ -1,410 +1,410 @@
1
- require 'spec_helper'
2
- require 'faker'
3
-
4
- module Concurrent
5
- class Reactor
6
-
7
- describe TcpSyncDemux, not_on_travis: true do
8
-
9
- subject do
10
- @subject = TcpSyncDemux.new(port: 2000 + rand(8000))
11
- end
12
-
13
- after(:each) do
14
- @subject.stop unless @subject.nil?
15
- end
16
-
17
- context '#initialize' do
18
-
19
- it 'sets the initial state to :stopped' do
20
- subject.should_not be_running
21
- end
22
-
23
- it 'raises an exception if already runed' do
24
- subject.run
25
-
26
- lambda {
27
- subject.run
28
- }.should raise_error(StandardError)
29
- end
30
-
31
- it 'uses the given host' do
32
- demux = TcpSyncDemux.new(host: 'www.foobar.com')
33
- demux.host.should eq 'www.foobar.com'
34
- demux.stop
35
- end
36
-
37
- it 'uses the default host when none is given' do
38
- demux = TcpSyncDemux.new
39
- demux.host.should eq TcpSyncDemux::DEFAULT_HOST
40
- demux.stop
41
- end
42
-
43
- it 'uses the given port' do
44
- demux = TcpSyncDemux.new(port: 4242)
45
- demux.port.should eq 4242
46
- demux.stop
47
- end
48
-
49
- it 'uses the default port when none is given' do
50
- demux = TcpSyncDemux.new
51
- demux.port.should eq TcpSyncDemux::DEFAULT_PORT
52
- demux.stop
53
- end
54
-
55
- it 'uses the given ACL' do
56
- acl = %w[deny all]
57
- ACL.should_receive(:new).with(acl).and_return(acl)
58
- demux = TcpSyncDemux.new(acl: acl)
59
- demux.acl.should eq acl
60
- demux.stop
61
- end
62
-
63
- it 'uses the default ACL when given' do
64
- acl = TcpSyncDemux::DEFAULT_ACL
65
- ACL.should_receive(:new).with(acl).and_return(acl)
66
- demux = TcpSyncDemux.new
67
- demux.acl.should eq acl
68
- demux.stop
69
- end
70
- end
71
-
72
- context '#run' do
73
-
74
- it 'creates a new TCP server' do
75
- TCPServer.should_receive(:new).with(TcpSyncDemux::DEFAULT_HOST, anything())
76
- subject.run
77
- end
78
-
79
- it 'returns true on success' do
80
- TCPServer.stub(:new).with(TcpSyncDemux::DEFAULT_HOST, anything())
81
- subject.run.should be_true
82
- end
83
-
84
- it 'returns false on failure' do
85
- TCPServer.stub(:new).with(TcpSyncDemux::DEFAULT_HOST, anything()) \
86
- .and_raise(StandardError)
87
- subject.run.should be_false
88
- end
89
-
90
- it 'raises an exception when already running' do
91
- subject.run
92
- lambda {
93
- subject.run
94
- }.should raise_error
95
- end
96
- end
97
-
98
- context '#stop' do
99
-
100
- let(:server){ double('tcp server') }
101
- let(:socket){ double('tcp socket') }
102
-
103
- before(:each) do
104
- socket.stub(:close).with(no_args())
105
- server.stub(:close).with(no_args())
106
- subject.run
107
- subject.instance_variable_set(:@socket, socket)
108
- subject.instance_variable_set(:@server, server)
109
- end
110
-
111
- it 'immediately returns true when not running' do
112
- socket.should_not_receive(:close)
113
- server.should_not_receive(:close)
114
- demux = TcpSyncDemux.new
115
- demux.stop.should be_true
116
- end
117
-
118
- it 'closes the socket' do
119
- socket.should_receive(:close).with(no_args())
120
- subject.stop
121
- end
122
-
123
- it 'closes the TCP server' do
124
- server.should_receive(:close).with(no_args())
125
- subject.stop
126
- end
127
-
128
- it 'is supresses socket close exceptions' do
129
- socket.should_receive(:close).and_raise(SocketError)
130
- lambda {
131
- subject.stop
132
- }.should_not raise_error
133
- end
134
-
135
- it 'supresses server close exceptions' do
136
- server.should_receive(:close).and_raise(SocketError)
137
- lambda {
138
- subject.stop
139
- }.should_not raise_error
140
- end
141
- end
142
-
143
- context '#running?' do
144
-
145
- it 'returns false when stopped' do
146
- subject.run
147
- sleep(0.1)
148
- subject.stop
149
- sleep(0.1)
150
- subject.should_not be_running
151
- end
152
-
153
- it 'returns true when running' do
154
- subject.run
155
- sleep(0.1)
156
- subject.should be_running
157
- end
158
- end
159
-
160
- context '#reset' do
161
-
162
- it 'closes the demux' do
163
- subject.should_receive(:run).exactly(2).times.and_return(true)
164
- subject.run
165
- sleep(0.1)
166
- subject.reset
167
- end
168
-
169
- it 'starts the demux' do
170
- # add one call to #stop for the :after clause
171
- subject.should_receive(:stop).exactly(2).times.and_return(true)
172
- sleep(0.1)
173
- subject.reset
174
- end
175
- end
176
-
177
- context '#accept' do
178
-
179
- let!(:event){ :echo }
180
- let!(:message){ 'hello world' }
181
-
182
- def setup_demux
183
- @demux = TcpSyncDemux.new(host: 'localhost', port: 5555, acl: %w[allow all])
184
- @demux.run
185
- end
186
-
187
- def send_event_message
188
- there = TCPSocket.open('localhost', 5555)
189
- @thread = Thread.new do
190
- there.puts(@demux.format_message(event, message))
191
- end
192
-
193
- @expected = nil
194
- Timeout::timeout(2) do
195
- @expected = @demux.accept
196
- end
197
- @thread.join(1)
198
- end
199
-
200
- after(:each) do
201
- @demux.stop unless @demux.nil?
202
- Thread.kill(@thread) unless @thread.nil?
203
- @expected = @demux = @thread = nil
204
- end
205
-
206
- it 'returns a correct EventContext object' do
207
- setup_demux
208
- send_event_message
209
- @expected.should be_a(EventContext)
210
- @expected.event.should eq :echo
211
- @expected.args.should eq ['hello world']
212
- @expected.callback.should be_nil
213
- end
214
-
215
- it 'returns nil on exception' do
216
- setup_demux
217
- @demux.should_receive(:get_message).with(any_args()).and_raise(StandardError)
218
- send_event_message
219
- @expected.should be_nil
220
- end
221
-
222
- it 'returns nil if the ACL rejects the client' do
223
- acl = double('acl')
224
- acl.should_receive(:allow_socket?).with(anything()).and_return(false)
225
- ACL.should_receive(:new).with(anything()).and_return(acl)
226
- setup_demux
227
- send_event_message
228
- @expected.should be_nil
229
- end
230
-
231
- it 'resets the demux on exception' do
232
- setup_demux
233
- @demux.should_receive(:get_message).with(any_args()).and_raise(StandardError)
234
- @demux.should_receive(:reset).with(no_args())
235
- send_event_message
236
- end
237
- end
238
-
239
- context '#respond' do
240
-
241
- it 'returns nil if the socket is nil' do
242
- subject.stop
243
- subject.respond(:ok, 'foo').should be_nil
244
- end
245
-
246
- it 'puts a message on the socket' do
247
- socket = double('tcp socket')
248
- socket.should_receive(:puts).with("ok\r\necho\r\n\r\n")
249
- subject.instance_variable_set(:@socket, socket)
250
- subject.respond(:ok, 'echo')
251
- end
252
-
253
- it 'resets the demux on exception' do
254
- socket = double('tcp socket')
255
- socket.should_receive(:puts).and_raise(SocketError)
256
- subject.instance_variable_set(:@socket, socket)
257
- subject.should_receive(:reset)
258
- subject.respond(:ok, 'echo')
259
- end
260
- end
261
-
262
- context '#format_message' do
263
-
264
- it 'raises an exception when the event is nil' do
265
- lambda {
266
- subject.format_message(nil)
267
- }.should raise_error(ArgumentError)
268
-
269
- lambda {
270
- TcpSyncDemux.format_message(nil)
271
- }.should raise_error(ArgumentError)
272
- end
273
-
274
- it 'raises an exception when the event is an empty string' do
275
- lambda {
276
- subject.format_message(' ')
277
- }.should raise_error(ArgumentError)
278
-
279
- lambda {
280
- TcpSyncDemux.format_message(' ')
281
- }.should raise_error(ArgumentError)
282
- end
283
-
284
- it 'creates a message with no arguments' do
285
- message = subject.format_message(:echo)
286
- message.should eq "echo\r\n\r\n"
287
-
288
- message = TcpSyncDemux.format_message('echo')
289
- message.should eq "echo\r\n\r\n"
290
- end
291
-
292
- it 'creates a message with arguments' do
293
- message = subject.format_message(:echo, 'hello', 'world')
294
- message.should eq "echo\r\nhello\r\nworld\r\n\r\n"
295
-
296
- message = TcpSyncDemux.format_message('echo', 'hello', 'world')
297
- message.should eq "echo\r\nhello\r\nworld\r\n\r\n"
298
- end
299
- end
300
-
301
- context '#parse_message' do
302
-
303
- it 'accepts the message as a string' do
304
- message = subject.parse_message("echo\r\nhello world\r\n\r\n")
305
- message.should_not eq [nil, nil]
306
- end
307
-
308
- it 'accepts the message as an array of lines' do
309
- message = subject.parse_message(%w[echo hello world])
310
- message.should_not eq [nil, nil]
311
- end
312
-
313
- it 'recognizes an event name beginning with a colon' do
314
- message = subject.parse_message(":echo\r\nhello world\r\n\r\n")
315
- message.first.should eq :echo
316
-
317
- message = TcpSyncDemux.parse_message(%w[:echo hello world])
318
- message.first.should eq :echo
319
- end
320
-
321
- it 'recognizes an event name without a beginning colon' do
322
- message = subject.parse_message(%w[echo hello world])
323
- message.first.should eq :echo
324
-
325
- message = TcpSyncDemux.parse_message("echo\r\nhello world\r\n\r\n")
326
- message.first.should eq :echo
327
- end
328
-
329
- it 'parses a message without arguments' do
330
- message = subject.parse_message("echo\r\n\r\n")
331
- message.first.should eq :echo
332
-
333
- message = TcpSyncDemux.parse_message(%w[echo])
334
- message.first.should eq :echo
335
- end
336
-
337
- it 'parses a message with arguments' do
338
- message = subject.parse_message(%w[echo hello world])
339
- message.last.should eq %w[hello world]
340
-
341
- message = TcpSyncDemux.parse_message("echo\r\nhello world\r\n\r\n")
342
- message.last.should eq ['hello world']
343
- end
344
-
345
- it 'returns nil for a malformed message' do
346
- message = subject.parse_message(nil)
347
- message.should eq [nil, []]
348
-
349
- message = subject.parse_message(' ')
350
- message.should eq [nil, []]
351
- end
352
- end
353
-
354
- specify 'integration', not_on_travis: true do
355
-
356
- # server
357
- demux = Concurrent::Reactor::TcpSyncDemux.new
358
- reactor = Concurrent::Reactor.new(demux)
359
-
360
- reactor.add_handler(:echo) {|*args| args.first }
361
- reactor.add_handler(:error) {|*args| raise StandardError.new(args.first) }
362
- reactor.add_handler(:unknown) {|*args| args.first }
363
- reactor.add_handler(:abend) {|*args| args.first }
364
-
365
- t = Thread.new { reactor.run }
366
- sleep(0.1)
367
-
368
- # client
369
- there = TCPSocket.open(TcpSyncDemux::DEFAULT_HOST, TcpSyncDemux::DEFAULT_PORT)
370
-
371
- # test :ok
372
- 10.times do
373
- message = Faker::Company.bs
374
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:echo, message))
375
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
376
- result.should eq :ok
377
- echo.first.should eq message
378
- end
379
-
380
- # test :ex
381
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:error, 'error'))
382
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
383
- result.should eq :ex
384
- echo.first.should eq 'error'
385
-
386
- # test :noop
387
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:bogus, 'bogus'))
388
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
389
- result.should eq :noop
390
- echo.first.should =~ /bogus/
391
-
392
- # test handler error
393
- ex = ArgumentError.new('abend')
394
- ec = Reactor::EventContext.new(:abend, [])
395
- Reactor::EventContext.should_receive(:new).with(:abend, []).and_return(ec)
396
- reactor.should_receive(:handle_event).with(ec).and_raise(ex)
397
- there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:abend))
398
- result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
399
- result.should eq :abend
400
- echo.first.should eq 'abend'
401
-
402
- #cleanup
403
- reactor.stop
404
- sleep(0.1)
405
- Thread.kill(t)
406
- sleep(0.1)
407
- end
408
- end
409
- end
410
- end
1
+ require 'spec_helper'
2
+ require 'faker'
3
+
4
+ module Concurrent
5
+ class Reactor
6
+
7
+ describe TcpSyncDemux, not_on_travis: true do
8
+
9
+ subject do
10
+ @subject = TcpSyncDemux.new(port: 2000 + rand(8000))
11
+ end
12
+
13
+ after(:each) do
14
+ @subject.stop unless @subject.nil?
15
+ end
16
+
17
+ context '#initialize' do
18
+
19
+ it 'sets the initial state to :stopped' do
20
+ subject.should_not be_running
21
+ end
22
+
23
+ it 'raises an exception if already runed' do
24
+ subject.run
25
+
26
+ lambda {
27
+ subject.run
28
+ }.should raise_error(StandardError)
29
+ end
30
+
31
+ it 'uses the given host' do
32
+ demux = TcpSyncDemux.new(host: 'www.foobar.com')
33
+ demux.host.should eq 'www.foobar.com'
34
+ demux.stop
35
+ end
36
+
37
+ it 'uses the default host when none is given' do
38
+ demux = TcpSyncDemux.new
39
+ demux.host.should eq TcpSyncDemux::DEFAULT_HOST
40
+ demux.stop
41
+ end
42
+
43
+ it 'uses the given port' do
44
+ demux = TcpSyncDemux.new(port: 4242)
45
+ demux.port.should eq 4242
46
+ demux.stop
47
+ end
48
+
49
+ it 'uses the default port when none is given' do
50
+ demux = TcpSyncDemux.new
51
+ demux.port.should eq TcpSyncDemux::DEFAULT_PORT
52
+ demux.stop
53
+ end
54
+
55
+ it 'uses the given ACL' do
56
+ acl = %w[deny all]
57
+ ACL.should_receive(:new).with(acl).and_return(acl)
58
+ demux = TcpSyncDemux.new(acl: acl)
59
+ demux.acl.should eq acl
60
+ demux.stop
61
+ end
62
+
63
+ it 'uses the default ACL when given' do
64
+ acl = TcpSyncDemux::DEFAULT_ACL
65
+ ACL.should_receive(:new).with(acl).and_return(acl)
66
+ demux = TcpSyncDemux.new
67
+ demux.acl.should eq acl
68
+ demux.stop
69
+ end
70
+ end
71
+
72
+ context '#run' do
73
+
74
+ it 'creates a new TCP server' do
75
+ TCPServer.should_receive(:new).with(TcpSyncDemux::DEFAULT_HOST, anything())
76
+ subject.run
77
+ end
78
+
79
+ it 'returns true on success' do
80
+ TCPServer.stub(:new).with(TcpSyncDemux::DEFAULT_HOST, anything())
81
+ subject.run.should be_true
82
+ end
83
+
84
+ it 'returns false on failure' do
85
+ TCPServer.stub(:new).with(TcpSyncDemux::DEFAULT_HOST, anything()) \
86
+ .and_raise(StandardError)
87
+ subject.run.should be_false
88
+ end
89
+
90
+ it 'raises an exception when already running' do
91
+ subject.run
92
+ lambda {
93
+ subject.run
94
+ }.should raise_error
95
+ end
96
+ end
97
+
98
+ context '#stop' do
99
+
100
+ let(:server){ double('tcp server') }
101
+ let(:socket){ double('tcp socket') }
102
+
103
+ before(:each) do
104
+ socket.stub(:close).with(no_args())
105
+ server.stub(:close).with(no_args())
106
+ subject.run
107
+ subject.instance_variable_set(:@socket, socket)
108
+ subject.instance_variable_set(:@server, server)
109
+ end
110
+
111
+ it 'immediately returns true when not running' do
112
+ socket.should_not_receive(:close)
113
+ server.should_not_receive(:close)
114
+ demux = TcpSyncDemux.new
115
+ demux.stop.should be_true
116
+ end
117
+
118
+ it 'closes the socket' do
119
+ socket.should_receive(:close).with(no_args())
120
+ subject.stop
121
+ end
122
+
123
+ it 'closes the TCP server' do
124
+ server.should_receive(:close).with(no_args())
125
+ subject.stop
126
+ end
127
+
128
+ it 'is supresses socket close exceptions' do
129
+ socket.should_receive(:close).and_raise(SocketError)
130
+ lambda {
131
+ subject.stop
132
+ }.should_not raise_error
133
+ end
134
+
135
+ it 'supresses server close exceptions' do
136
+ server.should_receive(:close).and_raise(SocketError)
137
+ lambda {
138
+ subject.stop
139
+ }.should_not raise_error
140
+ end
141
+ end
142
+
143
+ context '#running?' do
144
+
145
+ it 'returns false when stopped' do
146
+ subject.run
147
+ sleep(0.1)
148
+ subject.stop
149
+ sleep(0.1)
150
+ subject.should_not be_running
151
+ end
152
+
153
+ it 'returns true when running' do
154
+ subject.run
155
+ sleep(0.1)
156
+ subject.should be_running
157
+ end
158
+ end
159
+
160
+ context '#reset' do
161
+
162
+ it 'closes the demux' do
163
+ subject.should_receive(:run).exactly(2).times.and_return(true)
164
+ subject.run
165
+ sleep(0.1)
166
+ subject.reset
167
+ end
168
+
169
+ it 'starts the demux' do
170
+ # add one call to #stop for the :after clause
171
+ subject.should_receive(:stop).exactly(2).times.and_return(true)
172
+ sleep(0.1)
173
+ subject.reset
174
+ end
175
+ end
176
+
177
+ context '#accept' do
178
+
179
+ let!(:event){ :echo }
180
+ let!(:message){ 'hello world' }
181
+
182
+ def setup_demux
183
+ @demux = TcpSyncDemux.new(host: 'localhost', port: 5555, acl: %w[allow all])
184
+ @demux.run
185
+ end
186
+
187
+ def send_event_message
188
+ there = TCPSocket.open('localhost', 5555)
189
+ @thread = Thread.new do
190
+ there.puts(@demux.format_message(event, message))
191
+ end
192
+
193
+ @expected = nil
194
+ Timeout::timeout(2) do
195
+ @expected = @demux.accept
196
+ end
197
+ @thread.join(1)
198
+ end
199
+
200
+ after(:each) do
201
+ @demux.stop unless @demux.nil?
202
+ Thread.kill(@thread) unless @thread.nil?
203
+ @expected = @demux = @thread = nil
204
+ end
205
+
206
+ it 'returns a correct EventContext object' do
207
+ setup_demux
208
+ send_event_message
209
+ @expected.should be_a(EventContext)
210
+ @expected.event.should eq :echo
211
+ @expected.args.should eq ['hello world']
212
+ @expected.callback.should be_nil
213
+ end
214
+
215
+ it 'returns nil on exception' do
216
+ setup_demux
217
+ @demux.should_receive(:get_message).with(any_args()).and_raise(StandardError)
218
+ send_event_message
219
+ @expected.should be_nil
220
+ end
221
+
222
+ it 'returns nil if the ACL rejects the client' do
223
+ acl = double('acl')
224
+ acl.should_receive(:allow_socket?).with(anything()).and_return(false)
225
+ ACL.should_receive(:new).with(anything()).and_return(acl)
226
+ setup_demux
227
+ send_event_message
228
+ @expected.should be_nil
229
+ end
230
+
231
+ it 'resets the demux on exception' do
232
+ setup_demux
233
+ @demux.should_receive(:get_message).with(any_args()).and_raise(StandardError)
234
+ @demux.should_receive(:reset).with(no_args())
235
+ send_event_message
236
+ end
237
+ end
238
+
239
+ context '#respond' do
240
+
241
+ it 'returns nil if the socket is nil' do
242
+ subject.stop
243
+ subject.respond(:ok, 'foo').should be_nil
244
+ end
245
+
246
+ it 'puts a message on the socket' do
247
+ socket = double('tcp socket')
248
+ socket.should_receive(:puts).with("ok\r\necho\r\n\r\n")
249
+ subject.instance_variable_set(:@socket, socket)
250
+ subject.respond(:ok, 'echo')
251
+ end
252
+
253
+ it 'resets the demux on exception' do
254
+ socket = double('tcp socket')
255
+ socket.should_receive(:puts).and_raise(SocketError)
256
+ subject.instance_variable_set(:@socket, socket)
257
+ subject.should_receive(:reset)
258
+ subject.respond(:ok, 'echo')
259
+ end
260
+ end
261
+
262
+ context '#format_message' do
263
+
264
+ it 'raises an exception when the event is nil' do
265
+ lambda {
266
+ subject.format_message(nil)
267
+ }.should raise_error(ArgumentError)
268
+
269
+ lambda {
270
+ TcpSyncDemux.format_message(nil)
271
+ }.should raise_error(ArgumentError)
272
+ end
273
+
274
+ it 'raises an exception when the event is an empty string' do
275
+ lambda {
276
+ subject.format_message(' ')
277
+ }.should raise_error(ArgumentError)
278
+
279
+ lambda {
280
+ TcpSyncDemux.format_message(' ')
281
+ }.should raise_error(ArgumentError)
282
+ end
283
+
284
+ it 'creates a message with no arguments' do
285
+ message = subject.format_message(:echo)
286
+ message.should eq "echo\r\n\r\n"
287
+
288
+ message = TcpSyncDemux.format_message('echo')
289
+ message.should eq "echo\r\n\r\n"
290
+ end
291
+
292
+ it 'creates a message with arguments' do
293
+ message = subject.format_message(:echo, 'hello', 'world')
294
+ message.should eq "echo\r\nhello\r\nworld\r\n\r\n"
295
+
296
+ message = TcpSyncDemux.format_message('echo', 'hello', 'world')
297
+ message.should eq "echo\r\nhello\r\nworld\r\n\r\n"
298
+ end
299
+ end
300
+
301
+ context '#parse_message' do
302
+
303
+ it 'accepts the message as a string' do
304
+ message = subject.parse_message("echo\r\nhello world\r\n\r\n")
305
+ message.should_not eq [nil, nil]
306
+ end
307
+
308
+ it 'accepts the message as an array of lines' do
309
+ message = subject.parse_message(%w[echo hello world])
310
+ message.should_not eq [nil, nil]
311
+ end
312
+
313
+ it 'recognizes an event name beginning with a colon' do
314
+ message = subject.parse_message(":echo\r\nhello world\r\n\r\n")
315
+ message.first.should eq :echo
316
+
317
+ message = TcpSyncDemux.parse_message(%w[:echo hello world])
318
+ message.first.should eq :echo
319
+ end
320
+
321
+ it 'recognizes an event name without a beginning colon' do
322
+ message = subject.parse_message(%w[echo hello world])
323
+ message.first.should eq :echo
324
+
325
+ message = TcpSyncDemux.parse_message("echo\r\nhello world\r\n\r\n")
326
+ message.first.should eq :echo
327
+ end
328
+
329
+ it 'parses a message without arguments' do
330
+ message = subject.parse_message("echo\r\n\r\n")
331
+ message.first.should eq :echo
332
+
333
+ message = TcpSyncDemux.parse_message(%w[echo])
334
+ message.first.should eq :echo
335
+ end
336
+
337
+ it 'parses a message with arguments' do
338
+ message = subject.parse_message(%w[echo hello world])
339
+ message.last.should eq %w[hello world]
340
+
341
+ message = TcpSyncDemux.parse_message("echo\r\nhello world\r\n\r\n")
342
+ message.last.should eq ['hello world']
343
+ end
344
+
345
+ it 'returns nil for a malformed message' do
346
+ message = subject.parse_message(nil)
347
+ message.should eq [nil, []]
348
+
349
+ message = subject.parse_message(' ')
350
+ message.should eq [nil, []]
351
+ end
352
+ end
353
+
354
+ specify 'integration', not_on_travis: true do
355
+
356
+ # server
357
+ demux = Concurrent::Reactor::TcpSyncDemux.new
358
+ reactor = Concurrent::Reactor.new(demux)
359
+
360
+ reactor.add_handler(:echo) {|*args| args.first }
361
+ reactor.add_handler(:error) {|*args| raise StandardError.new(args.first) }
362
+ reactor.add_handler(:unknown) {|*args| args.first }
363
+ reactor.add_handler(:abend) {|*args| args.first }
364
+
365
+ t = Thread.new { reactor.run }
366
+ sleep(0.1)
367
+
368
+ # client
369
+ there = TCPSocket.open(TcpSyncDemux::DEFAULT_HOST, TcpSyncDemux::DEFAULT_PORT)
370
+
371
+ # test :ok
372
+ 10.times do
373
+ message = Faker::Company.bs
374
+ there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:echo, message))
375
+ result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
376
+ result.should eq :ok
377
+ echo.first.should eq message
378
+ end
379
+
380
+ # test :ex
381
+ there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:error, 'error'))
382
+ result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
383
+ result.should eq :ex
384
+ echo.first.should eq 'error'
385
+
386
+ # test :noop
387
+ there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:bogus, 'bogus'))
388
+ result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
389
+ result.should eq :noop
390
+ echo.first.should =~ /bogus/
391
+
392
+ # test handler error
393
+ ex = ArgumentError.new('abend')
394
+ ec = Reactor::EventContext.new(:abend, [])
395
+ Reactor::EventContext.should_receive(:new).with(:abend, []).and_return(ec)
396
+ reactor.should_receive(:handle_event).with(ec).and_raise(ex)
397
+ there.puts(Concurrent::Reactor::TcpSyncDemux.format_message(:abend))
398
+ result, echo = Concurrent::Reactor::TcpSyncDemux.get_message(there)
399
+ result.should eq :abend
400
+ echo.first.should eq 'abend'
401
+
402
+ #cleanup
403
+ reactor.stop
404
+ sleep(0.1)
405
+ Thread.kill(t)
406
+ sleep(0.1)
407
+ end
408
+ end
409
+ end
410
+ end