mqtt 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,21 @@
1
1
  # Class for implementing a proxy to filter/mangle MQTT packets.
2
2
  class MQTT::Proxy
3
+ # Address to bind listening socket to
3
4
  attr_reader :local_host
5
+
6
+ # Port to bind listening socket to
4
7
  attr_reader :local_port
8
+
9
+ # Address of upstream broker to send packets upstream to
5
10
  attr_reader :broker_host
11
+
12
+ # Port of upstream broker to send packets upstream to.
6
13
  attr_reader :broker_port
7
- attr_reader :listen_queue
14
+
15
+ # Time in seconds before disconnecting an idle connection
8
16
  attr_reader :select_timeout
17
+
18
+ # Ruby Logger object to send informational messages to
9
19
  attr_reader :logger
10
20
 
11
21
  # Create a new MQTT Proxy instance.
@@ -23,7 +33,7 @@ class MQTT::Proxy
23
33
  def initialize(args={})
24
34
  @local_host = args[:local_host] || '0.0.0.0'
25
35
  @local_port = args[:local_port] || MQTT::DEFAULT_PORT
26
- @broker_host = args[:broker_host] || MQTT::DEFAULT_HOST
36
+ @broker_host = args[:broker_host]
27
37
  @broker_port = args[:broker_port] || 18830
28
38
  @select_timeout = args[:select_timeout] || 60
29
39
 
@@ -1,3 +1,3 @@
1
1
  module MQTT
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,3 +1,6 @@
1
+ # encoding: BINARY
2
+ # Encoding is set to binary, so that the binary packets aren't validated as UTF-8
3
+
1
4
  $:.unshift(File.dirname(__FILE__))
2
5
 
3
6
  require 'spec_helper'
@@ -6,111 +9,236 @@ require 'mqtt'
6
9
  describe MQTT::Client do
7
10
 
8
11
  before(:each) do
9
- @client = MQTT::Client.new
10
- @socket = StringIO.new
11
- @socket.set_encoding("binary") if @socket.respond_to?(:set_encoding)
12
+ # Reset environment variable
13
+ ENV.delete('MQTT_BROKER')
14
+ end
15
+
16
+ let(:client) { MQTT::Client.new(:remote_host => 'localhost') }
17
+ let(:socket) do
18
+ socket = StringIO.new
19
+ if socket.respond_to?(:set_encoding)
20
+ socket.set_encoding("binary")
21
+ else
22
+ socket
23
+ end
12
24
  end
13
25
 
14
26
  describe "initializing a client" do
15
27
  it "with no arguments, it should use the defaults" do
16
- @client = MQTT::Client.new
17
- @client.remote_host.should == 'localhost'
18
- @client.remote_port.should == 1883
19
- @client.keep_alive.should == 15
28
+ client = MQTT::Client.new
29
+ client.remote_host.should == nil
30
+ client.remote_port.should == 1883
31
+ client.keep_alive.should == 15
20
32
  end
21
33
 
22
34
  it "with a single string argument, it should use it has the host" do
23
- @client = MQTT::Client.new('otherhost.mqtt.org')
24
- @client.remote_host.should == 'otherhost.mqtt.org'
25
- @client.remote_port.should == 1883
26
- @client.keep_alive.should == 15
35
+ client = MQTT::Client.new('otherhost.mqtt.org')
36
+ client.remote_host.should == 'otherhost.mqtt.org'
37
+ client.remote_port.should == 1883
38
+ client.keep_alive.should == 15
27
39
  end
28
40
 
29
41
  it "with two arguments, it should use it as the host and port" do
30
- @client = MQTT::Client.new('otherhost.mqtt.org', 1000)
31
- @client.remote_host.should == 'otherhost.mqtt.org'
32
- @client.remote_port.should == 1000
33
- @client.keep_alive.should == 15
42
+ client = MQTT::Client.new('otherhost.mqtt.org', 1000)
43
+ client.remote_host.should == 'otherhost.mqtt.org'
44
+ client.remote_port.should == 1000
45
+ client.keep_alive.should == 15
34
46
  end
35
47
 
36
48
  it "with names arguments, it should use those as arguments" do
37
- @client = MQTT::Client.new(:remote_host => 'otherhost.mqtt.org', :remote_port => 1000)
38
- @client.remote_host.should == 'otherhost.mqtt.org'
39
- @client.remote_port.should == 1000
40
- @client.keep_alive.should == 15
49
+ client = MQTT::Client.new(:remote_host => 'otherhost.mqtt.org', :remote_port => 1000)
50
+ client.remote_host.should == 'otherhost.mqtt.org'
51
+ client.remote_port.should == 1000
52
+ client.keep_alive.should == 15
41
53
  end
42
54
 
43
55
  it "with a hash, it should use those as arguments" do
44
- @client = MQTT::Client.new({:remote_host => 'otherhost.mqtt.org', :remote_port => 1000})
45
- @client.remote_host.should == 'otherhost.mqtt.org'
46
- @client.remote_port.should == 1000
47
- @client.keep_alive.should == 15
56
+ client = MQTT::Client.new({:remote_host => 'otherhost.mqtt.org', :remote_port => 1000})
57
+ client.remote_host.should == 'otherhost.mqtt.org'
58
+ client.remote_port.should == 1000
59
+ client.keep_alive.should == 15
48
60
  end
49
61
 
50
62
  it "with a hash containing just a keep alive setting" do
51
- @client = MQTT::Client.new(:keep_alive => 60)
52
- @client.remote_host.should == 'localhost'
53
- @client.remote_port.should == 1883
54
- @client.keep_alive.should == 60
63
+ client = MQTT::Client.new(:remote_host => 'localhost', :keep_alive => 60)
64
+ client.remote_host.should == 'localhost'
65
+ client.remote_port.should == 1883
66
+ client.keep_alive.should == 60
67
+ end
68
+
69
+ it "with a combination of a host name and a hash of settings" do
70
+ client = MQTT::Client.new('localhost', :keep_alive => 65)
71
+ client.remote_host.should == 'localhost'
72
+ client.remote_port.should == 1883
73
+ client.keep_alive.should == 65
74
+ end
75
+
76
+ it "with a combination of a host name, port and a hash of settings" do
77
+ client = MQTT::Client.new('localhost', 1888, :keep_alive => 65)
78
+ client.remote_host.should == 'localhost'
79
+ client.remote_port.should == 1888
80
+ client.keep_alive.should == 65
81
+ end
82
+
83
+ it "with a mqtt:// URI containing just a hostname" do
84
+ client = MQTT::Client.new(URI.parse('mqtt://mqtt.example.com'))
85
+ client.remote_host.should == 'mqtt.example.com'
86
+ client.remote_port.should == 1883
87
+ client.ssl.should be_false
88
+ end
89
+
90
+ it "with a mqtts:// URI containing just a hostname" do
91
+ client = MQTT::Client.new(URI.parse('mqtts://mqtt.example.com'))
92
+ client.remote_host.should == 'mqtt.example.com'
93
+ client.remote_port.should == 8883
94
+ client.ssl.should be_true
95
+ end
96
+
97
+ it "with a mqtt:// URI containing a custom port number" do
98
+ client = MQTT::Client.new(URI.parse('mqtt://mqtt.example.com:1234/'))
99
+ client.remote_host.should == 'mqtt.example.com'
100
+ client.remote_port.should == 1234
101
+ client.ssl.should be_false
102
+ end
103
+
104
+ it "with a mqtts:// URI containing a custom port number" do
105
+ client = MQTT::Client.new(URI.parse('mqtts://mqtt.example.com:1234/'))
106
+ client.remote_host.should == 'mqtt.example.com'
107
+ client.remote_port.should == 1234
108
+ client.ssl.should be_true
109
+ end
110
+
111
+ it "with a URI containing a username and password" do
112
+ client = MQTT::Client.new(URI.parse('mqtt://auser:bpass@mqtt.example.com'))
113
+ client.remote_host.should == 'mqtt.example.com'
114
+ client.remote_port.should == 1883
115
+ client.username.should == 'auser'
116
+ client.password.should == 'bpass'
117
+ end
118
+
119
+ it "with a URI as a string" do
120
+ client = MQTT::Client.new('mqtt://mqtt.example.com')
121
+ client.remote_host.should == 'mqtt.example.com'
122
+ client.remote_port.should == 1883
123
+ end
124
+
125
+ it "with a URI and a hash of settings" do
126
+ client = MQTT::Client.new('mqtt://mqtt.example.com', :keep_alive => 65)
127
+ client.remote_host.should == 'mqtt.example.com'
128
+ client.remote_port.should == 1883
129
+ client.keep_alive.should == 65
130
+ end
131
+
132
+ it "with no arguments uses the MQTT_BROKER environment variable as connect URI" do
133
+ ENV['MQTT_BROKER'] = 'mqtt://mqtt.example.com:1234'
134
+ client = MQTT::Client.new
135
+ client.remote_host.should == 'mqtt.example.com'
136
+ client.remote_port.should == 1234
137
+ end
138
+
139
+ it "with an unsupported URI scheme" do
140
+ lambda {
141
+ client = MQTT::Client.new(URI.parse('http://mqtt.example.com/'))
142
+ }.should raise_error(
143
+ 'Only the mqtt:// and mqtts:// schemes are supported'
144
+ )
55
145
  end
56
146
 
57
147
  it "with three arguments" do
58
148
  lambda {
59
- @client = MQTT::Client.new(1, 2, 3)
149
+ client = MQTT::Client.new(1, 2, 3)
60
150
  }.should raise_error(
61
151
  'Unsupported number of arguments'
62
152
  )
63
153
  end
64
154
  end
65
155
 
156
+ describe "setting a client certificate file path" do
157
+ it "should add a certificate to the SSL context" do
158
+ client.ssl_context.cert.should be_nil
159
+ client.cert_file = fixture_path('client.pem')
160
+ client.ssl_context.cert.should be_a(OpenSSL::X509::Certificate)
161
+ end
162
+ end
163
+
164
+ describe "setting a client private key file path" do
165
+ it "should add a certificate to the SSL context" do
166
+ client.ssl_context.key.should be_nil
167
+ client.key_file = fixture_path('client.key')
168
+ client.ssl_context.key.should be_a(OpenSSL::PKey::RSA)
169
+ end
170
+ end
171
+
172
+ describe "setting a Certificate Authority file path" do
173
+ it "should add a CA file path to the SSL context" do
174
+ client.ssl_context.ca_file.should be_nil
175
+ client.ca_file = fixture_path('root-ca.pem')
176
+ client.ssl_context.ca_file.should == fixture_path('root-ca.pem')
177
+ end
178
+
179
+ it "should enable peer verification" do
180
+ client.ca_file = fixture_path('root-ca.pem')
181
+ client.ssl_context.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
182
+ end
183
+ end
184
+
66
185
  describe "when calling the 'connect' method on a client" do
67
186
  before(:each) do
68
- TCPSocket.stub(:new).and_return(@socket)
187
+ TCPSocket.stub(:new).and_return(socket)
69
188
  Thread.stub(:new)
70
- @client.stub(:receive_connack)
189
+ client.stub(:receive_connack)
71
190
  end
72
191
 
73
192
  it "should create a TCP Socket if not connected" do
74
- TCPSocket.should_receive(:new).once.and_return(@socket)
75
- @client.connect('myclient')
193
+ TCPSocket.should_receive(:new).once.and_return(socket)
194
+ client.connect('myclient')
76
195
  end
77
196
 
78
197
  it "should not create a new TCP Socket if connected" do
79
- @client.stub(:connected?).and_return(true)
198
+ client.stub(:connected?).and_return(true)
80
199
  TCPSocket.should_receive(:new).never
81
- @client.connect('myclient')
200
+ client.connect('myclient')
82
201
  end
83
202
 
84
203
  it "should start the reader thread if not connected" do
85
204
  Thread.should_receive(:new).once
86
- @client.connect('myclient')
205
+ client.connect('myclient')
87
206
  end
88
207
 
89
208
  it "should write a valid CONNECT packet to the socket if not connected" do
90
- @client.connect('myclient')
91
- @socket.string.should == "\020\026\x00\x06MQIsdp\x03\x02\x00\x0f\x00\x08myclient"
209
+ client.connect('myclient')
210
+ socket.string.should == "\020\026\x00\x06MQIsdp\x03\x02\x00\x0f\x00\x08myclient"
92
211
  end
93
212
 
94
213
  it "should try and read an acknowledgement packet to the socket if not connected" do
95
- @client.should_receive(:receive_connack).once
96
- @client.connect('myclient')
214
+ client.should_receive(:receive_connack).once
215
+ client.connect('myclient')
216
+ end
217
+
218
+ it "should throw an exception if no host is configured" do
219
+ lambda {
220
+ client = MQTT::Client.new
221
+ client.connect
222
+ }.should raise_error(
223
+ 'No MQTT broker host set when attempting to connect'
224
+ )
97
225
  end
98
226
 
99
227
  it "should disconnect after connecting, if a block is given" do
100
- @client.should_receive(:disconnect).once
101
- @client.connect('myclient') { nil }
228
+ client.should_receive(:disconnect).once
229
+ client.connect('myclient') { nil }
102
230
  end
103
231
 
104
232
  it "should not disconnect after connecting, if no block is given" do
105
- @client.should_receive(:disconnect).never
106
- @client.connect('myclient')
233
+ client.should_receive(:disconnect).never
234
+ client.connect('myclient')
107
235
  end
108
236
 
109
237
  it "should include the username and password for an authenticated connection" do
110
- @client.username = 'username'
111
- @client.password = 'password'
112
- @client.connect('myclient')
113
- @socket.string.should ==
238
+ client.username = 'username'
239
+ client.password = 'password'
240
+ client.connect('myclient')
241
+ socket.string.should ==
114
242
  "\x10\x2A"+
115
243
  "\x00\x06MQIsdp"+
116
244
  "\x03\xC2\x00\x0f"+
@@ -119,37 +247,89 @@ describe MQTT::Client do
119
247
  "\x00\x08password"
120
248
  end
121
249
 
122
- it "should reset clean session flag to true, if no client id is given" do
123
- @client.client_id = nil
124
- @client.clean_session = false
125
- @client.connect
126
- @client.clean_session.should be_true
250
+ context "no client id is given" do
251
+ it "should throw an exception if the clean session flag is false" do
252
+ lambda {
253
+ client.client_id = nil
254
+ client.clean_session = false
255
+ client.connect
256
+ }.should raise_error(
257
+ 'Must provide a client_id if clean_session is set to false'
258
+ )
259
+ end
260
+
261
+ it "should generate a client if the clean session flag is true" do
262
+ client.client_id = nil
263
+ client.clean_session = true
264
+ client.connect
265
+ client.client_id.should match(/^\w+$/)
266
+ end
267
+ end
268
+
269
+ context "and using ssl" do
270
+ let(:ssl_socket) {
271
+ double(
272
+ "SSLSocket",
273
+ :sync_close= => true,
274
+ :write => true,
275
+ :connect => true,
276
+ :closed? => false
277
+ )
278
+ }
279
+
280
+ it "should use ssl if it enabled using the :ssl => true parameter" do
281
+ OpenSSL::SSL::SSLSocket.should_receive(:new).and_return(ssl_socket)
282
+ ssl_socket.should_receive(:connect)
283
+
284
+ client = MQTT::Client.new('mqtt.example.com', :ssl => true)
285
+ client.stub(:receive_connack)
286
+ client.connect
287
+ end
288
+
289
+ it "should use ssl if it enabled using the mqtts:// scheme" do
290
+ OpenSSL::SSL::SSLSocket.should_receive(:new).and_return(ssl_socket)
291
+ ssl_socket.should_receive(:connect)
292
+
293
+ client = MQTT::Client.new('mqtts://mqtt.example.com')
294
+ client.stub(:receive_connack)
295
+ client.connect
296
+ end
297
+
298
+ it "should use set the SSL version, if the :ssl parameter is a symbol" do
299
+ OpenSSL::SSL::SSLSocket.should_receive(:new).and_return(ssl_socket)
300
+ ssl_socket.should_receive(:connect)
301
+
302
+ client = MQTT::Client.new('mqtt.example.com', :ssl => :TLSv1)
303
+ client.ssl_context.should_receive('ssl_version=').with(:TLSv1)
304
+ client.stub(:receive_connack)
305
+ client.connect
306
+ end
127
307
  end
128
308
 
129
309
  context "with a last will and testament set" do
130
310
  before(:each) do
131
- @client.set_will('topic', 'hello', retain=false, qos=1)
311
+ client.set_will('topic', 'hello', retain=false, qos=1)
132
312
  end
133
313
 
134
314
  it "should have set the Will's topic" do
135
- @client.will_topic.should == 'topic'
315
+ client.will_topic.should == 'topic'
136
316
  end
137
317
 
138
318
  it "should have set the Will's payload" do
139
- @client.will_payload.should == 'hello'
319
+ client.will_payload.should == 'hello'
140
320
  end
141
321
 
142
322
  it "should have set the Will's retain flag to true" do
143
- @client.will_retain.should be_false
323
+ client.will_retain.should be_false
144
324
  end
145
325
 
146
326
  it "should have set the Will's retain QOS value to 1" do
147
- @client.will_qos.should == 1
327
+ client.will_qos.should == 1
148
328
  end
149
329
 
150
330
  it "should include the will in the CONNECT message" do
151
- @client.connect('myclient')
152
- @socket.string.should ==
331
+ client.connect('myclient')
332
+ socket.string.should ==
153
333
  "\x10\x24"+
154
334
  "\x00\x06MQIsdp"+
155
335
  "\x03\x0e\x00\x0f"+
@@ -161,218 +341,217 @@ describe MQTT::Client do
161
341
  end
162
342
 
163
343
  describe "calling 'connect' on the class" do
164
- before(:each) do
165
- TCPSocket.stub(:new).and_return(@socket)
166
- MQTT::Client.stub(:new).and_return(@client)
167
- Thread.stub(:new)
168
- @client.stub(:receive_connack)
169
- end
170
-
171
344
  it "should create a new client object" do
172
- MQTT::Client.should_receive(:new).once
345
+ client = double("MQTT::Client")
346
+ allow(client).to receive(:connect)
347
+ expect(MQTT::Client).to receive(:new).once.and_return(client)
173
348
  MQTT::Client.connect
174
349
  end
175
350
 
176
351
  it "should call connect new client object" do
177
- @client.should_receive(:connect).once
352
+ client = double("MQTT::Client")
353
+ expect(client).to receive(:connect)
354
+ allow(MQTT::Client).to receive(:new).once.and_return(client)
178
355
  MQTT::Client.connect
179
356
  end
180
357
 
181
358
  it "should return the new client object" do
182
- client = MQTT::Client.connect
183
- client.class.should == MQTT::Client
359
+ client = double("MQTT::Client")
360
+ allow(client).to receive(:connect)
361
+ allow(MQTT::Client).to receive(:new).once.and_return(client)
362
+ MQTT::Client.connect.should == client
184
363
  end
185
364
  end
186
365
 
187
366
  describe "when calling the 'receive_connack' method" do
188
367
  before(:each) do
189
- @client.instance_variable_set(:@socket, @socket)
190
- IO.stub(:select).and_return([[@socket], [], []])
368
+ client.instance_variable_set('@socket', socket)
369
+ IO.stub(:select).and_return([[socket], [], []])
191
370
  end
192
371
 
193
372
  it "should not throw an exception for a successful CONNACK packet" do
194
- @socket.write("\x20\x02\x00\x00")
195
- @socket.rewind
196
- lambda { @client.send(:receive_connack) }.should_not raise_error
373
+ socket.write("\x20\x02\x00\x00")
374
+ socket.rewind
375
+ lambda { client.send(:receive_connack) }.should_not raise_error
197
376
  end
198
377
 
199
378
  it "should throw an exception if the packet type isn't CONNACK" do
200
- @socket.write("\xD0\x00")
201
- @socket.rewind
202
- lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException)
379
+ socket.write("\xD0\x00")
380
+ socket.rewind
381
+ lambda { client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException)
203
382
  end
204
383
 
205
384
  it "should throw an exception if the CONNACK packet return code is 'unacceptable protocol version'" do
206
- @socket.write("\x20\x02\x00\x01")
207
- @socket.rewind
208
- lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /unacceptable protocol version/i)
385
+ socket.write("\x20\x02\x00\x01")
386
+ socket.rewind
387
+ lambda { client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /unacceptable protocol version/i)
209
388
  end
210
389
 
211
390
  it "should throw an exception if the CONNACK packet return code is 'client identifier rejected'" do
212
- @socket.write("\x20\x02\x00\x02")
213
- @socket.rewind
214
- lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /client identifier rejected/i)
391
+ socket.write("\x20\x02\x00\x02")
392
+ socket.rewind
393
+ lambda { client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /client identifier rejected/i)
215
394
  end
216
395
 
217
396
  it "should throw an exception if the CONNACK packet return code is 'broker unavailable'" do
218
- @socket.write("\x20\x02\x00\x03")
219
- @socket.rewind
220
- lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /broker unavailable/i)
397
+ socket.write("\x20\x02\x00\x03")
398
+ socket.rewind
399
+ lambda { client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /broker unavailable/i)
221
400
  end
222
401
 
223
402
  it "should throw an exception if the CONNACK packet return code is an unknown" do
224
- @socket.write("\x20\x02\x00\xAA")
225
- @socket.rewind
226
- lambda { @client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /connection refused/i)
403
+ socket.write("\x20\x02\x00\xAA")
404
+ socket.rewind
405
+ lambda { client.send(:receive_connack) }.should raise_error(MQTT::ProtocolException, /connection refused/i)
227
406
  end
228
407
  end
229
408
 
230
409
  describe "when calling the 'disconnect' method" do
231
410
  before(:each) do
232
411
  thread = double('Read Thread', :alive? => true, :kill => true)
233
- @client.instance_variable_set(:@socket, @socket)
234
- @client.instance_variable_set(:@read_thread, thread)
412
+ client.instance_variable_set('@socket', socket)
413
+ client.instance_variable_set('@read_thread', thread)
235
414
  end
236
415
 
237
416
  it "should not do anything if the socket is already disconnected" do
238
- @client.stub(:connected?).and_return(false)
239
- @client.disconnect(true)
240
- @socket.string.should == ""
417
+ client.stub(:connected?).and_return(false)
418
+ client.disconnect(true)
419
+ socket.string.should == ""
241
420
  end
242
421
 
243
422
  it "should write a valid DISCONNECT packet to the socket if connected and the send_msg=true an" do
244
- @client.stub(:connected?).and_return(true)
245
- @client.disconnect(true)
246
- @socket.string.should == "\xE0\x00"
423
+ client.stub(:connected?).and_return(true)
424
+ client.disconnect(true)
425
+ socket.string.should == "\xE0\x00"
247
426
  end
248
427
 
249
428
  it "should not write anything to the socket if the send_msg=false" do
250
- @client.stub(:connected?).and_return(true)
251
- @client.disconnect(false)
252
- @socket.string.should be_empty
429
+ client.stub(:connected?).and_return(true)
430
+ client.disconnect(false)
431
+ socket.string.should be_empty
253
432
  end
254
433
 
255
434
  it "should call the close method on the socket" do
256
- @socket.should_receive(:close)
257
- @client.disconnect
435
+ socket.should_receive(:close)
436
+ client.disconnect
258
437
  end
259
438
  end
260
439
 
261
440
  describe "when calling the 'ping' method" do
262
441
  before(:each) do
263
- @client.instance_variable_set(:@socket, @socket)
442
+ client.instance_variable_set('@socket', socket)
264
443
  end
265
444
 
266
445
  it "should write a valid PINGREQ packet to the socket" do
267
- @client.ping
268
- @socket.string.should == "\xC0\x00"
446
+ client.ping
447
+ socket.string.should == "\xC0\x00"
269
448
  end
270
449
 
271
450
  it "should update the time a ping was last sent" do
272
- @client.instance_variable_set(:@last_pingreq, 0)
273
- @client.ping
274
- @client.instance_variable_get(:@last_pingreq).should_not == 0
451
+ client.instance_variable_set('@last_pingreq', 0)
452
+ client.ping
453
+ client.instance_variable_get('@last_pingreq').should_not == 0
275
454
  end
276
455
  end
277
456
 
278
457
  describe "when calling the 'publish' method" do
279
458
  before(:each) do
280
- @client.instance_variable_set(:@socket, @socket)
459
+ client.instance_variable_set('@socket', socket)
281
460
  end
282
461
 
283
462
  it "should write a valid PUBLISH packet to the socket without the retain flag" do
284
- @client.publish('topic','payload', false, 0)
285
- @socket.string.should == "\x30\x0e\x00\x05topicpayload"
463
+ client.publish('topic','payload', false, 0)
464
+ socket.string.should == "\x30\x0e\x00\x05topicpayload"
286
465
  end
287
466
 
288
467
  it "should write a valid PUBLISH packet to the socket with the retain flag set" do
289
- @client.publish('topic','payload', true, 0)
290
- @socket.string.should == "\x31\x0e\x00\x05topicpayload"
468
+ client.publish('topic','payload', true, 0)
469
+ socket.string.should == "\x31\x0e\x00\x05topicpayload"
291
470
  end
292
471
 
293
472
  it "should write a valid PUBLISH packet to the socket with the QOS set to 1" do
294
- @client.publish('topic','payload', false, 1)
295
- @socket.string.should == "\x32\x10\x00\x05topic\x00\x01payload"
473
+ client.publish('topic','payload', false, 1)
474
+ socket.string.should == "\x32\x10\x00\x05topic\x00\x01payload"
296
475
  end
297
476
 
298
477
  it "should write a valid PUBLISH packet to the socket with the QOS set to 2" do
299
- @client.publish('topic','payload', false, 2)
300
- @socket.string.should == "\x34\x10\x00\x05topic\x00\x01payload"
478
+ client.publish('topic','payload', false, 2)
479
+ socket.string.should == "\x34\x10\x00\x05topic\x00\x01payload"
301
480
  end
302
481
  end
303
482
 
304
483
  describe "when calling the 'subscribe' method" do
305
484
  before(:each) do
306
- @client.instance_variable_set(:@socket, @socket)
485
+ client.instance_variable_set('@socket', socket)
307
486
  end
308
487
 
309
488
  it "should write a valid SUBSCRIBE packet to the socket if given a single topic String" do
310
- @client.subscribe('a/b')
311
- @socket.string.should == "\x82\x08\x00\x01\x00\x03a/b\x00"
489
+ client.subscribe('a/b')
490
+ socket.string.should == "\x82\x08\x00\x01\x00\x03a/b\x00"
312
491
  end
313
492
 
314
493
  it "should write a valid SUBSCRIBE packet to the socket if given a two topic Strings in an Array" do
315
- @client.subscribe('a/b','c/d')
316
- @socket.string.should == "\x82\x0e\x00\x01\x00\x03a/b\x00\x00\x03c/d\x00"
494
+ client.subscribe('a/b','c/d')
495
+ socket.string.should == "\x82\x0e\x00\x01\x00\x03a/b\x00\x00\x03c/d\x00"
317
496
  end
318
497
 
319
498
  it "should write a valid SUBSCRIBE packet to the socket if given a two topic Strings with QoS in an Array" do
320
- @client.subscribe(['a/b',0],['c/d',1])
321
- @socket.string.should == "\x82\x0e\x00\x01\x00\x03a/b\x00\x00\x03c/d\x01"
499
+ client.subscribe(['a/b',0],['c/d',1])
500
+ socket.string.should == "\x82\x0e\x00\x01\x00\x03a/b\x00\x00\x03c/d\x01"
322
501
  end
323
502
 
324
503
  it "should write a valid SUBSCRIBE packet to the socket if given a two topic Strings with QoS in a Hash" do
325
- @client.subscribe('a/b' => 0,'c/d' => 1)
326
- @socket.string.should == "\x82\x0e\x00\x01\x00\x03a/b\x00\x00\x03c/d\x01"
504
+ client.subscribe('a/b' => 0,'c/d' => 1)
505
+ socket.string.should == "\x82\x0e\x00\x01\x00\x03a/b\x00\x00\x03c/d\x01"
327
506
  end
328
507
  end
329
508
 
330
509
  describe "when calling the 'queue_length' method" do
331
510
  it "should return 0 if there are no incoming messages waiting" do
332
- @client.queue_length.should == 0
511
+ client.queue_length.should == 0
333
512
  end
334
513
 
335
514
  it "should return 1 if there is one incoming message waiting" do
336
515
  inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
337
- @client.queue_length.should == 1
516
+ client.queue_length.should == 1
338
517
  end
339
518
 
340
519
  it "should return 2 if there are two incoming message waiting" do
341
520
  inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
342
521
  inject_packet(:topic => 'topic0', :payload => 'payload1', :qos => 0)
343
- @client.queue_length.should == 2
522
+ client.queue_length.should == 2
344
523
  end
345
524
  end
346
525
 
347
526
  describe "when calling the 'queue_emtpy?' method" do
348
527
  it "should return return true if there no incoming messages waiting" do
349
- @client.queue_empty?.should be_true
528
+ client.queue_empty?.should be_true
350
529
  end
351
530
 
352
531
  it "should return return false if there is an incoming messages waiting" do
353
532
  inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
354
- @client.queue_empty?.should be_false
533
+ client.queue_empty?.should be_false
355
534
  end
356
535
  end
357
536
 
358
537
  describe "when calling the 'get' method" do
359
538
  before(:each) do
360
- @client.instance_variable_set(:@socket, @socket)
539
+ client.instance_variable_set('@socket', socket)
361
540
  end
362
541
 
363
542
  it "should successfull receive a valid PUBLISH packet with a QoS 0" do
364
543
  inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
365
- topic,payload = @client.get
544
+ topic,payload = client.get
366
545
  topic.should == 'topic0'
367
546
  payload.should == 'payload0'
368
547
  end
369
548
 
370
549
  it "should successfull receive a valid PUBLISH packet with a QoS 1" do
371
550
  inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
372
- topic,payload = @client.get
551
+ topic,payload = client.get
373
552
  topic.should == 'topic1'
374
553
  payload.should == 'payload1'
375
- @client.queue_empty?.should be_true
554
+ client.queue_empty?.should be_true
376
555
  end
377
556
 
378
557
  context "with a block" do
@@ -380,7 +559,7 @@ describe MQTT::Client do
380
559
  inject_packet(:topic => 'topic0', :payload => 'payload0')
381
560
  inject_packet(:topic => 'topic1', :payload => 'payload1')
382
561
  payloads = []
383
- @client.get do |topic,payload|
562
+ client.get do |topic,payload|
384
563
  payloads << payload
385
564
  break if payloads.size > 1
386
565
  end
@@ -392,12 +571,12 @@ describe MQTT::Client do
392
571
 
393
572
  describe "when calling the 'get_packet' method" do
394
573
  before(:each) do
395
- @client.instance_variable_set(:@socket, @socket)
574
+ client.instance_variable_set('@socket', socket)
396
575
  end
397
576
 
398
577
  it "should successfull receive a valid PUBLISH packet with a QoS 0" do
399
578
  inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
400
- packet = @client.get_packet
579
+ packet = client.get_packet
401
580
  packet.class.should == MQTT::Packet::Publish
402
581
  packet.qos.should == 0
403
582
  packet.topic.should == 'topic0'
@@ -406,12 +585,12 @@ describe MQTT::Client do
406
585
 
407
586
  it "should successfull receive a valid PUBLISH packet with a QoS 1" do
408
587
  inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
409
- packet = @client.get_packet
588
+ packet = client.get_packet
410
589
  packet.class.should == MQTT::Packet::Publish
411
590
  packet.qos.should == 1
412
591
  packet.topic.should == 'topic1'
413
592
  packet.payload.should == 'payload1'
414
- @client.queue_empty?.should be_true
593
+ client.queue_empty?.should be_true
415
594
  end
416
595
 
417
596
  context "with a block" do
@@ -419,7 +598,7 @@ describe MQTT::Client do
419
598
  inject_packet(:topic => 'topic0', :payload => 'payload0')
420
599
  inject_packet(:topic => 'topic1', :payload => 'payload1')
421
600
  packets = []
422
- @client.get_packet do |packet|
601
+ client.get_packet do |packet|
423
602
  packets << packet
424
603
  break if packets.size > 1
425
604
  end
@@ -431,97 +610,98 @@ describe MQTT::Client do
431
610
 
432
611
  describe "when calling the 'unsubscribe' method" do
433
612
  before(:each) do
434
- @client.instance_variable_set(:@socket, @socket)
613
+ client.instance_variable_set('@socket', socket)
435
614
  end
436
615
 
437
616
  it "should write a valid UNSUBSCRIBE packet to the socket if given a single topic String" do
438
- @client.unsubscribe('a/b')
439
- @socket.string.should == "\xa2\x07\x00\x01\x00\x03a/b"
617
+ client.unsubscribe('a/b')
618
+ socket.string.should == "\xa2\x07\x00\x01\x00\x03a/b"
440
619
  end
441
620
 
442
621
  it "should write a valid UNSUBSCRIBE packet to the socket if given a two topic Strings" do
443
- @client.unsubscribe('a/b','c/d')
444
- @socket.string.should == "\xa2\x0c\x00\x01\x00\x03a/b\x00\x03c/d"
622
+ client.unsubscribe('a/b','c/d')
623
+ socket.string.should == "\xa2\x0c\x00\x01\x00\x03a/b\x00\x03c/d"
624
+ end
625
+
626
+ it "should write a valid UNSUBSCRIBE packet to the socket if given an array of Strings" do
627
+ client.unsubscribe(['a/b','c/d'])
628
+ socket.string.should == "\xa2\x0c\x00\x01\x00\x03a/b\x00\x03c/d"
445
629
  end
446
630
  end
447
631
 
448
632
  describe "when calling the 'receive_packet' method" do
449
633
  before(:each) do
450
- @client.instance_variable_set(:@socket, @socket)
451
- IO.stub(:select).and_return([[@socket], [], []])
452
- @read_queue = @client.instance_variable_get(:@read_queue)
634
+ client.instance_variable_set('@socket', socket)
635
+ IO.stub(:select).and_return([[socket], [], []])
636
+ @read_queue = client.instance_variable_get('@read_queue')
453
637
  @parent_thread = Thread.current[:parent] = double('Parent Thread')
454
638
  @parent_thread.stub(:raise)
455
639
  end
456
640
 
457
641
  it "should put PUBLISH messages on to the read queue" do
458
- @socket.write("\x30\x0e\x00\x05topicpayload")
459
- @socket.rewind
460
- @client.send(:receive_packet)
642
+ socket.write("\x30\x0e\x00\x05topicpayload")
643
+ socket.rewind
644
+ client.send(:receive_packet)
461
645
  @read_queue.size.should == 1
462
646
  end
463
647
 
464
648
  it "should not put other messages on to the read queue" do
465
- @socket.write("\x20\x02\x00\x00")
466
- @socket.rewind
467
- @client.send(:receive_packet)
649
+ socket.write("\x20\x02\x00\x00")
650
+ socket.rewind
651
+ client.send(:receive_packet)
468
652
  @read_queue.size.should == 0
469
653
  end
470
654
 
471
655
  it "should send a ping packet if one is due" do
472
656
  IO.should_receive(:select).and_return(nil)
473
- @client.instance_variable_set(:@last_pingreq, Time.at(0))
474
- @client.should_receive(:ping).once
475
- @client.send(:receive_packet)
657
+ client.instance_variable_set('@last_pingreq', Time.at(0))
658
+ client.should_receive(:ping).once
659
+ client.send(:receive_packet)
476
660
  end
477
661
 
478
662
  it "should close the socket if there is an exception" do
479
- @socket.should_receive(:close).once
663
+ socket.should_receive(:close).once
480
664
  MQTT::Packet.stub(:read).and_raise(MQTT::Exception)
481
- @client.send(:receive_packet)
665
+ client.send(:receive_packet)
482
666
  end
483
667
 
484
668
  it "should pass exceptions up to parent thread" do
485
669
  @parent_thread.should_receive(:raise).once
486
670
  MQTT::Packet.stub(:read).and_raise(MQTT::Exception)
487
- @client.send(:receive_packet)
671
+ client.send(:receive_packet)
488
672
  end
489
673
  end
490
674
 
491
675
  describe "generating a client identifier" do
492
676
  context "with default parameters" do
493
- before :all do
494
- @client_id = MQTT::Client.generate_client_id
495
- end
677
+ let(:client_id) { MQTT::Client.generate_client_id }
496
678
 
497
679
  it "should be less or equal to 23 characters long" do
498
- @client_id.length.should <= 23
680
+ client_id.length.should <= 23
499
681
  end
500
682
 
501
683
  it "should have a prefix of ruby_" do
502
- @client_id.should match(/^ruby_/)
684
+ client_id.should match(/^ruby_/)
503
685
  end
504
686
 
505
687
  it "should end in 16 characters of lowercase letters and numbers" do
506
- @client_id.should match(/_[a-z0-9]{16}$/)
688
+ client_id.should match(/_[a-z0-9]{16}$/)
507
689
  end
508
690
  end
509
691
 
510
692
  context "with an alternative prefix" do
511
- before :all do
512
- @client_id = MQTT::Client.generate_client_id('test_')
513
- end
693
+ let(:client_id) { MQTT::Client.generate_client_id('test_') }
514
694
 
515
695
  it "should be less or equal to 23 characters long" do
516
- @client_id.length.should <= 23
696
+ client_id.length.should <= 23
517
697
  end
518
698
 
519
699
  it "should have a prefix of test_" do
520
- @client_id.should match(/^test_/)
700
+ client_id.should match(/^test_/)
521
701
  end
522
702
 
523
703
  it "should end in 16 characters of lowercase letters and numbers" do
524
- @client_id.should match(/_[a-z0-9]{16}$/)
704
+ client_id.should match(/_[a-z0-9]{16}$/)
525
705
  end
526
706
  end
527
707
  end
@@ -530,7 +710,7 @@ describe MQTT::Client do
530
710
 
531
711
  def inject_packet(opts={})
532
712
  packet = MQTT::Packet::Publish.new(opts)
533
- @client.instance_variable_get('@read_queue').push(packet)
713
+ client.instance_variable_get('@read_queue').push(packet)
534
714
  end
535
715
 
536
716
  end