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.
- data/LICENSE +21 -21
- data/README.md +275 -275
- data/lib/concurrent.rb +28 -28
- data/lib/concurrent/agent.rb +114 -114
- data/lib/concurrent/cached_thread_pool.rb +131 -129
- data/lib/concurrent/defer.rb +65 -65
- data/lib/concurrent/event.rb +60 -60
- data/lib/concurrent/event_machine_defer_proxy.rb +23 -23
- data/lib/concurrent/executor.rb +96 -95
- data/lib/concurrent/fixed_thread_pool.rb +99 -95
- data/lib/concurrent/functions.rb +120 -120
- data/lib/concurrent/future.rb +42 -42
- data/lib/concurrent/global_thread_pool.rb +16 -16
- data/lib/concurrent/goroutine.rb +29 -29
- data/lib/concurrent/null_thread_pool.rb +22 -22
- data/lib/concurrent/obligation.rb +67 -67
- data/lib/concurrent/promise.rb +174 -174
- data/lib/concurrent/reactor.rb +166 -166
- data/lib/concurrent/reactor/drb_async_demux.rb +83 -83
- data/lib/concurrent/reactor/tcp_sync_demux.rb +131 -131
- data/lib/concurrent/supervisor.rb +105 -100
- data/lib/concurrent/thread_pool.rb +76 -76
- data/lib/concurrent/utilities.rb +32 -32
- data/lib/concurrent/version.rb +3 -3
- data/lib/concurrent_ruby.rb +1 -1
- data/md/agent.md +123 -123
- data/md/defer.md +174 -174
- data/md/event.md +32 -32
- data/md/executor.md +187 -187
- data/md/future.md +83 -83
- data/md/goroutine.md +52 -52
- data/md/obligation.md +32 -32
- data/md/promise.md +227 -227
- data/md/thread_pool.md +224 -224
- data/spec/concurrent/agent_spec.rb +386 -386
- data/spec/concurrent/cached_thread_pool_spec.rb +125 -125
- data/spec/concurrent/defer_spec.rb +195 -195
- data/spec/concurrent/event_machine_defer_proxy_spec.rb +256 -256
- data/spec/concurrent/event_spec.rb +134 -134
- data/spec/concurrent/executor_spec.rb +200 -200
- data/spec/concurrent/fixed_thread_pool_spec.rb +83 -83
- data/spec/concurrent/functions_spec.rb +217 -217
- data/spec/concurrent/future_spec.rb +108 -108
- data/spec/concurrent/global_thread_pool_spec.rb +38 -38
- data/spec/concurrent/goroutine_spec.rb +67 -67
- data/spec/concurrent/null_thread_pool_spec.rb +57 -54
- data/spec/concurrent/obligation_shared.rb +132 -132
- data/spec/concurrent/promise_spec.rb +312 -312
- data/spec/concurrent/reactor/drb_async_demux_spec.rb +196 -196
- data/spec/concurrent/reactor/tcp_sync_demux_spec.rb +410 -410
- data/spec/concurrent/reactor_spec.rb +364 -364
- data/spec/concurrent/supervisor_spec.rb +269 -258
- data/spec/concurrent/thread_pool_shared.rb +204 -204
- data/spec/concurrent/utilities_spec.rb +74 -74
- data/spec/spec_helper.rb +32 -32
- metadata +20 -16
- 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
|