// Copyright (C) 2018 Witekio. // Copyright (C) 2018 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include #include #include #include class tst_QCoapInternalRequest : public QObject { Q_OBJECT private Q_SLOTS: void requestToFrame_data(); void requestToFrame(); void parseUri_data(); void parseUri(); void urlOptions_data(); void urlOptions(); void invalidUrls_data(); void invalidUrls(); void isMulticast_data(); void isMulticast(); void parseBlockOption_data(); void parseBlockOption(); void createBlockOption_data(); void createBlockOption(); void initEmptyMessage_data(); void initEmptyMessage(); }; void tst_QCoapInternalRequest::requestToFrame_data() { QTest::addColumn("url"); QTest::addColumn("method"); QTest::addColumn("type"); QTest::addColumn("messageId"); QTest::addColumn("token"); QTest::addColumn("pduHeader"); QTest::addColumn("pduPayload"); QTest::newRow("request_with_option_and_payload") << QUrl("coap://10.20.30.40:5683/test") << QtCoap::Method::Get << QCoapRequest::Type::NonConfirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "5401dc504647f09bb474657374ff" << "Some payload"; QTest::newRow("request_domain") << QUrl("coap://domain.com:5683/test") << QtCoap::Method::Get << QCoapRequest::Type::NonConfirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "5401dc504647f09b3a646f6d61696e2e636f6d8474657374ff" << "Some payload"; QTest::newRow("request_ipv6") << QUrl("coap://[::ffff:ac11:3]:5683/test") << QtCoap::Method::Get << QCoapRequest::Type::NonConfirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "5401dc504647f09bb474657374ff" << "Some payload"; QTest::newRow("request_without_payload") << QUrl("coap://10.20.30.40:5683/test") << QtCoap::Method::Get << QCoapRequest::Type::NonConfirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "5401dc504647f09bb474657374" << ""; QTest::newRow("request_without_option") << QUrl("coap://10.20.30.40:5683/") << QtCoap::Method::Put << QCoapRequest::Type::Confirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "4403dc504647f09bff" << "Some payload"; QTest::newRow("request_only") << QUrl("coap://10.20.30.40:5683/") << QtCoap::Method::Get << QCoapRequest::Type::NonConfirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "5401dc504647f09b" << ""; QTest::newRow("request_with_multiple_options") << QUrl("coap://10.20.30.40:5683/test/oui") << QtCoap::Method::Get << QCoapRequest::Type::NonConfirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "5401dc504647f09bb474657374036f7569" << ""; QTest::newRow("request_with_big_option_number") << QUrl("coap://10.20.30.40:5683/test") << QtCoap::Method::Get << QCoapRequest::Type::NonConfirmable << quint16(56400) << QByteArray::fromHex("4647f09b") << "5401dc504647f09bb474657374dd240d6162636465666768696a6b6c6d6e6f70" "7172737475767778797aff" << "Some payload"; } void tst_QCoapInternalRequest::requestToFrame() { QFETCH(QUrl, url); QFETCH(QtCoap::Method, method); QFETCH(QCoapMessage::Type, type); QFETCH(quint16, messageId); QFETCH(QByteArray, token); QFETCH(QString, pduHeader); QFETCH(QString, pduPayload); QCoapRequest request = QCoapRequestPrivate::createRequest(QCoapRequest(url), method); request.setType(type); request.setPayload(pduPayload.toUtf8()); request.setMessageId(messageId); request.setToken(token); if (qstrcmp(QTest::currentDataTag(), "request_with_big_option_number") == 0) request.addOption(QCoapOption::Size1, QByteArray("abcdefghijklmnopqrstuvwxyz")); QByteArray pdu; pdu.append(pduHeader.toUtf8()); if (!pduPayload.isEmpty()) pdu.append(pduPayload.toUtf8().toHex()); QCoapInternalRequest internalRequest(request); QCOMPARE(internalRequest.toQByteArray().toHex(), pdu); } void tst_QCoapInternalRequest::parseUri_data() { qRegisterMetaType>(); QTest::addColumn("uri"); QTest::addColumn("proxyUri"); QTest::addColumn>("options"); QTest::newRow("port_path") << QUrl("coap://10.20.30.40:1234/test/path1") << QUrl() << QList({ QCoapOption(QCoapOption::UriPort, 1234), QCoapOption(QCoapOption::UriPath, QByteArray("test")), QCoapOption(QCoapOption::UriPath, QByteArray("path1")) }); QTest::newRow("path_query") << QUrl("coap://10.20.30.40/test/path1/?rd=25&nd=4") << QUrl() << QList({ QCoapOption(QCoapOption::UriPath, QByteArray("test")), QCoapOption(QCoapOption::UriPath, QByteArray("path1")), QCoapOption(QCoapOption::UriQuery, QByteArray("rd=25")), QCoapOption(QCoapOption::UriQuery, QByteArray("nd=4")) }); QTest::newRow("host_path_query") << QUrl("coap://aa.bb.cc.com:5683/test/path1/?rd=25&nd=4") << QUrl() << QList({ QCoapOption(QCoapOption::UriHost, QByteArray("aa.bb.cc.com")), QCoapOption(QCoapOption::UriPath, QByteArray("test")), QCoapOption(QCoapOption::UriPath, QByteArray("path1")), QCoapOption(QCoapOption::UriQuery, QByteArray("rd=25")), QCoapOption(QCoapOption::UriQuery, QByteArray("nd=4")) }); QTest::newRow("proxy_url") << QUrl("coap://aa.bb.cc.com:5683/test/path1/?rd=25&nd=4") << QUrl("coap://10.20.30.40/test:5684/othertest/path") << QList({ QCoapOption(QCoapOption::ProxyUri, QByteArray("coap://10.20.30.40/test:5684/othertest/path")) }); } void tst_QCoapInternalRequest::parseUri() { QFETCH(QUrl, uri); QFETCH(QUrl, proxyUri); QFETCH(QList, options); QCoapRequest request(uri, QCoapMessage::Type::NonConfirmable, proxyUri); QCoapInternalRequest internalRequest(request); for (QCoapOption opt : options) QVERIFY2(internalRequest.message()->options().contains(opt), "Missing option"); QCOMPARE(options.size(), internalRequest.message()->optionCount()); } void tst_QCoapInternalRequest::urlOptions_data() { QTest::addColumn("url"); QTest::addColumn>("options"); QList options = { { QCoapOption::UriHost, QByteArray("example.com") }, { QCoapOption::UriPath, QByteArray("~sensors") }, { QCoapOption::UriPath, QByteArray("temp.xml") } }; QTest::newRow("url_with_default_port") << "coap://example.com:5683/~sensors/temp.xml" << options; QTest::newRow("url_percent_encoding_uppercase") << "coap://EXAMPLE.com/%7Esensors/temp.xml" << options; QTest::newRow("url_with_no_port_uppercase") << "coap://EXAMPLE.com:/%7esensors/temp.xml" << options; QTest::newRow("url_with_dot_segments") << "coap://exaMPLE.com/%7esensors/../%7esensors//./temp.xml" << options; //! TODO Add more test URLs } void tst_QCoapInternalRequest::urlOptions() { QFETCH(QString, url); QFETCH(QList, options); const QCoapRequest request(url); const QCoapInternalRequest internalRequest(request); auto requestOptions = internalRequest.message()->options(); for (const auto& option : options) QVERIFY2(requestOptions.removeAll(option) > 0, "Missing option"); QVERIFY2(requestOptions.isEmpty(), "Fewer options were expected"); } void tst_QCoapInternalRequest::invalidUrls_data() { QTest::addColumn("url"); QTest::newRow("url_with_non_ascii") << QString("coap://example.com:5683/~sensors/%1temp.xml").arg(QChar(0x00A3)); QTest::newRow("url_no_scheme") << "example.com:5683/~sensors/temp.xml"; QTest::newRow("url_wrong_scheme") << "http://example.com:5683/~sensors/temp.xml"; //! TODO Add more test URLs } void tst_QCoapInternalRequest::invalidUrls() { QFETCH(QString, url); const QCoapRequest request(url); const QCoapInternalRequest internalRequest(request); QVERIFY(!internalRequest.isValid()); QVERIFY(internalRequest.message()->options().empty()); } void tst_QCoapInternalRequest::isMulticast_data() { QTest::addColumn("url"); QTest::addColumn("result"); QTest::newRow("ipv4_multicast") << QString("coap://224.0.1.187") << true; QTest::newRow("ipv4_multicast_resource") << QString("coap://224.0.1.187/path") << true; QTest::newRow("ipv6_multicast_link_local") << "coap://[ff02::fd]" << true; QTest::newRow("ipv6_multicast_site_local") << "coap://[ff05::fd]" << true; QTest::newRow("not_multicast") << QString("coap://127.0.0.1") << false; } void tst_QCoapInternalRequest::isMulticast() { QFETCH(QString, url); QFETCH(bool, result); const QCoapRequest request(url); const QCoapInternalRequest internalRequest(request); QCOMPARE(internalRequest.isMulticast(), result); } void tst_QCoapInternalRequest::parseBlockOption_data() { QTest::addColumn("value"); QTest::addColumn("blockNumber"); QTest::addColumn("hasNext"); QTest::addColumn("blockSize"); QTest::newRow("block_option_1byte_more_blocks") << QByteArray::fromHex("3B") << 3u << true << 128u; QTest::newRow("block_option_1byte_no_more_blocks") << QByteArray::fromHex("93") << 9u << false << 128u; QTest::newRow("block_option_2bytes_more_blocks") << QByteArray::fromHex("12A") << 18u << true << 64u; QTest::newRow("block_option_2bytes_no_more_blocks") << QByteArray::fromHex("132") << 19u << false << 64u; QTest::newRow("block_option_3bytes_more_blocks") << QByteArray::fromHex("3AB2A") << 15026u << true << 64u; QTest::newRow("block_option_3bytes_no_more_blocks") << QByteArray::fromHex("3AB22") << 15026u << false << 64u; } void tst_QCoapInternalRequest::parseBlockOption() { QFETCH(QByteArray, value); QFETCH(uint, blockNumber); QFETCH(bool, hasNext); QFETCH(uint, blockSize); QCoapInternalRequest request; request.addOption(QCoapOption::Block1, value); QCOMPARE(request.currentBlockNumber(), blockNumber); QCOMPARE(request.hasMoreBlocksToReceive(), hasNext); QCOMPARE(request.blockSize(), blockSize); } void tst_QCoapInternalRequest::createBlockOption_data() { QTest::addColumn("payload"); QTest::addColumn("blockNumber"); QTest::addColumn("blockSize"); QTest::addColumn("expectedOption"); QByteArray data; for (uint i = 0; i < 1024; ++i) data.append(0xF); QByteArray largeData; for (uint i = 0; i < 32; ++i) largeData.append(data); QTest::newRow("block1_option_1byte_more_blocks") << data << 3u << 64u << QCoapOption(QCoapOption::Block1, QByteArray::fromHex("3A")); QTest::newRow("block1_option_1byte_no_more_blocks") << data << 8u << 128u << QCoapOption(QCoapOption::Block1, QByteArray::fromHex("83")); QTest::newRow("block2_option_1byte") << data << 3u << 64u << QCoapOption(QCoapOption::Block2, QByteArray::fromHex("32")); QTest::newRow("block1_option_2bytes_more_blocks") << data << 29u << 32u << QCoapOption(QCoapOption::Block1, QByteArray::fromHex("1D9")); QTest::newRow("block1_option_2bytes_no_more_blocks") << data << 32u << 32u << QCoapOption(QCoapOption::Block1, QByteArray::fromHex("201")); QTest::newRow("block2_option_2bytes") << data << 29u << 32u << QCoapOption(QCoapOption::Block2, QByteArray::fromHex("1D1")); QTest::newRow("block1_option_3bytes_more_blocks") << largeData << 4096u << 4u << QCoapOption(QCoapOption::Block1, QByteArray::fromHex("10008")); QTest::newRow("block1_option_3bytes_no_more_blocks") << largeData << 8192u << 4u << QCoapOption(QCoapOption::Block1, QByteArray::fromHex("20000")); QTest::newRow("block2_option_3bytes") << largeData << 4096u << 4u << QCoapOption(QCoapOption::Block2, QByteArray::fromHex("10000")); } void tst_QCoapInternalRequest::createBlockOption() { QFETCH(QByteArray, payload); QFETCH(uint, blockNumber); QFETCH(uint, blockSize); QFETCH(QCoapOption, expectedOption); QCoapRequest request; request.setPayload(payload); QCoapInternalRequest internalRequest(request); if (expectedOption.name() == QCoapOption::Block1) internalRequest.setToSendBlock(blockNumber, blockSize); else if (expectedOption.name() == QCoapOption::Block2) internalRequest.setToRequestBlock(blockNumber, blockSize); else QFAIL("Incorrect option, the test expects Block1 or Block2 options."); QCOMPARE(internalRequest.message()->options().size(), 1); const auto option = internalRequest.message()->options().back(); QCOMPARE(option.name(), expectedOption.name()); QCOMPARE(option.opaqueValue(), expectedOption.opaqueValue()); } void tst_QCoapInternalRequest::initEmptyMessage_data() { QTest::addColumn("type"); QTest::addColumn("messageHeader"); QTest::newRow("acknowledge") << QCoapMessage::Type::Acknowledgment << QByteArray("6000002a"); QTest::newRow("reset") << QCoapMessage::Type::Reset << QByteArray("7000002a"); } void tst_QCoapInternalRequest::initEmptyMessage() { QFETCH(QCoapMessage::Type, type); QFETCH(QByteArray, messageHeader); // Populate the request with random data QCoapRequest request = QCoapRequestPrivate::createRequest(QCoapRequest("coap://test"), QtCoap::Method::Get); request.setVersion(1); request.setType(QCoapMessage::Type::Confirmable); request.setMessageId(111); request.setToken("token"); request.addOption(QCoapOption::ProxyUri); request.setPayload("payload"); QCoapInternalRequest emptyMessageRequest(request); emptyMessageRequest.initEmptyMessage(42, type); QCOMPARE(emptyMessageRequest.toQByteArray().toHex(), messageHeader); } QTEST_APPLESS_MAIN(tst_QCoapInternalRequest) #include "tst_qcoapinternalrequest.moc"