1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNfc module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30
31#include <qndefrecord.h>
32#include <qndefmessage.h>
33#include <qndefnfctextrecord.h>
34#include <qndefnfcurirecord.h>
35
36QT_USE_NAMESPACE
37
38Q_DECLARE_METATYPE(QNdefRecord)
39
40class tst_QNdefMessage : public QObject
41{
42 Q_OBJECT
43
44public:
45 tst_QNdefMessage();
46 ~tst_QNdefMessage();
47
48private slots:
49 void tst_parse_data();
50 void tst_parse();
51 void messageParsingFromByteArray();
52};
53
54tst_QNdefMessage::tst_QNdefMessage()
55{
56}
57
58tst_QNdefMessage::~tst_QNdefMessage()
59{
60}
61
62void tst_QNdefMessage::tst_parse_data()
63{
64 QTest::addColumn<QByteArray>(name: "data");
65 QTest::addColumn<QNdefMessage>(name: "message");
66 QTest::addColumn<QVariantList>(name: "expectedData");
67
68 // empty message
69 {
70 QByteArray data;
71 data.append(c: char(0xc0)); // MB=1, ME=1
72 data.append(c: char(0)); // TYPE LENGTH
73 data.append(c: char(0)); // PAYLOAD LENGTH 3
74 data.append(c: char(0)); // PAYLOAD LENGTH 2
75 data.append(c: char(0)); // PAYLOAD LENGTH 1
76 data.append(c: char(0)); // PAYLOAD LENGTH 0
77 QTest::newRow(dataTag: "empty") << data << QNdefMessage() << QVariantList();
78
79 QNdefRecord record;
80 record.setTypeNameFormat(QNdefRecord::Empty);
81 QTest::newRow(dataTag: "empty record") << data
82 << QNdefMessage(record)
83 << QVariantList();
84 }
85
86 // empty short message
87 {
88 QByteArray data;
89 data.append(c: char(0xd0)); // MB=1, ME=1, SR=1
90 data.append(c: char(0)); // TYPE LENGTH
91 data.append(c: char(0)); // PAYLOAD LENGTH
92 QTest::newRow(dataTag: "empty") << data << QNdefMessage() << QVariantList();
93
94 QNdefRecord record;
95 record.setTypeNameFormat(QNdefRecord::Empty);
96 QTest::newRow(dataTag: "empty record") << data
97 << QNdefMessage(record)
98 << QVariantList();
99 }
100
101 // unknown message
102 {
103 QByteArray type("type");
104 QByteArray id("id");
105 QByteArray payload("payload");
106
107 QByteArray data;
108 data.append(c: char(0xcd)); // MB=1, ME=1, IL=1, TNF=5
109 data.append(c: char(type.length())); // TYPE LENGTH
110 data.append(c: char((payload.length() >> 24) & 0xff)); // PAYLOAD LENGTH 3
111 data.append(c: char((payload.length() >> 16) & 0xff)); // PAYLOAD LENGTH 2
112 data.append(c: char((payload.length() >> 8) & 0xff)); // PAYLOAD LENGTH 1
113 data.append(c: char((payload.length() >> 0) & 0xff)); // PAYLOAD LENGTH 0
114 data.append(c: char(id.length())); // ID LENGTH
115 data.append(a: type);
116 data.append(a: id);
117 data.append(a: payload);
118
119 QNdefRecord record;
120 record.setTypeNameFormat(QNdefRecord::Unknown);
121 record.setType(type);
122 record.setId(id);
123 record.setPayload(payload);
124 QList<QNdefRecord> recordList;
125 recordList.append(t: record);
126 QTest::newRow(dataTag: "unknown") << data << QNdefMessage(recordList) << QVariantList();
127 }
128
129 // chunked message
130 {
131 QByteArray type("type");
132 QByteArray id("id");
133 QByteArray payload("payload");
134
135 QByteArray data;
136 data.append(c: char(0xbd)); // MB=1, CF=1, SR=1, IL=1, TNF=5
137 data.append(c: char(type.length())); // TYPE LENGTH
138 data.append(c: char(1)); // PAYLOAD LENGTH
139 data.append(c: char(id.length())); // ID LENGTH
140 data.append(a: type); // TYPE
141 data.append(a: id); // ID
142 data.append(c: payload.at(i: 0)); // PAYLOAD[0]
143
144 for (int i = 1; i < payload.length() - 1; ++i) {
145 data.append(c: char(0x36)); // CF=1, SR=1, TNF=6
146 data.append(c: char(0)); // TYPE LENGTH
147 data.append(c: char(1)); // PAYLOAD LENGTH
148 data.append(c: payload.at(i)); // PAYLOAD[i]
149 }
150
151 data.append(c: char(0x56)); // ME=1, SR=1, TNF=6
152 data.append(c: char(0)); // TYPE LENGTH
153 data.append(c: char(1)); // PAYLOAD LENGTH
154 data.append(c: payload.at(i: payload.length() - 1)); // PAYLOAD[length - 1]
155
156 QNdefRecord record;
157 record.setTypeNameFormat(QNdefRecord::Unknown);
158 record.setType(type);
159 record.setId(id);
160 record.setPayload(payload);
161 QList<QNdefRecord> recordList;
162 recordList.append(t: record);
163 QTest::newRow(dataTag: "chunked") << data << QNdefMessage(recordList) << QVariantList();
164
165 const QByteArray recordContent = record.type() + record.id()
166 + record.payload();
167 QCOMPARE(recordContent, QByteArray::fromHex(QByteArray("7479706569647061796c6f6164")));
168 }
169
170 // NFC-RTD Text
171 {
172 QByteArray type("T");
173 QByteArray payload;
174 payload.append(c: char(0x02)); // UTF-8, 2 byte language code
175 payload.append(s: "en");
176 payload.append(s: "Test String");
177
178 QByteArray data;
179 data.append(c: char(0xc1)); // MB=1, ME=1, IL=0, TNF=1
180 data.append(c: char(type.length())); // TYPE LENGTH
181 data.append(c: char((payload.length() >> 24) & 0xff)); // PAYLOAD LENGTH 3
182 data.append(c: char((payload.length() >> 16) & 0xff)); // PAYLOAD LENGTH 2
183 data.append(c: char((payload.length() >> 8) & 0xff)); // PAYLOAD LENGTH 1
184 data.append(c: char((payload.length() >> 0) & 0xff)); // PAYLOAD LENGTH 0
185 data.append(a: type);
186 data.append(a: payload);
187
188 QNdefRecord record;
189 record.setTypeNameFormat(QNdefRecord::NfcRtd);
190 record.setType("T");
191 record.setPayload("\002enTest String");
192 QList<QNdefRecord> recordList;
193 recordList.append(t: record);
194 QTest::newRow(dataTag: "nfc-rtd text") << data << QNdefMessage(recordList)
195 << (QVariantList() << QStringLiteral("Test String")
196 << QStringLiteral("en"));
197
198 const QByteArray recordContent = record.type() + record.id()
199 + record.payload();
200 QCOMPARE(recordContent,
201 QByteArray::fromHex(QByteArray("5402656e5465737420537472696e67")));
202 }
203
204 // NFC-RTD Text
205 {
206 QByteArray type("T");
207 QByteArray payload;
208 payload.append(c: char(0x02)); // UTF-8, 2 byte language code
209 payload.append(s: "ja");
210 payload.append(a: QByteArray::fromHex(hexEncoded: "e38386e382b9e38388e69687e5ad97e58897"));
211
212 QByteArray data;
213 data.append(c: char(0xc1)); // MB=1, ME=1, IL=0, TNF=1
214 data.append(c: char(type.length())); // TYPE LENGTH
215 data.append(c: char((payload.length() >> 24) & 0xff)); // PAYLOAD LENGTH 3
216 data.append(c: char((payload.length() >> 16) & 0xff)); // PAYLOAD LENGTH 2
217 data.append(c: char((payload.length() >> 8) & 0xff)); // PAYLOAD LENGTH 1
218 data.append(c: char((payload.length() >> 0) & 0xff)); // PAYLOAD LENGTH 0
219 data.append(a: type);
220 data.append(a: payload);
221
222 QNdefRecord record;
223 record.setTypeNameFormat(QNdefRecord::NfcRtd);
224 record.setType("T");
225 record.setPayload("\002ja" + QByteArray::fromHex(hexEncoded: "e38386e382b9e38388e69687e5ad97e58897"));
226 QList<QNdefRecord> recordList;
227 recordList.append(t: record);
228 QTest::newRow(dataTag: "nfc-rtd text ja")
229 << data << QNdefMessage(recordList)
230 << (QVariantList() << QString::fromUtf8(str: "\343\203\206\343\202\271\343\203\210\346\226"
231 "\207\345\255\227\345\210\227")
232 << QStringLiteral("ja"));
233
234 const QByteArray recordContent = record.type() + record.id()
235 + record.payload();
236 QCOMPARE(recordContent,
237 QByteArray::fromHex(QByteArray("54026a61e38386e382b9e38388e69687e5ad97e58897")));
238 }
239
240 // NFC-RTD URI
241 {
242 QByteArray type("U");
243 QByteArray payload;
244 payload.append(c: char(0x00));
245 payload.append(s: "http://qt-project.org/");
246
247 QByteArray data;
248 data.append(c: char(0xc1));
249 data.append(c: char(type.length()));
250 data.append(c: char((payload.length() >> 24) & 0xff)); // PAYLOAD LENGTH 3
251 data.append(c: char((payload.length() >> 16) & 0xff)); // PAYLOAD LENGTH 2
252 data.append(c: char((payload.length() >> 8) & 0xff)); // PAYLOAD LENGTH 1
253 data.append(c: char((payload.length() >> 0) & 0xff)); // PAYLOAD LENGTH 0
254 data.append(a: type);
255 data.append(a: payload);
256
257 QNdefRecord record;
258 record.setTypeNameFormat(QNdefRecord::NfcRtd);
259 record.setType("U");
260 record.setPayload(QByteArray("\000http://qt-project.org/", 23));
261 QList<QNdefRecord> recordList;
262 recordList.append(t: record);
263 QTest::newRow(dataTag: "nfc-rtd uri http://qt-project.org/")
264 << data << QNdefMessage(recordList)
265 << (QVariantList() << QUrl(QStringLiteral("http://qt-project.org/")));
266
267 const QByteArray recordContent = record.type() + record.id()
268 + record.payload();
269 QCOMPARE(recordContent,
270 QByteArray::fromHex(QByteArray("5500687474703a2f2f71742d70726f6a6563742e6f72672f")));
271 }
272
273 // NFC-RTD URI
274 {
275 QByteArray type("U");
276 QByteArray payload;
277 payload.append(c: char(0x03));
278 payload.append(s: "qt-project.org/");
279
280 QByteArray data;
281 data.append(c: char(0xc1));
282 data.append(c: char(type.length()));
283 data.append(c: char((payload.length() >> 24) & 0xff)); // PAYLOAD LENGTH 3
284 data.append(c: char((payload.length() >> 16) & 0xff)); // PAYLOAD LENGTH 2
285 data.append(c: char((payload.length() >> 8) & 0xff)); // PAYLOAD LENGTH 1
286 data.append(c: char((payload.length() >> 0) & 0xff)); // PAYLOAD LENGTH 0
287 data.append(a: type);
288 data.append(a: payload);
289
290 QNdefRecord record;
291 record.setTypeNameFormat(QNdefRecord::NfcRtd);
292 record.setType("U");
293 record.setPayload(QByteArray("\003qt-project.org/", 16));
294 QList<QNdefRecord> recordList;
295 recordList.append(t: record);
296 QTest::newRow(dataTag: "nfc-rtd uri abbrev http://qt-project.org/")
297 << data << QNdefMessage(recordList)
298 << (QVariantList() << QUrl(QStringLiteral("http://qt-project.org/")));
299
300 const QByteArray recordContent = record.type() + record.id()
301 + record.payload();
302 QCOMPARE(recordContent,
303 QByteArray::fromHex(QByteArray("550371742d70726f6a6563742e6f72672f")));
304 }
305
306 // NFC-RTD URI
307 {
308 QByteArray type("U");
309 QByteArray payload;
310 payload.append(c: char(0x05));
311 payload.append(s: "+1234567890");
312
313 QByteArray data;
314 data.append(c: char(0xc1));
315 data.append(c: char(type.length()));
316 data.append(c: char((payload.length() >> 24) & 0xff)); // PAYLOAD LENGTH 3
317 data.append(c: char((payload.length() >> 16) & 0xff)); // PAYLOAD LENGTH 2
318 data.append(c: char((payload.length() >> 8) & 0xff)); // PAYLOAD LENGTH 1
319 data.append(c: char((payload.length() >> 0) & 0xff)); // PAYLOAD LENGTH 0
320 data.append(a: type);
321 data.append(a: payload);
322
323 QNdefRecord record;
324 record.setTypeNameFormat(QNdefRecord::NfcRtd);
325 record.setType("U");
326 record.setPayload(QByteArray("\005+1234567890", 12));
327 QList<QNdefRecord> recordList;
328 recordList.append(t: record);
329 QTest::newRow(dataTag: "nfc-rtd uri tel:+1234567890")
330 << data << QNdefMessage(recordList)
331 << (QVariantList() << QUrl(QStringLiteral("tel:+1234567890")));
332
333 const QByteArray recordContent = record.type() + record.id()
334 + record.payload();
335 QCOMPARE(recordContent,
336 QByteArray::fromHex(QByteArray("55052b31323334353637383930")));
337 }
338
339 // Truncated message
340 {
341 QByteArray type("U");
342 QByteArray id("Test ID");
343 QByteArray payload;
344 payload.append(c: char(0x00));
345 payload.append(s: "http://qt-project.org/");
346 QByteArray data;
347 data.append(c: char(0xc9)); // MB=1, ME=1, IL=1
348
349 QTest::newRow(dataTag: "truncated 1") << data << QNdefMessage() << QVariantList();
350
351 data.append(c: char(type.length())); // TYPE LENGTH
352 QTest::newRow(dataTag: "truncated 2") << data << QNdefMessage() << QVariantList();
353
354 data.append(c: char((payload.length() >> 24) & 0xff)); // PAYLOAD LENGTH 3
355 QTest::newRow(dataTag: "truncated 3") << data << QNdefMessage() << QVariantList();
356
357 data.append(c: char((payload.length() >> 16) & 0xff)); // PAYLOAD LENGTH 2
358 QTest::newRow(dataTag: "truncated 4") << data << QNdefMessage() << QVariantList();
359
360 data.append(c: char((payload.length() >> 8) & 0xff)); // PAYLOAD LENGTH 1
361 QTest::newRow(dataTag: "truncated 5") << data << QNdefMessage() << QVariantList();
362
363 data.append(c: char((payload.length() >> 0) & 0xff)); // PAYLOAD LENGTH 0
364 QTest::newRow(dataTag: "truncated 6") << data << QNdefMessage() << QVariantList();
365
366 data.append(c: char(id.length())); // ID LENGTH
367 QTest::newRow(dataTag: "truncated 7") << data << QNdefMessage() << QVariantList();
368
369 data.append(a: type);
370 QTest::newRow(dataTag: "truncated 8") << data << QNdefMessage() << QVariantList();
371
372 data.append(a: id);
373 QTest::newRow(dataTag: "truncated 9") << data << QNdefMessage() << QVariantList();
374
375 payload.chop(n: 1);
376 data.append(a: payload);
377 QTest::newRow(dataTag: "truncated 10") << data << QNdefMessage() << QVariantList();
378 }
379}
380
381void tst_QNdefMessage::tst_parse()
382{
383 QFETCH(QByteArray, data);
384 QFETCH(QNdefMessage, message);
385 QFETCH(QVariantList, expectedData);
386
387 if (QByteArray(QTest::currentDataTag()).startsWith(c: "truncated "))
388 QTest::ignoreMessage(type: QtWarningMsg, message: "Unexpected end of message");
389
390 QNdefMessage parsedMessage = QNdefMessage::fromByteArray(message: data);
391
392 QVERIFY(parsedMessage == message);
393 QVERIFY(message == parsedMessage);
394
395 QNdefMessage reparsedMessage = QNdefMessage::fromByteArray(message: message.toByteArray());
396
397 QVERIFY(message == reparsedMessage);
398 QVERIFY(reparsedMessage == message);
399
400 for (int i = 0; i < message.count(); ++i) {
401 const QNdefRecord &record = message.at(i);
402 const QNdefRecord &parsedRecord = parsedMessage.at(i);
403
404 QCOMPARE(record.typeNameFormat(), parsedRecord.typeNameFormat());
405 QCOMPARE(record.type(), parsedRecord.type());
406 QCOMPARE(record.id(), parsedRecord.id());
407 QCOMPARE(record.payload(), parsedRecord.payload());
408 QCOMPARE(record.isEmpty(), parsedRecord.isEmpty());
409
410 if (record.isRecordType<QNdefNfcTextRecord>()) {
411 QNdefNfcTextRecord textRecord(record);
412 QNdefNfcTextRecord parsedTextRecord(parsedRecord);
413
414 QCOMPARE(textRecord.text(), parsedTextRecord.text());
415 QCOMPARE(textRecord.locale(), parsedTextRecord.locale());
416
417 if (expectedData.count() == 2) {
418 QCOMPARE(parsedTextRecord.text(), expectedData.at(0).toString());
419 QCOMPARE(parsedTextRecord.locale(), expectedData.at(1).toString());
420 }
421 } else if (record.isRecordType<QNdefNfcUriRecord>()) {
422 QNdefNfcUriRecord uriRecord(record);
423 QNdefNfcUriRecord parsedUriRecord(parsedRecord);
424
425 QCOMPARE(uriRecord.uri(), parsedUriRecord.uri());
426
427 if (expectedData.count() == 1)
428 QCOMPARE(parsedUriRecord.uri(), expectedData.at(0).toUrl());
429 } else if (record.isRecordType<QNdefRecord>()) {
430 QVERIFY(record.isEmpty());
431 }
432 }
433}
434
435void tst_QNdefMessage::messageParsingFromByteArray()
436{
437 const QByteArray reference("1234567890");
438 QNdefMessage message;
439 QNdefRecord first;
440 QVERIFY(first.isEmpty());
441 first.setTypeNameFormat(QNdefRecord::Uri);
442 QVERIFY(first.isEmpty());
443 first.setPayload(reference);
444 QCOMPARE(first.payload(), reference);
445 QVERIFY(!first.isEmpty());
446 QCOMPARE(first.typeNameFormat(), QNdefRecord::Uri);
447
448 message.append(t: first);
449
450 QNdefRecord second;
451
452 QCOMPARE(second.payload(), QByteArray());
453 QVERIFY(second.isEmpty());
454 QCOMPARE(second.typeNameFormat(), QNdefRecord::Empty);
455
456 message.append(t: second);
457
458 QByteArray result = message.toByteArray();
459 QNdefMessage messageCopy = QNdefMessage::fromByteArray(message: result);
460 QCOMPARE(messageCopy.size(), 2);
461
462 first = messageCopy.at(i: 0);
463 second = messageCopy.at(i: 1);
464
465 QCOMPARE(first.payload(), reference);
466 QVERIFY(!first.isEmpty());
467 QCOMPARE(first.typeNameFormat(), QNdefRecord::Uri);
468 QCOMPARE(second.payload(), QByteArray());
469 QVERIFY(second.isEmpty());
470 QCOMPARE(second.typeNameFormat(), QNdefRecord::Empty);
471
472}
473
474QTEST_MAIN(tst_QNdefMessage)
475
476#include "tst_qndefmessage.moc"
477
478

source code of qtconnectivity/tests/auto/qndefmessage/tst_qndefmessage.cpp