Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <qndefrecord.h>
#include <qndefnfctextrecord.h>
#include <qndefnfcurirecord.h>
QT_USE_NAMESPACE
Q_DECLARE_METATYPE(QNdefRecord::TypeNameFormat)
class tst_QNdefRecord : public QObject
{
Q_OBJECT
public:
tst_QNdefRecord();
~tst_QNdefRecord();
private slots:
void tst_record();
void tst_comparison();
void tst_comparison_data();
void tst_isRecordType();
void tst_textRecord_data();
void tst_textRecord();
void tst_textRecord_malformedPayload_data();
void tst_textRecord_malformedPayload();
void tst_uriRecord_data();
void tst_uriRecord();
void tst_ndefRecord_data();
void tst_ndefRecord();
void fromOtherType();
};
tst_QNdefRecord::tst_QNdefRecord()
{
}
tst_QNdefRecord::~tst_QNdefRecord()
{
}
void tst_QNdefRecord::tst_record()
{
// test empty record
{
QNdefRecord record;
QVERIFY(record.isEmpty());
QCOMPARE(record.typeNameFormat(), QNdefRecord::Empty);
QVERIFY(record.type().isEmpty());
QVERIFY(record.id().isEmpty());
QVERIFY(record.payload().isEmpty());
QVERIFY(!record.isRecordType<QNdefNfcTextRecord>());
QVERIFY(!record.isRecordType<QNdefNfcUriRecord>());
QCOMPARE(record, QNdefRecord());
QVERIFY(!(record != QNdefRecord()));
}
// test type name format
{
QNdefRecord record;
record.setTypeNameFormat(QNdefRecord::Empty);
QCOMPARE(record.typeNameFormat(), QNdefRecord::Empty);
record.setTypeNameFormat(QNdefRecord::NfcRtd);
QCOMPARE(record.typeNameFormat(), QNdefRecord::NfcRtd);
record.setTypeNameFormat(QNdefRecord::Mime);
QCOMPARE(record.typeNameFormat(), QNdefRecord::Mime);
record.setTypeNameFormat(QNdefRecord::Uri);
QCOMPARE(record.typeNameFormat(), QNdefRecord::Uri);
record.setTypeNameFormat(QNdefRecord::ExternalRtd);
QCOMPARE(record.typeNameFormat(), QNdefRecord::ExternalRtd);
record.setTypeNameFormat(QNdefRecord::Unknown);
QCOMPARE(record.typeNameFormat(), QNdefRecord::Unknown);
record.setTypeNameFormat(QNdefRecord::TypeNameFormat(6));
QCOMPARE(record.typeNameFormat(), QNdefRecord::Unknown);
record.setTypeNameFormat(QNdefRecord::TypeNameFormat(7));
QCOMPARE(record.typeNameFormat(), QNdefRecord::Unknown);
}
// test type
{
QNdefRecord record;
record.setType("test type");
QCOMPARE(record.type(), QByteArray("test type"));
}
// test id
{
QNdefRecord record;
record.setId("test id");
QCOMPARE(record.id(), QByteArray("test id"));
}
// test payload
{
QNdefRecord record;
record.setPayload("test payload");
QVERIFY(!record.isEmpty());
QVERIFY(!record.payload().isEmpty());
QCOMPARE(record.payload(), QByteArray("test payload"));
}
// test copy
{
QNdefRecord record;
record.setTypeNameFormat(QNdefRecord::ExternalRtd);
record.setType("qt-project.org:test-rtd");
record.setId("test id");
record.setPayload("test payload");
QNdefRecord ccopy(record);
QCOMPARE(record.typeNameFormat(), ccopy.typeNameFormat());
QCOMPARE(record.type(), ccopy.type());
QCOMPARE(record.id(), ccopy.id());
QCOMPARE(record.payload(), ccopy.payload());
QVERIFY(record == ccopy);
QVERIFY(!(record != ccopy));
QNdefRecord acopy;
acopy = record;
QCOMPARE(record.typeNameFormat(), acopy.typeNameFormat());
QCOMPARE(record.type(), acopy.type());
QCOMPARE(record.id(), acopy.id());
QCOMPARE(record.payload(), acopy.payload());
QVERIFY(record == acopy);
QVERIFY(!(record != acopy));
QVERIFY(record != QNdefRecord());
}
// test clear
{
QNdefRecord record;
record.setTypeNameFormat(QNdefRecord::NfcRtd);
record.setId("id");
record.setType("type");
record.setPayload("Some random data");
record.clear();
QCOMPARE(record.typeNameFormat(), QNdefRecord::Empty);
QVERIFY(record.id().isEmpty());
QVERIFY(record.type().isEmpty());
QVERIFY(record.payload().isEmpty());
QVERIFY(record.isEmpty());
}
}
void tst_QNdefRecord::tst_comparison()
{
QFETCH(QNdefRecord, lhs);
QFETCH(QNdefRecord, rhs);
QFETCH(bool, result);
QCOMPARE(lhs == rhs, result);
QCOMPARE(lhs != rhs, !result);
QCOMPARE(rhs == lhs, result);
QCOMPARE(rhs != lhs, !result);
}
void tst_QNdefRecord::tst_comparison_data()
{
QTest::addColumn<QNdefRecord>("lhs");
QTest::addColumn<QNdefRecord>("rhs");
QTest::addColumn<bool>("result");
QTest::newRow("default-constructed records") << QNdefRecord() << QNdefRecord() << true;
{
QNdefRecord rec;
rec.setTypeNameFormat(QNdefRecord::Empty);
QTest::newRow("default-constructed vs empty") << QNdefRecord() << rec << false;
}
QNdefRecord record;
record.setTypeNameFormat(QNdefRecord::NfcRtd);
record.setType("T");
record.setId("text-record");
record.setPayload("Some random text");
{
QNdefRecord lhs = record;
QNdefRecord rhs = record;
rhs.setTypeNameFormat(QNdefRecord::ExternalRtd);
QTest::newRow("typeNameFormat mismatch") << lhs << rhs << false;
}
{
QNdefRecord lhs = record;
QNdefRecord rhs = record;
rhs.setType("t");
QTest::newRow("type mismatch") << lhs << rhs << false;
}
{
QNdefRecord lhs = record;
QNdefRecord rhs = record;
rhs.setId("random id");
QTest::newRow("id mismatch") << lhs << rhs << false;
}
{
QNdefRecord lhs = record;
QNdefRecord rhs = record;
rhs.setPayload("another random text");
QTest::newRow("payload mismatch") << lhs << rhs << false;
}
{
QNdefRecord lhs = record;
QNdefRecord rhs = record;
QTest::newRow("same records") << lhs << rhs << true;
}
}
class PngNdefRecord : public QNdefRecord
{
public:
PngNdefRecord() : QNdefRecord(QNdefRecord::Mime, "image/png") { }
};
class JpegNdefRecord : public QNdefRecord
{
public:
JpegNdefRecord() : QNdefRecord(QNdefRecord::Mime, "image/jpeg") { }
};
void tst_QNdefRecord::tst_isRecordType()
{
QNdefRecord rec;
rec.setTypeNameFormat(QNdefRecord::Mime);
rec.setType("image/png");
QVERIFY(!rec.isRecordType<QNdefRecord>());
QVERIFY(!rec.isRecordType<JpegNdefRecord>());
QVERIFY(rec.isRecordType<PngNdefRecord>());
}
void tst_QNdefRecord::tst_textRecord_data()
{
QTest::addColumn<QString>("locale");
QTest::addColumn<QString>("text");
QTest::addColumn<bool>("utf8");
QTest::addColumn<QByteArray>("payload");
QTest::newRow("en_US utf8") << QString::fromLatin1("en_US")
<< QString::fromLatin1("Test String")
<< true
<< QByteArray::fromHex("05656E5F55535465737420537472696E67");
QTest::newRow("en_US utf16") << QString::fromLatin1("en_US")
<< QString::fromLatin1("Test String")
<< false
<< QByteArray::fromHex("85656E5F5553FEFF005400650073007400200053007400720069006E0067");
QTest::newRow("ja_JP utf8") << QString::fromLatin1("ja_JP")
<< QString::fromUtf8("\343\203\206\343\202\271\343\203\210\346"
"\226\207\345\255\227\345\210\227")
<< true
<< QByteArray::fromHex("056A615F4A50E38386E382B9E38388E69687E5AD97E58897");
QTest::newRow("ja_JP utf16") << QString::fromLatin1("ja_JP")
<< QString::fromUtf8("\343\203\206\343\202\271\343\203\210\346"
"\226\207\345\255\227\345\210\227")
<< false
<< QByteArray::fromHex("856A615F4A50FEFF30C630B930C865875B575217");
}
void tst_QNdefRecord::tst_textRecord()
{
QFETCH(QString, locale);
QFETCH(QString, text);
QFETCH(bool, utf8);
QFETCH(QByteArray, payload);
// test setters
{
QNdefNfcTextRecord record;
record.setLocale(locale);
record.setText(text);
record.setEncoding(utf8 ? QNdefNfcTextRecord::Utf8 : QNdefNfcTextRecord::Utf16);
QCOMPARE(record.typeNameFormat(), QNdefRecord::NfcRtd);
QCOMPARE(record.type(), QByteArray("T"));
QCOMPARE(record.payload(), payload);
QVERIFY(record != QNdefRecord());
}
// test getters
{
QNdefNfcTextRecord record;
record.setPayload(payload);
QCOMPARE(record.locale(), locale);
QCOMPARE(record.text(), text);
QCOMPARE(record.encoding() == QNdefNfcTextRecord::Utf8, utf8);
}
// test copy
{
QNdefRecord record;
record.setTypeNameFormat(QNdefRecord::NfcRtd);
record.setType("T");
record.setPayload(payload);
QVERIFY(!record.isRecordType<QNdefRecord>());
QVERIFY(record.isRecordType<QNdefNfcTextRecord>());
QVERIFY(!record.isRecordType<QNdefNfcUriRecord>());
QNdefNfcTextRecord textRecord(record);
QVERIFY(!textRecord.isEmpty());
QVERIFY(record == textRecord);
QCOMPARE(textRecord.locale(), locale);
QCOMPARE(textRecord.text(), text);
QCOMPARE(textRecord.encoding() == QNdefNfcTextRecord::Utf8, utf8);
}
}
void tst_QNdefRecord::tst_textRecord_malformedPayload_data()
{
QTest::addColumn<QByteArray>("payload");
// The payload layout for an nfc text record is:
// [status: 1 byte][language code: codeLength bytes][text: remaining bytes]
// status byte claims codeLength=2, so payload should have 3 bytes or more
QTest::newRow("codeLength=2-payload=1byte")
<< QByteArray::fromHex("02");
// status byte claims codeLength=5, but only 2 locale bytes follow
QTest::newRow("codeLength=5-payload=3bytes")
<< QByteArray::fromHex("05656e"); // 0x05 + "en"
// status byte claims codeLength=63 (maximum), but payload is only 1 byte
QTest::newRow("codeLength=63-payload=1byte")
<< QByteArray::fromHex("3f");
// utf16 flag set, codeLength=2, payload is only 2 bytes
QTest::newRow("utf16-codeLength=2-payload=2bytes")
<< QByteArray::fromHex("8265"); // 0x82 + 'e'
}
void tst_QNdefRecord::tst_textRecord_malformedPayload()
{
QFETCH(QByteArray, payload);
QNdefNfcTextRecord record;
record.setPayload(payload);
// Before the fix for QTBUG-147372, locale() and text() read past the end of
// the payload when codeLength > p.size()-1. ASAN would catch both calls here.
QCOMPARE(record.locale(), QString());
QCOMPARE(record.text(), QString());
}
void tst_QNdefRecord::tst_uriRecord_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<QByteArray>("payload");
QTest::newRow("http") << QString::fromLatin1("http://qt-project.org/")
<< QByteArray::fromHex("0371742d70726f6a6563742e6f72672f");
QTest::newRow("tel") << QString::fromLatin1("tel:+1234567890")
<< QByteArray::fromHex("052B31323334353637383930");
QTest::newRow("mailto") << QString::fromLatin1("mailto:test@example.com")
<< QByteArray::fromHex("0674657374406578616D706C652E636F6D");
QTest::newRow("urn") << QString::fromLatin1("urn:nfc:ext:qt-project.org:test")
<< QByteArray::fromHex("136E66633A6578743A71742D70726F6A6563742E6F72673A74657374");
}
void tst_QNdefRecord::tst_uriRecord()
{
QFETCH(QString, url);
QFETCH(QByteArray, payload);
// test setters
{
QNdefNfcUriRecord record;
record.setUri(QUrl(url));
QCOMPARE(record.typeNameFormat(), QNdefRecord::NfcRtd);
QCOMPARE(record.type(), QByteArray("U"));
QCOMPARE(record.payload(), payload);
QVERIFY(record != QNdefRecord());
}
// test getters
{
QNdefNfcUriRecord record;
record.setPayload(payload);
QCOMPARE(record.uri(), QUrl(url));
}
// test copy
{
QNdefRecord record;
record.setTypeNameFormat(QNdefRecord::NfcRtd);
record.setType("U");
record.setPayload(payload);
QVERIFY(!record.isRecordType<QNdefRecord>());
QVERIFY(!record.isRecordType<QNdefNfcTextRecord>());
QVERIFY(record.isRecordType<QNdefNfcUriRecord>());
QNdefNfcUriRecord uriRecord(record);
QVERIFY(!uriRecord.isEmpty());
QVERIFY(record == uriRecord);
QCOMPARE(uriRecord.uri(), QUrl(url));
}
}
void tst_QNdefRecord::tst_ndefRecord_data()
{
QTest::addColumn<QNdefRecord::TypeNameFormat>("typeNameFormat");
QTest::addColumn<QByteArray>("type");
QTest::newRow("NfcRtd:U") << QNdefRecord::NfcRtd << QByteArray("U");
QTest::newRow("NfcRtd:T") << QNdefRecord::NfcRtd << QByteArray("T");
QTest::newRow("Empty:BLAH") << QNdefRecord::Empty << QByteArray("BLAH");
QTest::newRow("Empty") << QNdefRecord::Empty << QByteArray("");
QTest::newRow("Unknown") << QNdefRecord::Unknown << QByteArray("BLAHfoo");
QTest::newRow("Mime") << QNdefRecord::Mime << QByteArray("foobar");
QTest::newRow("ExternalRtd") << QNdefRecord::ExternalRtd << QByteArray("");
QTest::newRow("Uri") << QNdefRecord::Uri << QByteArray("example.com/uri");
}
void tst_QNdefRecord::tst_ndefRecord()
{
QFETCH(QNdefRecord::TypeNameFormat, typeNameFormat);
QFETCH(QByteArray, type);
{
QNdefRecord record;
record.setTypeNameFormat(typeNameFormat);
record.setType(type);
QCOMPARE(record.typeNameFormat(), typeNameFormat);
QCOMPARE(record.type(), type);
}
}
void tst_QNdefRecord::fromOtherType()
{
QNdefRecord empty;
QNdefNfcTextRecord text;
text.setText("Hello, world!");
text.setLocale("en_US");
text.setEncoding(QNdefNfcTextRecord::Utf16);
QNdefNfcUriRecord uri;
uri.setUri(QUrl("http://qt-project.org/"));
// All constructors from incompatible type should yield records of
// the corresponding type with an empty payload
{
// text from empty
QNdefNfcTextRecord copy = empty;
QCOMPARE(copy.type(), "T");
QCOMPARE(copy.typeNameFormat(), QNdefRecord::NfcRtd);
QVERIFY(copy.payload().isEmpty());
}
{
// text from uri
QNdefNfcTextRecord copy = uri;
QCOMPARE(copy.type(), "T");
QCOMPARE(copy.typeNameFormat(), QNdefRecord::NfcRtd);
QVERIFY(copy.payload().isEmpty());
}
{
// uri from empty
QNdefNfcUriRecord copy = empty;
QCOMPARE(copy.type(), "U");
QCOMPARE(copy.typeNameFormat(), QNdefRecord::NfcRtd);
QVERIFY(copy.payload().isEmpty());
}
{
// uri from text
QNdefNfcUriRecord copy = text;
QCOMPARE(copy.type(), "U");
QCOMPARE(copy.typeNameFormat(), QNdefRecord::NfcRtd);
QVERIFY(copy.payload().isEmpty());
}
}
QTEST_MAIN(tst_QNdefRecord)
#include "tst_qndefrecord.moc"