blondy-dhcpd 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,21 +4,69 @@ module Blondy
4
4
  module DHCPD
5
5
  describe 'Dispatcher' do
6
6
  subject(:dispatcher) {Dispatcher}
7
+ let(:pool) {Pool}
8
+ let(:pool_query_result) do
9
+ pr = OpenStruct.new
10
+ pr.data = OpenStruct.new
11
+ pr.data.fname = String.new
12
+ pr.data.yiaddr = '0.0.0.0'
13
+ pr.data.options = Array.new
14
+ pr.code = 200
15
+ pr
16
+ end
7
17
  let(:from_ip) {'0.0.0.0'}
8
18
  let(:from_port) {67}
9
- let(:discover) do
10
- d = DHCP::Discover.new
11
- d.xid = 123456789
12
- d
19
+ [ :discover, :request, :release, :inform ].each do |message|
20
+ let(message) do
21
+ msg_class = DHCP.const_get message.to_s.capitalize.to_sym
22
+ d = msg_class.new
23
+ d.xid = 123456789
24
+ d
25
+ end
26
+ end
27
+ let(:reply) {OpenStruct.new}
28
+
29
+ before(:each) do
30
+ allow(pool).to receive(:query).and_return(pool_query_result)
31
+ allow(pool).to receive(:query).with('ee:ee:ee:ee:ee:ee', :discover).and_return(pool_query_result)
32
+ end
33
+
34
+ shared_examples 'Dispatcher' do |message|
35
+ it 'data is correct' do
36
+ dispatcher.dispatch(eval(message.to_s).pack, from_ip, from_port).data.pack.should == reply.data.pack
37
+ end
38
+ it 'ip is correct' do
39
+ dispatcher.dispatch(eval(message.to_s).pack, from_ip, from_port).ip.should == reply.ip
40
+ end
41
+ it 'port is correct' do
42
+ dispatcher.dispatch(eval(message.to_s).pack, from_ip, from_port).port.should == reply.port
43
+ end
13
44
  end
14
45
 
15
- context 'receive dhcp message' do
16
- it 'reply xid is the same as received xid' do
17
- dispatcher.dispatch(discover.pack, from_ip, from_port).data.xid.should == discover.xid
46
+ %w{discover request release inform}.each do |message|
47
+ context "receive dhcp #{message} message" do
48
+ it "dispatch it to specific #{message}_handler private method" do
49
+ dispatcher.should_receive("#{message}_handler".to_sym)
50
+ dispatcher.dispatch(eval(message).pack, from_ip, from_port)
51
+ end
52
+ if %w{discover request inform}.include? message
53
+ it 'reply xid is the same as received xid' do
54
+ dispatcher.dispatch(eval(message).pack, from_ip, from_port).data.xid.should == eval(message).xid
55
+ end
56
+ end
18
57
  end
19
58
  end
20
59
 
21
- context 'receive discovery message' do
60
+ context 'wrong message' do
61
+ it 'false when message is unknown' do
62
+ lambda { dispatcher.dispatch("abracadabra", from_ip, from_port) }.should raise_error(IncorrectMessage)
63
+ end
64
+ it 'false when action for message unspecified' do
65
+ lambda { dispatcher.dispatch("abracadabra", from_ip, from_port) }.should raise_error(IncorrectMessage)
66
+ end
67
+ end
68
+
69
+ describe 'receive discovery message' do
22
70
  # RFC 2131 (http://www.ietf.org/rfc/rfc2131.txt)
23
71
  #
24
72
  # http://stackoverflow.com/a/10757849
@@ -34,44 +82,155 @@ module Blondy
34
82
  # then the server unicasts DHCPOFFER and DHCPACK messages to the client's hardware address
35
83
  # and 'yiaddr' address. In all cases, when 'giaddr' is zero,
36
84
  # the server broadcasts any DHCPNAK messages to 0xffffffff.
37
- let(:reply) do
38
- reply = OpenStruct.new
85
+
86
+ before(:each) do
87
+ discover.chaddr = [238, 238, 238, 238, 238, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
88
+ discover.hlen = 6
89
+ pool_query_result.data.fname = 'test.txt'.unpack('C128').map {|x| x ? x : 0}
90
+ pool_query_result.data.yiaddr = IPAddr.new('192.168.5.150').to_i
91
+ pool_query_result.data.options = [
92
+ DHCP::MessageTypeOption.new({payload: [$DHCP_MSG_OFFER]}),
93
+ DHCP::ServerIdentifierOption.new({payload: [192, 168, 1, 1]}),
94
+ DHCP::DomainNameOption.new({payload: 'example.com'.unpack('C*')}),
95
+ DHCP::DomainNameServerOption.new({payload: [8,8,8,8]}),
96
+ DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
97
+ DHCP::SubnetMaskOption.new({payload: [255, 255, 255, 255]}),
98
+ DHCP::RouterOption.new({payload: [192, 168, 1, 1]})
99
+ ]
39
100
  reply.data = DHCP::Offer.new
40
- reply
101
+ reply.data.xid = discover.xid
102
+ reply.data.options = pool_query_result.data.options
103
+ reply.data.yiaddr = pool_query_result.data.yiaddr
104
+ reply.data.fname = pool_query_result.data.fname
105
+ reply.data.siaddr = IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
106
+ end
107
+
108
+ it 'reply with offer' do
109
+ dispatcher.dispatch(discover.pack, from_ip, from_port).data.class == DHCP::Offer
41
110
  end
42
111
 
43
112
  context 'giaddr != 0' do
44
- it 'send offer message to bootp relay' do
45
- pending
46
- data.giaddr = IPAddr.new('192.168.3.3').to_i
47
- server.should_receive(:send_datagram).with(reply.data.pack, '192.168.3.3', 67)
48
- server.receive_data(data.pack)
113
+ #reply with offer message to bootp relay
114
+ before(:each) do
115
+ giaddr = '192.168.3.3'
116
+ discover.giaddr = IPAddr.new(giaddr).to_i
117
+ reply.ip = giaddr
118
+ reply.port = 67
49
119
  end
120
+ it_should_behave_like 'Dispatcher', :discover
50
121
  end
51
122
  context 'giaddr = 0 and ciaddr != 0' do
52
- it 'send offer message to client by unicast' do
53
- pending
54
- data.ciaddr = IPAddr.new('192.168.3.3').to_i
55
- server.should_receive(:send_data).with(reply.data.pack, '192.168.3.3', 68)
56
- server.receive_data(data.pack)
123
+ #send offer message to client
124
+ before(:each) do
125
+ ciaddr = '192.168.3.4'
126
+ discover.ciaddr = IPAddr.new(ciaddr).to_i
127
+ reply.ip = ciaddr
128
+ reply.port = 68
57
129
  end
130
+ it_should_behave_like 'Dispatcher', :discover
58
131
  end
59
- context 'giaddr = 0 and ciaddr = 0 and flags = 1' do
60
- it 'send offer message to client by broadcast to 255.255.255.255' do
61
- pending
62
- data.flags = 1
63
- server.should_receive(:send_data).with(reply.data.pack, '', 68)
64
- server.receive_data(data.pack)
132
+ context 'giaddr = 0 and ciaddr = 0' do
133
+ #send offer message to client by broadcast to 255.255.255.255
134
+ before(:each) do
135
+ discover.flags = 1
136
+ reply.ip = '255.255.255.255'
137
+ reply.port = 68
65
138
  end
139
+ it_should_behave_like 'Dispatcher', :discover
66
140
  end
67
- context 'giaddr = 0 and ciaddr = 0 and flags = 0' do
68
- it 'send offer message to client by unicast it to client hwaddr and yiaddr' do
69
- pending
70
- data.yiaddr = IPAddr.new('192.168.3.200').to_i
71
- data.chaddr = [80, 229, 73, 35, 15, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
72
- reply.data.chaddr.should eq(data.chaddr)
73
- server.should_receive(:send_data).with(reply.data.pack, '192.168.3.200', 68)
74
- server.receive_data(data.pack)
141
+
142
+ context 'ask pool for configuration' do
143
+ context 'query already found in cache' do
144
+ it 'not reply for message' do
145
+ pool.should_receive(:query).with('ee:ee:ee:ee:ee:ee', :discover).and_return(false)
146
+ dispatcher.dispatch(discover.pack, from_ip, from_port).should be_false
147
+ end
148
+ end
149
+ context 'query not found in cache' do
150
+ it 'set reply fields according to pool query result' do
151
+ dispatcher.dispatch(discover.pack, from_ip, from_port).data.fname.should == pool_query_result.data.fname
152
+ dispatcher.dispatch(discover.pack, from_ip, from_port).data.yiaddr.should == pool_query_result.data.yiaddr
153
+ dispatcher.dispatch(discover.pack, from_ip, from_port).data.options.should == pool_query_result.data.options
154
+ end
155
+ it 'set siaddr to server ip address' do
156
+ dispatcher.dispatch(discover.pack, from_ip, from_port).data.siaddr.should == IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ describe 'receive request message' do
163
+ before(:each) do
164
+ request.chaddr = [238, 238, 238, 238, 238, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
165
+ request.hlen = 6
166
+ pool_query_result.data.fname = 'test.txt'.unpack('C128').map {|x| x ? x : 0}
167
+ pool_query_result.data.yiaddr = IPAddr.new('192.168.5.150').to_i
168
+ pool_query_result.data.options = [
169
+ DHCP::MessageTypeOption.new({payload: [$DHCP_MSG_ACK]}),
170
+ DHCP::ServerIdentifierOption.new({payload: [192, 168, 1, 1]}),
171
+ DHCP::DomainNameOption.new({payload: 'example.com'.unpack('C*')}),
172
+ DHCP::DomainNameServerOption.new({payload: [8,8,8,8]}),
173
+ DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
174
+ DHCP::SubnetMaskOption.new({payload: [255, 255, 255, 255]}),
175
+ DHCP::RouterOption.new({payload: [192, 168, 1, 1]})
176
+ ]
177
+ reply.data = DHCP::ACK.new
178
+ reply.data.xid = request.xid
179
+ reply.data.options = pool_query_result.data.options
180
+ reply.data.yiaddr = pool_query_result.data.yiaddr
181
+ reply.data.fname = pool_query_result.data.fname
182
+ reply.data.siaddr = IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
183
+ message = request
184
+ end
185
+ it 'reply with ack' do
186
+ dispatcher.dispatch(discover.pack, from_ip, from_port).data.class == DHCP::ACK
187
+ end
188
+ context 'giaddr != 0' do
189
+ #reply with ack message to bootp relay
190
+ before(:each) do
191
+ giaddr = '192.168.3.3'
192
+ request.giaddr = IPAddr.new(giaddr).to_i
193
+ reply.ip = giaddr
194
+ reply.port = 67
195
+ end
196
+ it_should_behave_like 'Dispatcher', :request
197
+ end
198
+ context 'giaddr = 0 and ciaddr != 0' do
199
+ #send ack message to client
200
+ before(:each) do
201
+ ciaddr = '192.168.3.4'
202
+ request.ciaddr = IPAddr.new(ciaddr).to_i
203
+ reply.ip = ciaddr
204
+ reply.port = 68
205
+ end
206
+ it_should_behave_like 'Dispatcher', :request
207
+ end
208
+ context 'giaddr = 0 and ciaddr = 0' do
209
+ #send ack message to client by broadcast to 255.255.255.255
210
+ before(:each) do
211
+ request.flags = 1
212
+ reply.ip = '255.255.255.255'
213
+ reply.port = 68
214
+ end
215
+ it_should_behave_like 'Dispatcher', :request
216
+ end
217
+
218
+ context 'ask pool for configuration' do
219
+ context 'query already found in cache' do
220
+ it 'not reply for message' do
221
+ pool.should_receive(:query).with('ee:ee:ee:ee:ee:ee', :request).and_return(false)
222
+ dispatcher.dispatch(request.pack, from_ip, from_port).should be_false
223
+ end
224
+ end
225
+ context 'query not found in cache' do
226
+ it 'set reply fields according to pool query result' do
227
+ dispatcher.dispatch(request.pack, from_ip, from_port).data.fname.should == pool_query_result.data.fname
228
+ dispatcher.dispatch(request.pack, from_ip, from_port).data.yiaddr.should == pool_query_result.data.yiaddr
229
+ dispatcher.dispatch(request.pack, from_ip, from_port).data.options.should == pool_query_result.data.options
230
+ end
231
+ it 'set siaddr to server ip address' do
232
+ dispatcher.dispatch(request.pack, from_ip, from_port).data.siaddr.should == IPAddr.new(Blondy::DHCPD::CONFIG['server_ip']).to_i
233
+ end
75
234
  end
76
235
  end
77
236
  end
data/spec/pool_spec.rb ADDED
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ module Blondy
4
+ module DHCPD
5
+
6
+ describe 'Pool' do
7
+ subject(:pool) {Pool}
8
+ let(:cache) {Cache}
9
+ let(:logger) {Logger}
10
+ let(:remote_json) { {'fname' => 'test.txt',
11
+ 'yiaddr' => '192.168.5.150',
12
+ 'domain' => 'example.com',
13
+ 'dns' => '8.8.8.8',
14
+ 'gw' => '192.168.1.3',
15
+ 'netmask' => '255.255.255.255' }.to_json }
16
+ let(:remote_invalid_json) { { 'netaddr' => 'invalid', 'yomask' => '' }.to_json }
17
+ let(:reply_data) do
18
+ pool_query_result = OpenStruct.new
19
+ pool_query_result.data = OpenStruct.new
20
+ pool_query_result.data.fname = 'test.txt'.unpack('C128').map {|x| x ? x : 0}
21
+ pool_query_result.data.yiaddr = IPAddr.new('192.168.5.150').to_i
22
+ pool_query_result.data.options = [
23
+ DHCP::MessageTypeOption.new({payload: [$DHCP_MSG_OFFER]}),
24
+ DHCP::ServerIdentifierOption.new({payload: Blondy::DHCPD::CONFIG['server_ip'].split('.').map {|octet| octet.to_i}}),
25
+ DHCP::DomainNameOption.new({payload: 'example.com'.unpack('C*')}),
26
+ DHCP::DomainNameServerOption.new({payload: [8,8,8,8]}),
27
+ DHCP::IPAddressLeaseTimeOption.new({payload: [7200].pack('N').unpack('C*')}),
28
+ DHCP::SubnetMaskOption.new({payload: [255, 255, 255, 255]}),
29
+ DHCP::RouterOption.new({payload: [192, 168, 1, 3]})
30
+ ]
31
+ pool_query_result
32
+ end
33
+
34
+ describe 'receive query' do
35
+
36
+ before(:each) do
37
+ cache.flush
38
+ stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
39
+ with(:headers => {'X-Blondy-Key'=>'abcd'}).
40
+ to_return(:status => 200, :body => remote_json, :headers => {})
41
+ end
42
+
43
+ it 'check for reply in cache' do
44
+ cache.should_receive(:query).with('11:11:11:11:11:11', :discover)
45
+ pool.query('11:11:11:11:11:11', :discover)
46
+ end
47
+
48
+ context 'not found in cache' do
49
+ before(:each) do
50
+ allow(cache).to receive(:query).with('11:11:11:11:11:11', :discover).and_return(false)
51
+ end
52
+ it 'initiate query to remote server' do
53
+ pool.query('11:11:11:11:11:11', :discover)
54
+ WebMock.should have_requested(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover")
55
+ end
56
+ it 'reply with false' do
57
+ pool.query('11:11:11:11:11:11', :discover).should == false
58
+ end
59
+ it 'set received data to cache', :no_em do
60
+ cache.stub(:query) { cache.unstub(:query); false }
61
+ EM.run_block { pool.query('11:11:11:11:11:11', :discover) }
62
+ cache.query('11:11:11:11:11:11', :discover)[:data].should == reply_data
63
+ end
64
+ end
65
+
66
+ context 'found in cache' do
67
+ before(:each) do
68
+ allow(cache).to receive(:query).with('11:11:11:11:11:11', :discover).and_return({data:reply_data, time: Time.now})
69
+ end
70
+
71
+ it 'return reply data' do
72
+ pool.query('11:11:11:11:11:11', :discover).should == reply_data
73
+ end
74
+ end
75
+
76
+ context 'remote pool unavailable' do
77
+ before(:each) do
78
+ stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
79
+ with(:headers => {'X-Blondy-Key'=>'abcd'}).
80
+ to_timeout
81
+ end
82
+ it 'log that pool unavailable', :no_em do
83
+ logger.should_receive(:error).with('Remote pool server is unavailable.')
84
+ EM.run_block { pool.query('11:11:11:11:11:11', :discover) }
85
+ end
86
+ it 'not set values to cache' do
87
+ cache.should_not_receive(:add)
88
+ pool.query('11:11:11:11:11:11', :discover)
89
+ end
90
+ end
91
+
92
+ context 'remote pool send incorrect data' do
93
+ before(:each) do
94
+ stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
95
+ with(:headers => {'X-Blondy-Key'=>'abcd'}).
96
+ to_return(:status => 200, :body => remote_invalid_json, :headers => {})
97
+ end
98
+ it 'return false' do
99
+ pool.query('11:11:11:11:11:11', :discover).should be_false
100
+ end
101
+ end
102
+
103
+ context 'remote pool reply with error' do
104
+ before(:each) do
105
+ stub_request(:get, "https://127.0.0.1/blondy/dhcpd?hwaddr=11:11:11:11:11:11&type=discover").
106
+ with(:headers => {'X-Blondy-Key'=>'abcd'}).
107
+ to_return(:status => 500, :body => "", :headers => {})
108
+ end
109
+ it 'log error code', :no_em do
110
+ logger.should_receive(:error).with('Remote server reply with 500 error code.')
111
+ EM.run_block { pool.query('11:11:11:11:11:11', :discover) }
112
+ end
113
+ it 'not set values to cache' do
114
+ cache.should_not_receive(:add)
115
+ pool.query('11:11:11:11:11:11', :discover)
116
+ end
117
+ end
118
+
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ module Blondy
4
+ module DHCPD
5
+ describe 'Reply' do
6
+
7
+ end
8
+ end
9
+ end
data/spec/server_spec.rb CHANGED
@@ -17,26 +17,87 @@ module Blondy
17
17
  d.xid = 123456789
18
18
  d
19
19
  end
20
+ let(:logger) {Logger}
20
21
 
21
22
  before(:each) do
22
23
  allow(EM).to receive(:open_udp_socket).and_return 0
23
24
  allow(Socket).to receive(:unpack_sockaddr_in).and_return ['0.0.0.0', '68']
25
+ allow(Logger).to receive(:info)
26
+ allow(Logger).to receive(:warn)
27
+ allow(Logger).to receive(:error)
28
+ allow(Pool).to receive(:query)
24
29
  end
25
30
 
26
- context 'receive dhcp message' do
31
+ it 'has defined buffer' do
32
+ server.instance_variable_defined?('@buffer').should be_true
33
+ end
34
+
35
+ describe 'receive message' do
27
36
  before(:each) do
28
37
  @ip, @port = Socket.unpack_sockaddr_in(server.get_peername)
29
38
  end
30
- it 'run dispatcher' do
31
- dispatcher.should_receive(:dispatch).with(discover.pack, @ip, @port)
32
- server.receive_data(discover.pack)
39
+
40
+ it 'add received data in buffer' do
41
+ server.receive_data('test data')
42
+ server.instance_variable_get('@buffer').include?('test data').should be_true
33
43
  end
34
- it 'send reply' do
35
- server.should_receive(:send_datagram)
36
- server.receive_data(discover.pack)
44
+ context 'buffer include DHCP magic number' do
45
+ it 'run dispatcher' do
46
+ dispatcher.should_receive(:dispatch).with(discover.pack, @ip, @port)
47
+ server.receive_data(discover.pack)
48
+ end
49
+ it 'clear buffer' do
50
+ server.receive_data(discover.pack)
51
+ server.instance_variable_get('@buffer').size.should eq(0)
52
+ end
53
+ end
54
+ context 'buffer not include DHCP magic' do
55
+ # Maximum dhcp message size is 576 bytes (but client can increase it)
56
+ # So we will try with 1000 bytes buffer
57
+ context 'data size in buffer is greater than 1000 bytes' do
58
+ it 'clear buffer' do
59
+ dummy_data = String.new
60
+ 1000.times { dummy_data += rand(9).to_s }
61
+ server.instance_variable_set('@buffer', dummy_data)
62
+ server.receive_data(discover.pack.byteslice(0,15))
63
+ server.instance_variable_get('@buffer').size.should eq(15)
64
+ end
65
+ end
66
+ it 'shoul not clear buffer' do
67
+ server.receive_data(discover.pack.byteslice(0,10))
68
+ server.instance_variable_get('@buffer').size.should eq(10)
69
+ end
37
70
  end
38
- end
39
71
 
72
+ context 'receive dhcp message' do
73
+ it 'send reply' do
74
+ reply = OpenStruct.new
75
+ reply.data = DHCP::Offer.new
76
+ allow(dispatcher).to receive(:dispatch).and_return(reply)
77
+ server.should_receive(:send_datagram)
78
+ server.receive_data(discover.pack)
79
+ end
80
+ context 'wrong data' do
81
+ it 'not send reply' do
82
+ server.should_not_receive(:send_datagram)
83
+ server.receive_data('abracadabra')
84
+ end
85
+ context 'can not convert received data to Message object' do
86
+ it 'should log error' do
87
+ logger.should_receive(:warn).with('Incorrect message received. Ignore.')
88
+ discover.options.shift
89
+ server.receive_data(discover.pack)
90
+ end
91
+ end
92
+ context 'no handler for message' do
93
+ it 'should log error' do
94
+ logger.should_receive(:warn).with('No handler for message found. Ignore.')
95
+ server.receive_data(DHCP::Offer.new.pack)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
40
101
  end
41
102
  end
42
103
  end
data/spec/spec_helper.rb CHANGED
@@ -4,16 +4,31 @@
4
4
  # loaded once.
5
5
  #
6
6
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require 'simplecov'
8
+ SimpleCov.start
7
9
 
8
10
  require 'net-dhcp'
9
11
  require 'eventmachine'
12
+ require 'em-http'
10
13
  require 'ostruct'
11
14
  require 'ipaddr'
12
15
  require 'rspec'
16
+ require 'webmock/rspec'
17
+ require 'json'
18
+ require 'fiber'
19
+ require 'log4r'
20
+
13
21
 
14
22
  spec_root = File.dirname(File.absolute_path(__FILE__))
15
- Dir.glob(spec_root + '/../lib/blondy/dhcpd/*') {|file| require file}
23
+ exclude = %w{config.rb logger.rb}
24
+ Dir.glob(spec_root + '/../lib/blondy/dhcpd/*') {|file| require file unless exclude.include?(file.gsub(/^.*\//,''))}
16
25
 
26
+ # Set config for test
27
+ Blondy::DHCPD::CONFIG = Hash.new
28
+ Blondy::DHCPD::CONFIG['server_ip'] = '192.168.5.1'
29
+ Blondy::DHCPD::CONFIG['master'] = 'https://127.0.0.1/blondy/dhcpd'
30
+ Blondy::DHCPD::CONFIG['client_key'] = 'abcd'
31
+ Logger = Log4r::Logger.new 'ruby-dhcpd'
17
32
 
18
33
  RSpec.configure do |config|
19
34
  config.treat_symbols_as_metadata_keys_with_true_values = true
@@ -30,17 +45,46 @@ end
30
45
  # Wrap for running examples inside eventmachine reactor
31
46
  RSpec::Core::Example.class_eval do
32
47
  alias ignorant_run run
48
+ class << self
49
+ attr_accessor :examples_fibers
50
+ end
51
+ self.examples_fibers = Array.new
33
52
 
34
53
  def run(example_group_instance, reporter)
35
- Fiber.new do
54
+ if @options[:no_em]
55
+ fibers_alive = [true]
56
+ while fibers_alive.include?(true)
57
+ fibers_alive = []
58
+ self.class.examples_fibers.each do |f|
59
+ f.resume if f.alive?
60
+ fibers_alive << f.alive?
61
+ end
62
+ end
63
+ while EM.reactor_running?
64
+ EM.stop
65
+ sleep 0.5
66
+ end
67
+ ignorant_run example_group_instance, reporter
68
+ elsif @options[:no_fiber]
36
69
  EM.run do
37
70
  df = EM::DefaultDeferrable.new
38
71
  result = nil
39
- df.callback { |x| EM.stop_event_loop; Fiber.yield result }
72
+ df.callback { |x| EM.stop_event_loop; result }
40
73
  result = ignorant_run example_group_instance, reporter
41
74
  df.succeed
42
75
  end
43
- end.resume
76
+ else
77
+ f = Fiber.new do
78
+ EM.run do
79
+ df = EM::DefaultDeferrable.new
80
+ result = nil
81
+ df.callback { |x| EM.stop_event_loop; Fiber.yield result }
82
+ result = ignorant_run example_group_instance, reporter
83
+ df.succeed
84
+ end
85
+ end
86
+ self.class.examples_fibers << f
87
+ f.resume
88
+ end
44
89
  end
45
90
  end
46
-