1 /**
2  *
3  * /home/tomas/workspace/mqtt-d/source/mqttd/tests.d
4  *
5  * Author:
6  * Tomáš Chaloupka <chalucha@gmail.com>
7  *
8  * Copyright (c) 2015 Tomáš Chaloupka
9  *
10  * Boost Software License 1.0 (BSL-1.0)
11  *
12  * Permission is hereby granted, free of charge, to any person or organization obtaining a copy
13  * of the software and accompanying documentation covered by this license (the "Software") to use,
14  * reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative
15  * works of the Software, and to permit third-parties to whom the Software is furnished to do so,
16  * all subject to the following:
17  *
18  * The copyright notices in the Software and this entire statement, including the above license
19  * grant, this restriction and the following disclaimer, must be included in all copies of the Software,
20  * in whole or in part, and all derivative works of the Software, unless such copies or derivative works
21  * are solely in the form of machine-executable object code generated by a source language processor.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
24  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
25  * PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
26  * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT,
27  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28  * OTHER DEALINGS IN THE SOFTWARE.
29  */
30 module mqttd.tests;
31 
32 version (unittest):
33 
34 import std.array;
35 import std.stdio;
36 import mqttd;
37 import mqttd.serialization;
38 
39 /// ubyte tests
40 unittest
41 {
42 	ubyte id = 10;
43 	auto wr = serializer(appender!(ubyte[]));
44 	wr.write(id);
45 
46 	assert(wr.data.length == 1);
47 	assert(wr.data[0] == 0x0A);
48 
49 	id = 0x2B;
50 	wr.clear();
51 	wr.write(id);
52 
53 	assert(wr.data.length == 1);
54 	assert(wr.data[0] == 0x2B);
55 
56 	id = deserializer([0x11]).read!ubyte();
57 	assert(id == 0x11);
58 
59 	id = [0x22].deserializer.read!ubyte();
60 	assert(id == 0x22);
61 }
62 
63 /// ushort tests
64 unittest
65 {
66 	ushort id = 1;
67 	auto wr = serializer(appender!(ubyte[]));
68 	wr.write(id);
69 
70 	assert(wr.data.length == 2);
71 	assert(wr.data[0] == 0);
72 	assert(wr.data[1] == 1);
73 
74 	id = 0x1A2B;
75 	wr.clear();
76 	wr.write(id);
77 
78 	assert(wr.data.length == 2);
79 	assert(wr.data[0] == 0x1A);
80 	assert(wr.data[1] == 0x2B);
81 
82 	id = [0x11, 0x22].deserializer.read!ushort();
83 	assert(id == 0x1122);
84 }
85 
86 /// string tests
87 unittest
88 {
89 	import std..string : representation;
90 
91 	auto name = "test";
92 	auto wr = serializer(appender!(ubyte[]));
93 	wr.write(name);
94 
95 	assert(wr.data.length == 6);
96 	assert(wr.data[0] == 0);
97 	assert(wr.data[1] == 4);
98 	assert(wr.data[2..$] == "test".representation);
99 
100 	name = (cast(ubyte[])[0x00, 0x0A] ~ "randomname".representation).deserializer.read!string();
101 	assert(name == "randomname");
102 }
103 
104 /// Fixed header tests
105 unittest
106 {
107 	assert(FixedHeader(PacketType.RESERVED1, true, QoSLevel.Reserved, true) == 0x0F);
108 
109 	FixedHeader header = 0x0F;
110 	assert(header.type == PacketType.RESERVED1);
111 	assert(header.dup);
112 	assert(header.retain);
113 	assert(header.qos == QoSLevel.Reserved);
114 
115 	header = FixedHeader(PacketType.CONNECT, 0x0F, 255);
116 
117 	auto wr = serializer(appender!(ubyte[]));
118 	wr.write(header);
119 
120 	assert(wr.data.length == 3);
121 	assert(wr.data[0] == 0x1F);
122 	assert(wr.data[1] == 0xFF);
123 	assert(wr.data[2] == 0x01);
124 
125 	header.length = 10;
126 	wr.clear();
127 	wr.write(header);
128 	assert(wr.data.length == 2);
129 	assert(wr.data[0] == 0x1F);
130 	assert(wr.data[1] == 0x0A);
131 
132 	header = [0x1F, 0x0A].deserializer.read!FixedHeader();
133 	assert(header.type == PacketType.CONNECT);
134 	assert(header.flags == 0x1F);
135 	assert(header.length == 10);
136 
137 	header = [0x20, 0x80, 0x02].deserializer.read!FixedHeader();
138 	assert(header.type == PacketType.CONNACK);
139 	assert(header.flags == 0x20);
140 	assert(header.length == 256);
141 }
142 
143 /// ConnectFlags test
144 unittest
145 {
146 	ConnectFlags flags = ConnectFlags(128);
147 
148 	auto wr = serializer(appender!(ubyte[]));
149 	wr.write(flags);
150 
151 	assert(wr.data.length == 1);
152 	assert(wr.data[0] == 128);
153 
154 	flags = [2].deserializer.read!ConnectFlags();
155 	assert(flags.cleanSession);
156 }
157 
158 /// Connect message tests
159 unittest
160 {
161 	ubyte[] data = [
162 		0x10, //fixed header
163 		0x1c, // rest is 28
164 		0x00, 0x04, //length of MQTT text
165 		'M', 'Q', 'T', 'T', // MQTT
166 		0x04, //protocol level
167 		0x80, //just user name flag
168 		0x00, 0x00, //zero keepalive
169 		0x00, 0x0a, //length of client identifier
170 		't', 'e', 's', 't', 'c', 'l', 'i', 'e', 'n', 't', //testclient text
171 		0x00, 0x04, //username length
172 		'u', 's', 'e', 'r' //user text
173 	];
174 
175 	auto con = Connect();
176 	con.clientIdentifier = "testclient";
177 	con.flags.userName = true;
178 	con.userName = "user";
179 
180 	auto wr = appender!(ubyte[]);
181 	wr.serialize(con);
182 
183 	assert(wr.data.length == 30);
184 
185 	//debug writefln("%(%.02x %)", wr.data);
186 	assert(wr.data == data);
187 
188 	auto con2 = wr.data.deserialize!Connect();
189 	//auto con2 = deserialize!Connect(data);
190 	assert(con == con2);
191 }
192 
193 unittest
194 {
195 	ubyte[] data = [
196 		0x20, //fixed header
197 		0x02, //rest is 2
198 		0x00, //flags
199 		0x00  //return code
200 	];
201 	ubyte[] data2 = [
202 		0x20, //fixed header
203 		0x02, //rest is 2
204 		0x01, //flags
205 		0x05  //return code
206 	];
207 
208 	auto conack = ConnAck();
209 
210 	auto wr = appender!(ubyte[]);
211 	wr.serialize(conack);
212 
213 	assert(wr.data.length == 4);
214 
215 	//debug writefln("%(%.02x %)", wr.data);
216 	assert(wr.data == data);
217 
218 	auto conack2 = wr.data.deserialize!ConnAck();
219 
220 	// TODO: this for some reason fails..
221 	//    writefln("%(%.02x %)", *(cast(byte[ConnAck.sizeof]*)(&conack)));
222 	//    writefln("%(%.02x %)", *(cast(byte[ConnAck.sizeof]*)(&conack2)));
223 	//    assert(conack == conack2);
224 	assert(conack.header == conack2.header);
225 	assert(conack.flags == conack2.flags);
226 	assert(conack.returnCode == conack2.returnCode);
227 	assert(conack.returnCode == ConnectReturnCode.ConnectionAccepted);
228 
229 	conack2.flags = 0x01;
230 	conack2.returnCode = ConnectReturnCode.NotAuthorized;
231 	wr.clear();
232 
233 	wr.serialize(conack2);
234 
235 	assert(wr.data.length == 4);
236 	assert(wr.data == data2);
237 }
238 
239 unittest
240 {
241 	ubyte[] data = [
242 		0x33, //fixed header
243 		0x12, //rest is 18
244 		0x00, 0x09, //topic length
245 		'/', 'r', 'o', 'o', 't', '/', 's', 'e', 'c', //filter text
246 		0xab, 0xcd,  //packet id
247 		0x01, 0x2, 0x3, 0x4, 0x5 //payload
248 	];
249 
250 	auto pub = Publish();
251 	pub.header.qos = QoSLevel.QoS1;
252 	pub.header.retain = true;
253 	pub.packetId = 0xabcd;
254 	pub.topic = "/root/sec";
255 	pub.payload = [1, 2, 3, 4, 5];
256 
257 	auto wr = appender!(ubyte[]).serialize(pub);
258 
259 	// debug writefln("%(%.02x %)", wr.data);
260 	assert(wr.data.length == 20);
261 
262 	assert(wr.data == data);
263 
264 	auto pub2 = wr.data.deserialize!Publish();
265 	assert(pub == pub2);
266 }
267 
268 unittest
269 {
270 	void testPubx(T)(ubyte header)
271 	{
272 		ubyte[] data = [
273 			header, //fixed header
274 			0x02, //rest is 2
275 			0x00, 0x00  //packet id
276 		];
277 
278 		ubyte[] data2 = [
279 			header, //fixed header
280 			0x02, //rest is 2
281 			0xab, 0xcd  //packet id
282 		];
283 
284 		auto px = T();
285 
286 		auto wr = appender!(ubyte[]);
287 		wr.serialize(px);
288 
289 		assert(wr.data.length == 4);
290 
291 		//debug writefln("%(%.02x %)", wr.data);
292 		assert(wr.data == data);
293 
294 		auto px2 = wr.data.deserialize!T();
295 
296 		//TODO: Fails but are same
297 		//assert(px == px2);
298 		assert(px.header == px2.header);
299 		assert(px.packetId == px2.packetId);
300 
301 		px2.packetId = 0xabcd;
302 		wr.clear();
303 
304 		wr.serialize(px2);
305 
306 		assert(wr.data.length == 4);
307 		assert(wr.data == data2);
308 	}
309 
310 	testPubx!PubAck(0x40);
311 	testPubx!PubRec(0x50);
312 	testPubx!PubRel(0x62);
313 	testPubx!PubComp(0x70);
314 	testPubx!UnsubAck(0xb0);
315 }
316 
317 unittest
318 {
319 	ubyte[] data = [
320 		0x82, //fixed header
321 		0x0c, //rest is 12
322 		0xab, 0xcd,  //packet id
323 		0x00, 0x07, //filter length
324 		'/', 'r', 'o', 'o', 't', '/', '*', //filter text
325 		0x02 //qos
326 	];
327 
328 	auto sub = Subscribe();
329 	sub.packetId = 0xabcd;
330 	sub.topics ~= Topic("/root/*", QoSLevel.QoS2);
331 
332 	auto wr = appender!(ubyte[]);
333 	wr.serialize(sub);
334 
335 	assert(wr.data.length == 14);
336 
337 	//debug writefln("%(%.02x %)", wr.data);
338 	assert(wr.data == data);
339 
340 	auto sub2 = wr.data.deserialize!Subscribe();
341 	assert(sub == sub2);
342 }
343 
344 unittest
345 {
346 	ubyte[] data = [
347 		0x90, //fixed header
348 		0x06, //rest is 2
349 		0xab, 0xcd,  //packet id
350 		0x00, 0x01, 0x02, 0x80 //ret codes
351 	];
352 
353 	auto suback = SubAck();
354 	suback.packetId = 0xabcd;
355 	suback.returnCodes ~= QoSLevel.QoS0;
356 	suback.returnCodes ~= QoSLevel.QoS1;
357 	suback.returnCodes ~= QoSLevel.QoS2;
358 	suback.returnCodes ~= QoSLevel.Failure;
359 
360 	auto wr = appender!(ubyte[]);
361 	wr.serialize(suback);
362 
363 	assert(wr.data.length == 8);
364 
365 	//debug writefln("%(%.02x %)", wr.data);
366 	assert(wr.data == data);
367 
368 	auto suback2 = wr.data.deserialize!SubAck();
369 	assert(suback == suback2);
370 }
371 
372 unittest
373 {
374 	ubyte[] data = [
375 		0xa2, //fixed header
376 		0x0b, //rest is 11
377 		0xab, 0xcd,  //packet id
378 		0x00, 0x07, //filter length
379 		'/', 'r', 'o', 'o', 't', '/', '*' //filter text
380 	];
381 
382 	auto unsub = Unsubscribe();
383 	unsub.packetId = 0xabcd;
384 	unsub.topics ~= "/root/*";
385 
386 	auto wr = appender!(ubyte[]);
387 	wr.serialize(unsub);
388 
389 	//debug writefln("%(%.02x %)", wr.data);
390 	assert(wr.data.length == 13);
391 	assert(wr.data == data);
392 
393 	auto unsub2 = wr.data.deserialize!Unsubscribe();
394 	assert(unsub == unsub2);
395 }
396 
397 unittest
398 {
399 	void testSimple(T)(ubyte header)
400 	{
401 		auto s = T();
402 
403 		auto wr = appender!(ubyte[]);
404 		wr.serialize(s);
405 
406 		assert(wr.data.length == 2);
407 
408 		//debug writefln("%(%.02x %)", wr.data);
409 		assert(wr.data == cast(ubyte[])[header, 0x00]);
410 
411 		auto s2 = wr.data.deserialize!T();
412 
413 		assert(s.header == s2.header);
414 
415 		wr.clear();
416 		wr.serialize(s2);
417 
418 		assert(wr.data.length == 2);
419 		assert(wr.data == cast(ubyte[])[header, 0x00]);
420 	}
421 
422 	testSimple!PingReq(0xc0);
423 	testSimple!PingResp(0xd0);
424 	testSimple!Disconnect(0xe0);
425 }