aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorAdrien Leravat <aleravat@witekio.com>2018-08-24 04:42:50 +0000
committerSona Kurazyan <sona.kurazyan@qt.io>2018-12-14 12:20:26 +0000
commit2b3755c8e6587c6a720dd8dcfaafca6e2ba755a9 (patch)
tree12c2287fba0edee83e5153ca6ab4574d14d78f23 /tests
parentaeb607e972af3be78cc25435a52a1fbd900fb8af (diff)
Add the CoAP module, providing a client for Qt
Features: - Send GET/POST/PUT/DELETE requests - Discover resources (single server) - Observe resources and cancel the observation - Blockwise requests and replies - Requests (and replies) can be confirmable or non-confirmable - Some options can be added to the request - Replies can be received in a separate or piggybacked message Change-Id: I31e0e20a4f19bdc6d6489281fde73a4ff848eda4 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/auto.pro14
-rw-r--r--tests/auto/cmake/CMakeLists.txt13
-rw-r--r--tests/auto/cmake/cmake.pro6
-rw-r--r--tests/auto/coapnetworksettings.h71
-rw-r--r--tests/auto/coaptestserver.pri7
-rw-r--r--tests/auto/qcoapclient/qcoapclient.pro8
-rw-r--r--tests/auto/qcoapclient/tst_qcoapclient.cpp723
-rw-r--r--tests/auto/qcoapconnection/qcoapconnection.pro8
-rw-r--r--tests/auto/qcoapconnection/tst_qcoapconnection.cpp185
-rw-r--r--tests/auto/qcoapinternalreply/qcoapinternalreply.pro4
-rw-r--r--tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp251
-rw-r--r--tests/auto/qcoapinternalrequest/qcoapinternalrequest.pro5
-rw-r--r--tests/auto/qcoapinternalrequest/tst_qcoapinternalrequest.cpp235
-rw-r--r--tests/auto/qcoapmessage/qcoapmessage.pro4
-rw-r--r--tests/auto/qcoapmessage/tst_qcoapmessage.cpp138
-rw-r--r--tests/auto/qcoapoption/qcoapoption.pro4
-rw-r--r--tests/auto/qcoapoption/tst_qcoapoption.cpp89
-rw-r--r--tests/auto/qcoapreply/qcoapreply.pro4
-rw-r--r--tests/auto/qcoapreply/tst_qcoapreply.cpp164
-rw-r--r--tests/auto/qcoaprequest/qcoaprequest.pro4
-rw-r--r--tests/auto/qcoaprequest/tst_qcoaprequest.cpp160
-rw-r--r--tests/auto/qcoapresource/qcoapresource.pro4
-rw-r--r--tests/auto/qcoapresource/tst_qcoapresource.cpp167
-rw-r--r--tests/tests.pro2
24 files changed, 2270 insertions, 0 deletions
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
new file mode 100644
index 0000000..a6f3a48
--- /dev/null
+++ b/tests/auto/auto.pro
@@ -0,0 +1,14 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ cmake \
+# TODO: enable the tests below, when CI is configured properly
+# qcoapclient \
+# qcoapconnection \
+ qcoapinternalreply \
+ qcoapinternalrequest \
+ qcoapmessage \
+ qcoapoption \
+ qcoapreply \
+ qcoaprequest \
+ qcoapresource
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
new file mode 100644
index 0000000..5f7dd02
--- /dev/null
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(qmake_cmake_files)
+
+enable_testing()
+
+find_package(Qt5Core REQUIRED)
+
+include("${_Qt5CTestMacros}")
+
+test_module_includes(
+ Coap QCoapClient
+)
diff --git a/tests/auto/cmake/cmake.pro b/tests/auto/cmake/cmake.pro
new file mode 100644
index 0000000..6e5acb5
--- /dev/null
+++ b/tests/auto/cmake/cmake.pro
@@ -0,0 +1,6 @@
+# Cause make to do nothing.
+TEMPLATE = subdirs
+
+CMAKE_QT_MODULES_UNDER_TEST = coap
+
+CONFIG += ctest_testcase
diff --git a/tests/auto/coapnetworksettings.h b/tests/auto/coapnetworksettings.h
new file mode 100644
index 0000000..0e1ee04
--- /dev/null
+++ b/tests/auto/coapnetworksettings.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QtCore/qstring.h>
+#include <QtNetwork/qhostinfo.h>
+
+/*!
+ \internal
+
+ This namespace provides URL and settings used in QtCoap tests.
+
+ Tests require a Californium plugtest server, accessible with
+ "coap-plugtest-server" host name. You create such server with Docker and
+ the following command line:
+ \code
+ docker run -d --rm -p 5683:5683/udp aleravat/coap-test-server:latest
+ \endcode
+
+ For more details, see
+ \l{https://github.com/Pixep/coap-testserver-docker}{https://github.com/Pixep/coap-testserver-docker}.
+*/
+namespace QtCoapNetworkSettings
+{
+ QString testServerHost()
+ {
+#if defined(COAP_TEST_SERVER_IP)
+ return QStringLiteral(COAP_TEST_SERVER_IP);
+#else
+ static_assert(false, "COAP_TEST_SERVER_IP variable must be set");
+#endif
+ }
+
+ QString testServerUrl()
+ {
+ return QStringLiteral("coap://") + testServerHost() + QStringLiteral(":")
+ + QString::number(QtCoap::DefaultPort);
+ }
+
+ QString testServerResource()
+ {
+ return testServerUrl() + QStringLiteral("/test");
+ }
+}
diff --git a/tests/auto/coaptestserver.pri b/tests/auto/coaptestserver.pri
new file mode 100644
index 0000000..420aa89
--- /dev/null
+++ b/tests/auto/coaptestserver.pri
@@ -0,0 +1,7 @@
+COAP_TEST_SERVER_IP = $$(COAP_TEST_SERVER_IP)
+isEmpty( COAP_TEST_SERVER_IP ) {
+ error(No IP set for CoAP plugtest server. Please set COAP_TEST_SERVER_IP environment variable to run this test.)
+}
+
+DEFINES += COAP_TEST_SERVER_IP=\\\"$${COAP_TEST_SERVER_IP}\\\"
+message(CoAP plugtest server IP set to $$COAP_TEST_SERVER_IP)
diff --git a/tests/auto/qcoapclient/qcoapclient.pro b/tests/auto/qcoapclient/qcoapclient.pro
new file mode 100644
index 0000000..5c7fe1e
--- /dev/null
+++ b/tests/auto/qcoapclient/qcoapclient.pro
@@ -0,0 +1,8 @@
+QT = testlib core-private network core coap coap-private
+CONFIG += testcase
+
+include(../coaptestserver.pri)
+
+HEADERS += ../coapnetworksettings.h
+
+SOURCES += tst_qcoapclient.cpp
diff --git a/tests/auto/qcoapclient/tst_qcoapclient.cpp b/tests/auto/qcoapclient/tst_qcoapclient.cpp
new file mode 100644
index 0000000..1e4dbe2
--- /dev/null
+++ b/tests/auto/qcoapclient/tst_qcoapclient.cpp
@@ -0,0 +1,723 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <QtCoap/qcoapclient.h>
+#include <QtCoap/qcoaprequest.h>
+#include <QtCoap/qcoapreply.h>
+#include <QtCoap/qcoapdiscoveryreply.h>
+#include <QtCore/qbuffer.h>
+#include <QtNetwork/qnetworkdatagram.h>
+#include <private/qcoapclient_p.h>
+#include <private/qcoapconnection_p.h>
+
+#include "../coapnetworksettings.h"
+
+using namespace QtCoapNetworkSettings;
+
+class tst_QCoapClient : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void incorrectUrls_data();
+ void incorrectUrls();
+ void methods_data();
+ void methods();
+ void separateMethod();
+ void socketError();
+ void timeout_data();
+ void timeout();
+ void abort();
+ void removeReply();
+ void setBlockSize_data();
+ void setBlockSize();
+ void requestWithQIODevice_data();
+ void requestWithQIODevice();
+ void multipleRequests();
+ void blockwiseReply_data();
+ void blockwiseReply();
+ void blockwiseRequest_data();
+ void blockwiseRequest();
+ void discover_data();
+ void discover();
+ void observe_data();
+ void observe();
+};
+
+class QCoapConnectionSocketTestsPrivate : public QCoapConnectionPrivate
+{
+ bool bind() override
+ {
+ // Force a socket binding error
+ QUdpSocket anotherSocket;
+ anotherSocket.bind(QHostAddress::Any, 6080);
+ return socket()->bind(QHostAddress::Any, 6080);
+ }
+};
+
+class QCoapConnectionSocketTests : public QCoapConnection
+{
+public:
+ QCoapConnectionSocketTests() :
+ QCoapConnection(*new QCoapConnectionSocketTestsPrivate)
+ {
+ createSocket();
+ }
+
+private:
+ Q_DECLARE_PRIVATE(QCoapConnectionSocketTests)
+};
+
+class QCoapClientForSocketErrorTests : public QCoapClient
+{
+public:
+ QCoapClientForSocketErrorTests() :
+ QCoapClient(new QCoapProtocol, new QCoapConnectionSocketTests)
+ {}
+
+ QCoapConnection *connection()
+ {
+ QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
+ return privateClient->connection;
+ }
+};
+
+class QCoapClientForTests : public QCoapClient
+{
+public:
+ QCoapClientForTests() {}
+ QCoapClientForTests(QCoapProtocol *protocol, QCoapConnection *connection) :
+ QCoapClient(protocol, connection)
+ {}
+
+ QCoapProtocol *protocol()
+ {
+ QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
+ return privateClient->protocol;
+ }
+ QCoapConnection *connection()
+ {
+ QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
+ return privateClient->connection;
+ }
+};
+
+class Helper : public QObject
+{
+ Q_OBJECT
+public:
+ Helper() {}
+
+public slots:
+ void onError(QCoapReply *, QtCoap::Error error)
+ {
+ qWarning() << "Network error" << error << "occurred";
+ }
+};
+
+void tst_QCoapClient::incorrectUrls_data()
+{
+ QWARN("Expect warnings here...");
+ QTest::addColumn<QUrl>("url");
+
+ QTest::newRow("get") << QUrl("wrong://10.20.30.40:5683/test");
+ QTest::newRow("post") << QUrl("wrong://10.20.30.40:5683/test");
+ QTest::newRow("put") << QUrl("wrong://10.20.30.40:5683/test");
+ QTest::newRow("delete") << QUrl("wrong://10.20.30.40:5683/test");
+ QTest::newRow("discover") << QUrl("wrong://10.20.30.40:5683/test");
+}
+
+void tst_QCoapClient::incorrectUrls()
+{
+ QFETCH(QUrl, url);
+
+ QCoapClient client;
+ QScopedPointer<QCoapReply> reply;
+
+ if (qstrcmp(QTest::currentDataTag(), "get") == 0)
+ reply.reset(client.get(url));
+ else if (qstrcmp(QTest::currentDataTag(), "post") == 0)
+ reply.reset(client.post(url));
+ else if (qstrcmp(QTest::currentDataTag(), "put") == 0)
+ reply.reset(client.put(url));
+ else if (qstrcmp(QTest::currentDataTag(), "delete") == 0)
+ reply.reset(client.deleteResource(url));
+ else if (qstrcmp(QTest::currentDataTag(), "discover") == 0)
+ reply.reset(client.discover(url));
+ else {
+ QString error = QLatin1Literal("Unrecognized method '") + QTest::currentDataTag() + "'";
+ QFAIL(qPrintable(error));
+ }
+
+ QVERIFY2(reply.isNull(), "Request did not fail as expected.");
+}
+
+void tst_QCoapClient::methods_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<QtCoap::Method>("method");
+
+ QTest::newRow("get_no_op") << QUrl(testServerResource()) << QtCoap::Invalid;
+ QTest::newRow("get") << QUrl(testServerResource()) << QtCoap::Get;
+ QTest::newRow("get_incorrect_op") << QUrl(testServerResource()) << QtCoap::Put;
+ QTest::newRow("get_no_port")
+ << QUrl("coap://" + testServerHost() + "/test") << QtCoap::Get;
+ QTest::newRow("get_no_scheme_no_port") << QUrl(testServerHost() + "/test") << QtCoap::Get;
+ QTest::newRow("post_no_op") << QUrl(testServerResource()) << QtCoap::Invalid;
+ QTest::newRow("post") << QUrl(testServerResource()) << QtCoap::Post;
+ QTest::newRow("post_incorrect_op") << QUrl(testServerResource()) << QtCoap::Delete;
+ QTest::newRow("put_no_op") << QUrl(testServerResource()) << QtCoap::Invalid;
+ QTest::newRow("put") << QUrl(testServerResource()) << QtCoap::Put;
+ QTest::newRow("put_incorrect_op") << QUrl(testServerResource()) << QtCoap::Post;
+ QTest::newRow("delete_no_op") << QUrl(testServerResource()) << QtCoap::Invalid;
+ QTest::newRow("delete") << QUrl(testServerResource()) << QtCoap::Delete;
+ QTest::newRow("delete_incorrect_op") << QUrl(testServerResource()) << QtCoap::Get;
+}
+
+void tst_QCoapClient::methods()
+{
+ QFETCH(QUrl, url);
+ QFETCH(QtCoap::Method, method);
+
+ QCoapClient client;
+
+ QCoapRequest request(url);
+ if (method != QtCoap::Invalid)
+ request.setMethod(method);
+
+ QSignalSpy spyClientFinished(&client, SIGNAL(finished(QCoapReply *)));
+
+ QScopedPointer<QCoapReply> reply;
+ if (qstrncmp(QTest::currentDataTag(), "get", 3) == 0)
+ reply.reset(client.get(request));
+ else if (qstrncmp(QTest::currentDataTag(), "post", 4) == 0)
+ reply.reset(client.post(request));
+ else if (qstrncmp(QTest::currentDataTag(), "put", 3) == 0)
+ reply.reset(client.put(request));
+ else if (qstrncmp(QTest::currentDataTag(), "delete", 6) == 0)
+ reply.reset(client.deleteResource(request));
+ else {
+ QString error = QLatin1Literal("Unrecognized method '") + QTest::currentDataTag() + "'";
+ QFAIL(qPrintable(error));
+ }
+
+ QVERIFY2(!reply.isNull(), "Request failed unexpectedly");
+ QSignalSpy spyReplyFinished(reply.data(), SIGNAL(finished(QCoapReply *)));
+ QTRY_COMPARE(spyReplyFinished.count(), 1);
+ QTRY_COMPARE(spyClientFinished.count(), 1);
+
+ QByteArray replyData;
+ if (!reply.isNull()) {
+ replyData = reply->readAll();
+ if (qstrncmp(QTest::currentDataTag(), "get", 3) == 0) {
+ QVERIFY(!replyData.isEmpty());
+ QCOMPARE(reply->responseCode(), QtCoap::Content);
+ } else if (qstrncmp(QTest::currentDataTag(), "post", 4) == 0) {
+ QVERIFY(replyData.isEmpty());
+ QCOMPARE(reply->responseCode(), QtCoap::Created);
+ } else if (qstrncmp(QTest::currentDataTag(), "put", 3) == 0) {
+ QVERIFY(replyData.isEmpty());
+ QCOMPARE(reply->responseCode(), QtCoap::Changed);
+ } else if (qstrncmp(QTest::currentDataTag(), "delete", 6) == 0) {
+ QVERIFY(replyData.isEmpty());
+ QCOMPARE(reply->responseCode(), QtCoap::Deleted);
+ } else {
+ QString error = QLatin1Literal("Unrecognized method '") + QTest::currentDataTag() + "'";
+ QFAIL(qPrintable(error));
+ }
+ }
+}
+
+void tst_QCoapClient::separateMethod()
+{
+ QCoapClient client;
+ QScopedPointer<QCoapReply> reply(client.get(QUrl(testServerUrl() + "/separate")));
+
+ QVERIFY2(!reply.isNull(), "Request failed unexpectedly");
+ QSignalSpy spyReplyFinished(reply.data(), SIGNAL(finished(QCoapReply *)));
+ QTRY_COMPARE(spyReplyFinished.count(), 1);
+
+ QByteArray replyData = reply->readAll();
+
+ QVERIFY(!replyData.isEmpty());
+ QCOMPARE(reply->responseCode(), QtCoap::Content);
+}
+
+void tst_QCoapClient::removeReply()
+{
+ QCoapClient client;
+ QCoapReply *reply = client.get(QUrl(testServerResource()));
+ QVERIFY2(reply != nullptr, "Request failed unexpectedly");
+
+ try {
+ reply->deleteLater();
+
+ QEventLoop eventLoop;
+ QTimer::singleShot(2000, &eventLoop, &QEventLoop::quit);
+ eventLoop.exec();
+ } catch (...) {
+ QFAIL("Exception occurred after destroying the QCoapReply");
+ }
+}
+
+void tst_QCoapClient::setBlockSize_data()
+{
+ QTest::addColumn<int>("blockSizeSet");
+ QTest::addColumn<int>("blockSizeExpected");
+
+ QTest::newRow("valid_size_0") << 0 << 0;
+ QTest::newRow("valid_size_16") << 16 << 16;
+ QTest::newRow("valid_size_1024") << 1024 << 1024;
+ QTest::newRow("invalid_size_8") << 8 << 0;
+ QTest::newRow("invalid_size_350") << 350 << 0;
+ QTest::newRow("invalid_size_2048") << 2048 << 0;
+}
+
+void tst_QCoapClient::setBlockSize()
+{
+ QFETCH(int, blockSizeSet);
+ QFETCH(int, blockSizeExpected);
+
+ QCoapClientForTests client;
+ client.setBlockSize(blockSizeSet);
+
+ QEventLoop eventLoop;
+ QTimer::singleShot(1000, &eventLoop, &QEventLoop::quit);
+ eventLoop.exec();
+
+ QCOMPARE(client.protocol()->blockSize(), blockSizeExpected);
+}
+
+void tst_QCoapClient::requestWithQIODevice_data()
+{
+ QTest::addColumn<QUrl>("url");
+
+ QTest::newRow("post") << QUrl(testServerResource());
+ QTest::newRow("put") << QUrl(testServerResource());
+}
+
+void tst_QCoapClient::requestWithQIODevice()
+{
+ QFETCH(QUrl, url);
+
+ QCoapClient client;
+ QCoapRequest request(url);
+
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadWrite);
+ buffer.write("Some data");
+
+ QScopedPointer<QCoapReply> reply;
+ if (qstrcmp(QTest::currentDataTag(), "post") == 0)
+ reply.reset(client.post(request, &buffer));
+ else
+ reply.reset(client.put(request, &buffer));
+
+ QVERIFY2(!reply.isNull(), "Request failed unexpectedly");
+ QSignalSpy spyReplyFinished(reply.data(), SIGNAL(finished(QCoapReply *)));
+ QTRY_COMPARE(spyReplyFinished.count(), 1);
+
+ QByteArray replyData = reply->readAll();
+
+ if (qstrcmp(QTest::currentDataTag(), "post") == 0) {
+ QVERIFY(replyData.isEmpty());
+ QCOMPARE(reply->responseCode(), QtCoap::Created);
+ } else if (qstrcmp(QTest::currentDataTag(), "put") == 0) {
+ QVERIFY(replyData.isEmpty());
+ QCOMPARE(reply->responseCode(), QtCoap::Changed);
+ }
+}
+
+void tst_QCoapClient::multipleRequests()
+{
+ QCoapClient client;
+ QUrl url = QUrl(testServerResource());
+ QSignalSpy spyClientFinished(&client, SIGNAL(finished(QCoapReply *)));
+
+ QScopedPointer<QCoapReply> replyGet1(client.get(url));
+ QScopedPointer<QCoapReply> replyGet2(client.get(url));
+ QScopedPointer<QCoapReply> replyGet3(client.get(url));
+ QScopedPointer<QCoapReply> replyGet4(client.get(url));
+
+ QVERIFY2(!replyGet1.isNull(), "Request failed unexpectedly");
+ QVERIFY2(!replyGet2.isNull(), "Request failed unexpectedly");
+ QVERIFY2(!replyGet3.isNull(), "Request failed unexpectedly");
+ QVERIFY2(!replyGet4.isNull(), "Request failed unexpectedly");
+
+ QSignalSpy spyReplyGet1Finished(replyGet1.data(), SIGNAL(finished(QCoapReply *)));
+ QSignalSpy spyReplyGet2Finished(replyGet2.data(), SIGNAL(finished(QCoapReply *)));
+ QSignalSpy spyReplyGet3Finished(replyGet3.data(), SIGNAL(finished(QCoapReply *)));
+ QSignalSpy spyReplyGet4Finished(replyGet4.data(), SIGNAL(finished(QCoapReply *)));
+
+ QTRY_COMPARE(spyReplyGet1Finished.count(), 1);
+ QTRY_COMPARE(spyReplyGet2Finished.count(), 1);
+ QTRY_COMPARE(spyReplyGet3Finished.count(), 1);
+ QTRY_COMPARE(spyReplyGet4Finished.count(), 1);
+ QTRY_COMPARE(spyClientFinished.count(), 4);
+
+ QByteArray replyData1 = replyGet1->readAll();
+ QByteArray replyData2 = replyGet2->readAll();
+ QByteArray replyData3 = replyGet3->readAll();
+ QByteArray replyData4 = replyGet4->readAll();
+
+ QCOMPARE(replyGet1->responseCode(), QtCoap::Content);
+ QCOMPARE(replyGet2->responseCode(), QtCoap::Content);
+ QCOMPARE(replyGet3->responseCode(), QtCoap::Content);
+ QCOMPARE(replyGet4->responseCode(), QtCoap::Content);
+
+ QVERIFY(replyData1 != replyData2);
+ QVERIFY(replyData1 != replyData3);
+ QVERIFY(replyData1 != replyData4);
+ QVERIFY(replyData2 != replyData3);
+ QVERIFY(replyData2 != replyData4);
+ QVERIFY(replyData3 != replyData4);
+}
+
+void tst_QCoapClient::socketError()
+{
+#ifdef QT_BUILD_INTERNAL
+ QCoapClientForSocketErrorTests client;
+ QUrl url = QUrl(testServerResource());
+
+ QUdpSocket *socket = client.connection()->socket();
+ QVERIFY2(socket, "Socket not properly created with connection");
+ QSignalSpy spySocketError(socket, SIGNAL(error(QAbstractSocket::SocketError)));
+ QScopedPointer<QCoapReply> reply(client.get(url));
+ QSignalSpy spyClientError(&client, &QCoapClient::error);
+
+ QTRY_COMPARE_WITH_TIMEOUT(spySocketError.count(), 1, 10000);
+ QTRY_COMPARE_WITH_TIMEOUT(spyClientError.count(), 1, 1000);
+ QCOMPARE(spyClientError.first().at(1), QtCoap::AddressInUseError);
+#else
+ QSKIP("Not an internal build, skipping this test");
+#endif
+}
+void tst_QCoapClient::timeout_data()
+{
+ QTest::addColumn<int>("timeout");
+ QTest::addColumn<int>("maxRetransmit");
+
+ QTest::newRow("2000/0") << 2000 << 0;
+ QTest::newRow("2000/2") << 2000 << 2;
+ QTest::newRow("4000/0") << 4000 << 0;
+}
+
+void tst_QCoapClient::timeout()
+{
+ QFETCH(int, timeout);
+ QFETCH(int, maxRetransmit);
+
+ QCoapClientForTests client;
+ // Trigger a network timeout
+ client.protocol()->setAckTimeout(timeout);
+ client.protocol()->setAckRandomFactor(1);
+ client.protocol()->setMaxRetransmit(maxRetransmit);
+ QUrl url = QUrl("coap://240.0.0.0:5683/"); // Need an url that returns nothing
+
+ QElapsedTimer timeoutTimer;
+ timeoutTimer.start();
+ QScopedPointer<QCoapReply> reply(client.get(QCoapRequest(url, QCoapMessage::Confirmable)));
+ QSignalSpy spyClientError(&client, &QCoapClient::error);
+ QSignalSpy spyReplyError(reply.data(), &QCoapReply::error);
+ QSignalSpy spyReplyAborted(reply.data(), &QCoapReply::aborted);
+ QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished);
+
+ // Check timeout upper limit
+ int transmissions = maxRetransmit + 1;
+
+ // 10% Precision expected at least, plus timer precision
+ QTRY_COMPARE_WITH_TIMEOUT(spyReplyError.count(), 1, static_cast<int>(
+ 1.1 * client.protocol()->maxTransmitWait() + 20 * transmissions));
+
+ // Check timeout lower limit
+ qint64 elapsedTime = timeoutTimer.elapsed();
+ QString errorMessage = QString("Timeout was triggered after %1ms, while expecting about %2ms")
+ .arg(QString::number(elapsedTime),
+ QString::number(client.protocol()->maxTransmitWait()));
+
+ // 10% Precision expected at least, minus timer precision
+ QVERIFY2(elapsedTime > 0.9 * client.protocol()->maxTransmitWait() - 20 * transmissions,
+ qPrintable(errorMessage));
+
+ QCOMPARE(spyReplyError.first().at(1), QtCoap::TimeOutError);
+ QCOMPARE(spyReplyFinished.count(), 1);
+ QCOMPARE(spyReplyAborted.count(), 0);
+ QCOMPARE(spyClientError.count(), 1);
+}
+
+void tst_QCoapClient::abort()
+{
+ QCoapClient client;
+ QUrl url = QUrl(testServerUrl() + "/large");
+
+ QScopedPointer<QCoapReply> reply(client.get(url));
+ QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished);
+ QSignalSpy spyReplyAborted(reply.data(), &QCoapReply::aborted);
+ QSignalSpy spyReplyError(reply.data(), &QCoapReply::error);
+
+ reply->abortRequest();
+
+ QEventLoop eventLoop;
+ QTimer::singleShot(1000, &eventLoop, &QEventLoop::quit);
+ eventLoop.exec();
+
+ QCOMPARE(spyReplyAborted.count(), 1);
+ QCOMPARE(spyReplyFinished.count(), 1);
+ QCOMPARE(spyReplyError.count(), 0);
+}
+
+void tst_QCoapClient::blockwiseReply_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<QCoapMessage::MessageType>("type");
+ QTest::addColumn<QByteArray>("replyData");
+
+ QByteArray data;
+ data.append("/-------------------------------------------------------------\\\n");
+ data.append("| RESOURCE BLOCK NO. 1 OF 5 |\n");
+ data.append("| [each line contains 64 bytes] |\n");
+ data.append("\\-------------------------------------------------------------/\n");
+ data.append("/-------------------------------------------------------------\\\n");
+ data.append("| RESOURCE BLOCK NO. 2 OF 5 |\n");
+ data.append("| [each line contains 64 bytes] |\n");
+ data.append("\\-------------------------------------------------------------/\n");
+ data.append("/-------------------------------------------------------------\\\n");
+ data.append("| RESOURCE BLOCK NO. 3 OF 5 |\n");
+ data.append("| [each line contains 64 bytes] |\n");
+ data.append("\\-------------------------------------------------------------/\n");
+ data.append("/-------------------------------------------------------------\\\n");
+ data.append("| RESOURCE BLOCK NO. 4 OF 5 |\n");
+ data.append("| [each line contains 64 bytes] |\n");
+ data.append("\\-------------------------------------------------------------/\n");
+ data.append("/-------------------------------------------------------------\\\n");
+ data.append("| RESOURCE BLOCK NO. 5 OF 5 |\n");
+ data.append("| [each line contains 64 bytes] |\n");
+ data.append("\\-------------------------------------------------------------/\n");
+
+ QTest::newRow("get_large")
+ << QUrl(testServerUrl() + "/large")
+ << QCoapMessage::NonConfirmable
+ << data;
+ QTest::newRow("get_large_separate")
+ << QUrl(testServerUrl() + "/large-separate")
+ << QCoapMessage::NonConfirmable
+ << data;
+ QTest::newRow("get_large_confirmable")
+ << QUrl(testServerUrl() + "/large")
+ << QCoapMessage::Confirmable
+ << data;
+ QTest::newRow("get_large_separate_confirmable")
+ << QUrl(testServerUrl() + "/large-separate")
+ << QCoapMessage::Confirmable
+ << data;
+ QTest::newRow("get_large_16bits")
+ << QUrl(testServerUrl() + "/large")
+ << QCoapMessage::NonConfirmable
+ << data;
+ QTest::newRow("get_large_16bits_confirmable")
+ << QUrl(testServerUrl() + "/large")
+ << QCoapMessage::Confirmable
+ << data;
+}
+
+void tst_QCoapClient::blockwiseReply()
+{
+ QFETCH(QUrl, url);
+ QFETCH(QCoapMessage::MessageType, type);
+ QFETCH(QByteArray, replyData);
+
+ QCoapClient client;
+ QCoapRequest request(url);
+
+ if (qstrncmp(QTest::currentDataTag(), "get_large_16bits", 16) == 0)
+ client.setBlockSize(16);
+
+ request.setType(type);
+ QScopedPointer<QCoapReply> reply(client.get(request));
+ QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished);
+ QSignalSpy spyReplyError(reply.data(), &QCoapReply::error);
+ Helper helper;
+ connect(reply.data(), &QCoapReply::error, &helper, &Helper::onError);
+
+ QCOMPARE(spyReplyError.count(), 0);
+ QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.count(), 1, 30000);
+ QCOMPARE(reply->readAll(), replyData);
+}
+
+void tst_QCoapClient::blockwiseRequest_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<QCoapMessage::MessageType>("type");
+ QTest::addColumn<QByteArray>("requestData");
+ QTest::addColumn<QtCoap::ResponseCode>("responseCode");
+ QTest::addColumn<QByteArray>("replyData");
+
+ QByteArray data;
+ const char alphabet[] = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
+ for (int i = 3; i-- > 0; )
+ data.append(alphabet);
+
+ QTest::newRow("large_post_empty_reply") << QUrl(testServerUrl() + "/query")
+ << QCoapMessage::NonConfirmable
+ << data
+ << QtCoap::MethodNotAllowed
+ << QByteArray();
+ QTest::newRow("large_post_large_reply") << QUrl(testServerUrl() + "/large-post")
+ << QCoapMessage::NonConfirmable
+ << data
+ << QtCoap::Changed
+ << data.toUpper();
+}
+
+void tst_QCoapClient::blockwiseRequest()
+{
+ QFETCH(QUrl, url);
+ QFETCH(QCoapMessage::MessageType, type);
+ QFETCH(QByteArray, requestData);
+ QFETCH(QtCoap::ResponseCode, responseCode);
+ QFETCH(QByteArray, replyData);
+
+ QCoapClient client;
+ client.setBlockSize(16);
+
+ QCoapRequest request(url);
+ request.setType(type);
+ request.addOption(QCoapOption::ContentFormat);
+
+ QScopedPointer<QCoapReply> reply(client.post(request, requestData));
+ QSignalSpy spyReplyFinished(reply.data(), SIGNAL(finished(QCoapReply *)));
+
+ QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.count(), 1, 30000);
+
+ QByteArray dataReply = reply->readAll();
+ QCOMPARE(dataReply, replyData);
+ QCOMPARE(reply->responseCode(), responseCode);
+}
+
+void tst_QCoapClient::discover_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<int>("resourceNumber");
+
+ // Californium test server exposes 29 resources
+ QTest::newRow("discover") << QUrl(testServerUrl())
+ << 29;
+}
+
+void tst_QCoapClient::discover()
+{
+ QFETCH(QUrl, url);
+ QFETCH(int, resourceNumber);
+
+ QCoapClient client;
+
+ QScopedPointer<QCoapDiscoveryReply> resourcesReply(client.discover(url)); // /.well-known/core
+ QSignalSpy spyReplyFinished(resourcesReply.data(), SIGNAL(finished(QCoapReply *)));
+
+ QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.count(), 1, 30000);
+
+ QCOMPARE(resourcesReply->resources().length(), resourceNumber);
+
+ //! TODO Test discovery content too
+}
+
+void tst_QCoapClient::observe_data()
+{
+ QWARN("Observe tests may take some time, don't forget to raise Tests timeout in settings.");
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<QCoapMessage::MessageType>("type");
+
+ QTest::newRow("observe")
+ << QUrl(testServerUrl() + "/obs")
+ << QCoapMessage::NonConfirmable;
+
+ QTest::newRow("observe_confirmable")
+ << QUrl(testServerUrl() + "/obs")
+ << QCoapMessage::Confirmable;
+
+ QTest::newRow("observe_receive")
+ << QUrl(testServerUrl() + "/obs-non")
+ << QCoapMessage::NonConfirmable;
+
+ QTest::newRow("observe_receive_confirmable")
+ << QUrl(testServerUrl() + "/obs-non")
+ << QCoapMessage::Confirmable;
+
+ QTest::newRow("observe_large")
+ << QUrl(testServerUrl() + "/obs-large")
+ << QCoapMessage::NonConfirmable;
+
+ QTest::newRow("observe_large_confirmable")
+ << QUrl(testServerUrl() + "/obs-large")
+ << QCoapMessage::Confirmable;
+
+ QTest::newRow("observe_pumping")
+ << QUrl(testServerUrl() + "/obs-pumping")
+ << QCoapMessage::NonConfirmable;
+
+ QTest::newRow("observe_pumping_confirmable")
+ << QUrl(testServerUrl() + "/obs-pumping")
+ << QCoapMessage::Confirmable;
+}
+
+void tst_QCoapClient::observe()
+{
+ QFETCH(QUrl, url);
+ QFETCH(QCoapMessage::MessageType, type);
+
+ QCoapClient client;
+ QCoapRequest request(url);
+
+ request.setType(type);
+ QSharedPointer<QCoapReply> reply(client.observe(request),
+ &QObject::deleteLater);
+ QSignalSpy spyReplyNotified(reply.data(), &QCoapReply::notified);
+ QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished);
+
+ QTRY_COMPARE_WITH_TIMEOUT(spyReplyNotified.count(), 3, 30000);
+ client.cancelObserve(reply.data());
+
+ QVERIFY2(!spyReplyNotified.wait(7000), "'Notify' signal received after cancelling observe");
+ QCOMPARE(spyReplyFinished.count(), 1);
+
+ for (QList<QVariant> receivedSignals : qAsConst(spyReplyNotified)) {
+ QRegularExpression regexp(QStringLiteral("..:..:.."));
+ QByteArray payload = receivedSignals.at(1).value<QCoapMessage>().payload();
+ QString error = QString("Invalid payload for 'notified' signal: %1").arg(QString(payload));
+ QVERIFY2(regexp.match(payload).hasMatch(), qPrintable(error));
+ }
+}
+
+QTEST_MAIN(tst_QCoapClient)
+
+#include "tst_qcoapclient.moc"
diff --git a/tests/auto/qcoapconnection/qcoapconnection.pro b/tests/auto/qcoapconnection/qcoapconnection.pro
new file mode 100644
index 0000000..6c89a38
--- /dev/null
+++ b/tests/auto/qcoapconnection/qcoapconnection.pro
@@ -0,0 +1,8 @@
+QT = testlib network core-private core coap coap-private
+CONFIG += testcase
+
+include(../coaptestserver.pri)
+
+HEADERS += ../coapnetworksettings.h
+
+SOURCES += tst_qcoapconnection.cpp
diff --git a/tests/auto/qcoapconnection/tst_qcoapconnection.cpp b/tests/auto/qcoapconnection/tst_qcoapconnection.cpp
new file mode 100644
index 0000000..87d1d9f
--- /dev/null
+++ b/tests/auto/qcoapconnection/tst_qcoapconnection.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <QtCore/qglobal.h>
+#include <QtCoap/qcoapnamespace.h>
+#include <QtCore/qbuffer.h>
+#include <QtNetwork/qudpsocket.h>
+#include <QtNetwork/qnetworkdatagram.h>
+#include <QtCoap/qcoapglobal.h>
+#include <QtCoap/qcoapconnection.h>
+#include <QtCoap/qcoaprequest.h>
+#include <private/qcoapconnection_p.h>
+#include <private/qcoapinternalrequest_p.h>
+#include "../coapnetworksettings.h"
+
+using namespace QtCoapNetworkSettings;
+
+class tst_QCoapConnection : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void ctor();
+ void connectToHost();
+ void sendRequest_data();
+ void sendRequest();
+};
+
+class QCoapConnectionForTest : public QCoapConnection
+{
+ Q_OBJECT
+public:
+ QCoapConnectionForTest(QObject *parent = nullptr) :
+ QCoapConnection(parent)
+ {}
+
+ void bindSocketForTest() { d_func()->bindSocket(); }
+};
+
+void tst_QCoapConnection::ctor()
+{
+ QCoapConnection connection;
+ QVERIFY(connection.socket());
+}
+
+void tst_QCoapConnection::connectToHost()
+{
+#ifdef QT_BUILD_INTERNAL
+ QCoapConnectionForTest connection;
+
+ QUdpSocket *socket = qobject_cast<QUdpSocket*>(connection.socket());
+ QSignalSpy spyConnectionBound(&connection, SIGNAL(bound()));
+ QSignalSpy spySocketStateChanged(socket , SIGNAL(stateChanged(QAbstractSocket::SocketState)));
+
+ QCOMPARE(connection.state(), QCoapConnection::Unconnected);
+
+ connection.bindSocketForTest();
+
+ QTRY_COMPARE(spySocketStateChanged.count(), 1);
+ QTRY_COMPARE(spyConnectionBound.count(), 1);
+ QCOMPARE(connection.state(), QCoapConnection::Bound);
+#else
+ QSKIP("Not an internal build, skipping this test");
+#endif
+}
+
+void tst_QCoapConnection::sendRequest_data()
+{
+ QTest::addColumn<QString>("protocol");
+ QTest::addColumn<QString>("host");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<quint16>("port");
+ QTest::addColumn<QtCoap::Method>("method");
+ QTest::addColumn<QString>("dataHexaHeader");
+ QTest::addColumn<QString>("dataHexaPayload");
+
+ QTest::newRow("simple_get_request")
+ << "coap://"
+ << testServerHost()
+ << "/test"
+ << quint16(QtCoap::DefaultPort)
+ << QtCoap::Get
+ << "5445"
+ << "61626364c0211eff547970653a203120284e4f4e290a436f64653a2031202847"
+ "4554290a4d49443a2032343830360a546f6b656e3a203631363236333634";
+
+ QTest::newRow("simple_put_request")
+ << "coap://"
+ << testServerHost()
+ << "/test"
+ << quint16(QtCoap::DefaultPort)
+ << QtCoap::Put
+ << "5444"
+ << "61626364";
+
+ QTest::newRow("simple_post_request")
+ << "coap://"
+ << testServerHost()
+ << "/test"
+ << quint16(QtCoap::DefaultPort)
+ << QtCoap::Post
+ << "5441"
+ << "61626364896c6f636174696f6e31096c6f636174696f6e32096c6f636174696f"
+ "6e33";
+
+ QTest::newRow("simple_delete_request")
+ << "coap://"
+ << testServerHost()
+ << "/test"
+ << quint16(QtCoap::DefaultPort)
+ << QtCoap::Delete
+ << "5442"
+ << "61626364";
+}
+
+void tst_QCoapConnection::sendRequest()
+{
+#ifdef QT_BUILD_INTERNAL
+ QFETCH(QString, protocol);
+ QFETCH(QString, host);
+ QFETCH(QString, path);
+ QFETCH(quint16, port);
+ QFETCH(QtCoap::Method, method);
+ QFETCH(QString, dataHexaHeader);
+ QFETCH(QString, dataHexaPayload);
+
+ QCoapConnectionForTest connection;
+
+ QSignalSpy spySocketReadyRead(connection.socket(), &QUdpSocket::readyRead);
+ QSignalSpy spyConnectionReadyRead(&connection, &QCoapConnection::readyRead);
+
+ QCoapRequest request(protocol + host + path);
+ request.setMessageId(24806);
+ request.setToken(QByteArray("abcd"));
+ request.setMethod(method);
+ QVERIFY(connection.socket() != nullptr);
+ QCoapInternalRequest internalRequest(request);
+ connection.sendRequest(internalRequest.toQByteArray(), host, port);
+
+ QTRY_COMPARE(spySocketReadyRead.count(), 1);
+ QTRY_COMPARE(spyConnectionReadyRead.count(), 1);
+
+ QNetworkDatagram datagram = spyConnectionReadyRead.first()
+ .first().value<QNetworkDatagram>();
+
+ QVERIFY(QString(datagram.data().toHex()).startsWith(dataHexaHeader));
+ QVERIFY(QString(datagram.data().toHex()).endsWith(dataHexaPayload));
+#else
+ QSKIP("Not an internal build, skipping this test");
+#endif
+}
+
+QTEST_MAIN(tst_QCoapConnection)
+
+#include "tst_qcoapconnection.moc"
diff --git a/tests/auto/qcoapinternalreply/qcoapinternalreply.pro b/tests/auto/qcoapinternalreply/qcoapinternalreply.pro
new file mode 100644
index 0000000..a8e0777
--- /dev/null
+++ b/tests/auto/qcoapinternalreply/qcoapinternalreply.pro
@@ -0,0 +1,4 @@
+QT = testlib core-private network core coap coap-private
+CONFIG += testcase
+
+SOURCES += tst_qcoapinternalreply.cpp
diff --git a/tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp b/tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp
new file mode 100644
index 0000000..58bd51b
--- /dev/null
+++ b/tests/auto/qcoapinternalreply/tst_qcoapinternalreply.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <private/qcoapinternalreply_p.h>
+#include <private/qcoapreply_p.h>
+
+#ifdef QT_BUILD_INTERNAL
+
+class tst_QCoapInternalReply : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void parseReplyPdu_data();
+ void parseReplyPdu();
+ void updateReply_data();
+ void updateReply();
+ void requestData();
+ void abortRequest();
+};
+
+void tst_QCoapInternalReply::parseReplyPdu_data()
+{
+ QTest::addColumn<QtCoap::ResponseCode>("responseCode");
+ QTest::addColumn<QCoapMessage::MessageType>("type");
+ QTest::addColumn<quint16>("messageId");
+ QTest::addColumn<QByteArray>("token");
+ QTest::addColumn<quint8>("tokenLength");
+ QTest::addColumn<QList<QCoapOption::OptionName>>("optionsNames");
+ QTest::addColumn<QList<quint8>>("optionsLengths");
+ QTest::addColumn<QList<QByteArray>>("optionsValues");
+ QTest::addColumn<QString>("payload");
+ QTest::addColumn<QString>("pduHexa");
+
+ QList<QCoapOption::OptionName> optionsNamesReply({QCoapOption::ContentFormat,
+ QCoapOption::MaxAge});
+ QList<quint8> optionsLengthsReply({0, 1});
+ QList<QByteArray> optionsValuesReply({"", QByteArray::fromHex("1e")});
+
+ QList<QCoapOption::OptionName> bigOptionNameReply({QCoapOption::Size1});
+ QList<quint8> bigOptionLengthReply({26});
+ QList<QByteArray> bigOptionValueReply({QByteArray("abcdefghijklmnopqrstuvwxyz")});
+
+ QTest::newRow("reply_with_options_and_payload")
+ << QtCoap::Content
+ << QCoapMessage::NonConfirmable
+ << quint16(64463)
+ << QByteArray("4647f09b")
+ << quint8(4)
+ << optionsNamesReply
+ << optionsLengthsReply
+ << optionsValuesReply
+ << "Type: 1 (NON)\nCode: 1 (GET)\nMID: 56400\nToken: 4647f09b"
+ << "5445fbcf4647f09bc0211eff547970653a203120284e4f4e290a436f64653a20"
+ "312028474554290a4d49443a2035363430300a546f6b656e3a20343634376630"
+ "3962";
+
+ QTest::newRow("reply_with_payload")
+ << QtCoap::Content
+ << QCoapMessage::NonConfirmable
+ << quint16(64463)
+ << QByteArray("4647f09b")
+ << quint8(4)
+ << QList<QCoapOption::OptionName>()
+ << QList<quint8>()
+ << QList<QByteArray>()
+ << "Type: 1 (NON)\nCode: 1 (GET)\nMID: 56400\nToken: 4647f09b"
+ << "5445fbcf4647f09bff547970653a203120284e4f4e290a436f64653a20312028"
+ "474554290a4d49443a2035363430300a546f6b656e3a203436343766303962";
+
+ QTest::newRow("reply_with_options")
+ << QtCoap::Content
+ << QCoapMessage::NonConfirmable
+ << quint16(64463)
+ << QByteArray("4647f09b")
+ << quint8(4)
+ << optionsNamesReply
+ << optionsLengthsReply
+ << optionsValuesReply
+ << ""
+ << "5445fbcf4647f09bc0211e";
+
+ QTest::newRow("reply_only")
+ << QtCoap::Content
+ << QCoapMessage::NonConfirmable
+ << quint16(64463)
+ << QByteArray("4647f09b")
+ << quint8(4)
+ << QList<QCoapOption::OptionName>()
+ << QList<quint8>()
+ << QList<QByteArray>()
+ << ""
+ << "5445fbcf4647f09b";
+
+ QTest::newRow("reply_with_big_option")
+ << QtCoap::Content
+ << QCoapMessage::NonConfirmable
+ << quint16(64463)
+ << QByteArray("4647f09b")
+ << quint8(4)
+ << bigOptionNameReply
+ << bigOptionLengthReply
+ << bigOptionValueReply
+ << ""
+ << "5445fbcf4647f09bdd2f0d6162636465666768696a6b6c6d6e6f707172737475"
+ "767778797a";
+}
+
+void tst_QCoapInternalReply::parseReplyPdu()
+{
+ QFETCH(QtCoap::ResponseCode, responseCode);
+ QFETCH(QCoapMessage::MessageType, type);
+ QFETCH(quint16, messageId);
+ QFETCH(QByteArray, token);
+ QFETCH(quint8, tokenLength);
+ QFETCH(QList<QCoapOption::OptionName>, optionsNames);
+ QFETCH(QList<quint8>, optionsLengths);
+ QFETCH(QList<QByteArray>, optionsValues);
+ QFETCH(QString, payload);
+ QFETCH(QString, pduHexa);
+
+ QScopedPointer<QCoapInternalReply>
+ reply(QCoapInternalReply::createFromFrame(QByteArray::fromHex(pduHexa.toUtf8())));
+
+ QCOMPARE(reply->message()->type(), type);
+ QCOMPARE(reply->message()->tokenLength(), tokenLength);
+ QCOMPARE(reply->responseCode(), responseCode);
+ QCOMPARE(reply->message()->messageId(), messageId);
+ QCOMPARE(reply->message()->token().toHex(), token);
+ QCOMPARE(reply->message()->optionCount(), optionsNames.count());
+ for (int i = 0; i < reply->message()->optionCount(); ++i) {
+ QCoapOption option = reply->message()->option(i);
+ QCOMPARE(option.name(), optionsNames.at(i));
+ QCOMPARE(option.length(), optionsLengths.at(i));
+ QCOMPARE(option.value(), optionsValues.at(i));
+ }
+ QCOMPARE(reply->message()->payload(), payload);
+}
+
+class QCoapReplyForTests : public QCoapReply
+{
+public:
+ QCoapReplyForTests(const QCoapRequest &req) : QCoapReply (req) {}
+
+ void setRunning(const QCoapToken &token, QCoapMessageId messageId)
+ {
+ Q_D(QCoapReply);
+ d->_q_setRunning(token, messageId);
+ }
+ void setContentAndFinished(const QCoapInternalReply *internal)
+ {
+ Q_D(QCoapReply);
+ d->_q_setContent(internal->senderAddress(), *internal->message(), internal->responseCode());
+ d->_q_setFinished();
+ }
+};
+
+void tst_QCoapInternalReply::updateReply_data()
+{
+ QTest::addColumn<QByteArray>("data");
+
+ QTest::newRow("success") << QByteArray("Data for the updating test");
+}
+
+void tst_QCoapInternalReply::updateReply()
+{
+ QFETCH(QByteArray, data);
+
+ QCoapReplyForTests reply((QCoapRequest()));
+ QCoapInternalReply internalReply;
+ internalReply.message()->setPayload(data);
+ QSignalSpy spyReplyFinished(&reply, &QCoapReply::finished);
+
+ reply.setContentAndFinished(&internalReply);
+
+ QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.count(), 1, 1000);
+ QCOMPARE(reply.readAll(), data);
+}
+
+void tst_QCoapInternalReply::requestData()
+{
+ QCoapReplyForTests reply((QCoapRequest()));
+ reply.setRunning("token", 543);
+
+ QCOMPARE(reply.request().token(), QByteArray("token"));
+ QCOMPARE(reply.request().messageId(), 543);
+}
+
+void tst_QCoapInternalReply::abortRequest()
+{
+ QCoapReplyForTests reply((QCoapRequest()));
+ reply.setRunning("token", 543);
+
+ QSignalSpy spyAborted(&reply, &QCoapReply::aborted);
+ QSignalSpy spyFinished(&reply, &QCoapReply::finished);
+ reply.abortRequest();
+
+ QTRY_COMPARE_WITH_TIMEOUT(spyAborted.count(), 1, 1000);
+ QList<QVariant> arguments = spyAborted.takeFirst();
+ QTRY_COMPARE_WITH_TIMEOUT(spyFinished.count(), 1, 1000);
+ QVERIFY(arguments.at(0).toByteArray() == "token");
+}
+
+#else
+
+class tst_QCoapInternalReply : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ QSKIP("Not an internal build, nothing to test");
+ }
+};
+
+#endif
+
+QTEST_MAIN(tst_QCoapInternalReply)
+
+#include "tst_qcoapinternalreply.moc"
diff --git a/tests/auto/qcoapinternalrequest/qcoapinternalrequest.pro b/tests/auto/qcoapinternalrequest/qcoapinternalrequest.pro
new file mode 100644
index 0000000..a1e57a6
--- /dev/null
+++ b/tests/auto/qcoapinternalrequest/qcoapinternalrequest.pro
@@ -0,0 +1,5 @@
+QT = testlib core-private network core coap coap-private
+CONFIG += testcase
+
+SOURCES += \
+ tst_qcoapinternalrequest.cpp
diff --git a/tests/auto/qcoapinternalrequest/tst_qcoapinternalrequest.cpp b/tests/auto/qcoapinternalrequest/tst_qcoapinternalrequest.cpp
new file mode 100644
index 0000000..97bf421
--- /dev/null
+++ b/tests/auto/qcoapinternalrequest/tst_qcoapinternalrequest.cpp
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <QtCoap/qcoaprequest.h>
+#include <private/qcoapinternalrequest_p.h>
+
+#ifdef QT_BUILD_INTERNAL
+
+class tst_QCoapInternalRequest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void requestToFrame_data();
+ void requestToFrame();
+ void parseUri_data();
+ void parseUri();
+};
+
+void tst_QCoapInternalRequest::requestToFrame_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<QtCoap::Method>("method");
+ QTest::addColumn<QCoapMessage::MessageType>("type");
+ QTest::addColumn<quint16>("messageId");
+ QTest::addColumn<QByteArray>("token");
+ QTest::addColumn<QString>("pduHeader");
+ QTest::addColumn<QString>("pduPayload");
+
+ QTest::newRow("request_with_option_and_payload")
+ << QUrl("coap://10.20.30.40:5683/test")
+ << QtCoap::Get
+ << QCoapRequest::NonConfirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "5401dc504647f09bb474657374ff"
+ << "Some payload";
+
+ QTest::newRow("request_domain")
+ << QUrl("coap://domain.com:5683/test")
+ << QtCoap::Get
+ << QCoapRequest::NonConfirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "5401dc504647f09b3a646f6d61696e2e636f6d8474657374ff"
+ << "Some payload";
+
+ QTest::newRow("request_ipv6")
+ << QUrl("coap://[::ffff:ac11:3]:5683/test")
+ << QtCoap::Get
+ << QCoapRequest::NonConfirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "5401dc504647f09bb474657374ff"
+ << "Some payload";
+
+ QTest::newRow("request_without_payload")
+ << QUrl("coap://10.20.30.40:5683/test")
+ << QtCoap::Get
+ << QCoapRequest::NonConfirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "5401dc504647f09bb474657374"
+ << "";
+
+ QTest::newRow("request_without_option")
+ << QUrl("coap://10.20.30.40:5683/")
+ << QtCoap::Put
+ << QCoapRequest::Confirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "4403dc504647f09bff"
+ << "Some payload";
+
+ QTest::newRow("request_only")
+ << QUrl("coap://10.20.30.40:5683/")
+ << QtCoap::Get
+ << QCoapRequest::NonConfirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "5401dc504647f09b"
+ << "";
+
+ QTest::newRow("request_with_multiple_options")
+ << QUrl("coap://10.20.30.40:5683/test/oui")
+ << QtCoap::Get
+ << QCoapRequest::NonConfirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "5401dc504647f09bb474657374036f7569"
+ << "";
+
+ QTest::newRow("request_with_big_option_number")
+ << QUrl("coap://10.20.30.40:5683/test")
+ << QtCoap::Get
+ << QCoapRequest::NonConfirmable
+ << quint16(56400)
+ << QByteArray::fromHex("4647f09b")
+ << "5401dc504647f09bb474657374dd240d6162636465666768696a6b6c6d6e6f70"
+ "7172737475767778797aff"
+ << "Some payload";
+}
+
+void tst_QCoapInternalRequest::requestToFrame()
+{
+ QFETCH(QUrl, url);
+ QFETCH(QtCoap::Method, method);
+ QFETCH(QCoapMessage::MessageType, type);
+ QFETCH(quint16, messageId);
+ QFETCH(QByteArray, token);
+ QFETCH(QString, pduHeader);
+ QFETCH(QString, pduPayload);
+
+ QCoapRequest request(url);
+ request.setType(type);
+ request.setMethod(method);
+ 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);
+ if (!pduPayload.isEmpty())
+ pdu.append(pduPayload.toUtf8().toHex());
+
+ QCoapInternalRequest internalRequest(request);
+ QCOMPARE(internalRequest.toQByteArray().toHex(), pdu);
+}
+
+void tst_QCoapInternalRequest::parseUri_data()
+{
+ qRegisterMetaType<QVector<QCoapOption>>();
+ QTest::addColumn<QUrl>("uri");
+ QTest::addColumn<QUrl>("proxyUri");
+ QTest::addColumn<QVector<QCoapOption>>("options");
+
+ QTest::newRow("port_path")
+ << QUrl("coap://10.20.30.40:1234/test/path1")
+ << QUrl()
+ << QVector<QCoapOption>({
+ QCoapOption(QCoapOption::UriPort, 1234),
+ QCoapOption(QCoapOption::UriPath, "test"),
+ QCoapOption(QCoapOption::UriPath, "path1") });
+
+ QTest::newRow("path_query")
+ << QUrl("coap://10.20.30.40/test/path1/?rd=25&nd=4")
+ << QUrl()
+ << QVector<QCoapOption>({
+ QCoapOption(QCoapOption::UriPath, "test"),
+ QCoapOption(QCoapOption::UriPath, "path1"),
+ QCoapOption(QCoapOption::UriQuery, "rd=25"),
+ QCoapOption(QCoapOption::UriQuery, "nd=4") });
+
+ QTest::newRow("host_path_query")
+ << QUrl("coap://aa.bb.cc.com:5683/test/path1/?rd=25&nd=4")
+ << QUrl()
+ << QVector<QCoapOption>({
+ QCoapOption(QCoapOption::UriHost, "aa.bb.cc.com"),
+ QCoapOption(QCoapOption::UriPath, "test"),
+ QCoapOption(QCoapOption::UriPath, "path1"),
+ QCoapOption(QCoapOption::UriQuery, "rd=25"),
+ QCoapOption(QCoapOption::UriQuery, "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")
+ << QVector<QCoapOption>({
+ QCoapOption(QCoapOption::ProxyUri, "coap://10.20.30.40/test:5684/othertest/path") });
+}
+
+void tst_QCoapInternalRequest::parseUri()
+{
+ QFETCH(QUrl, uri);
+ QFETCH(QUrl, proxyUri);
+ QFETCH(QVector<QCoapOption>, options);
+
+ QCoapRequest request(uri, QCoapMessage::NonConfirmable, proxyUri);
+ QCoapInternalRequest internalRequest(request);
+
+ for (QCoapOption opt : options)
+ QVERIFY2(internalRequest.message()->options().contains(opt), "Missing option");
+
+ QCOMPARE(options.count(), internalRequest.message()->optionCount());
+}
+
+#else
+
+class tst_QCoapInternalRequest : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase()
+ {
+ QSKIP("Not an internal build, nothing to test");
+ }
+};
+
+#endif
+
+QTEST_APPLESS_MAIN(tst_QCoapInternalRequest)
+
+#include "tst_qcoapinternalrequest.moc"
diff --git a/tests/auto/qcoapmessage/qcoapmessage.pro b/tests/auto/qcoapmessage/qcoapmessage.pro
new file mode 100644
index 0000000..47179d6
--- /dev/null
+++ b/tests/auto/qcoapmessage/qcoapmessage.pro
@@ -0,0 +1,4 @@
+QT = testlib core-private network core coap
+CONFIG += testcase
+
+SOURCES += tst_qcoapmessage.cpp
diff --git a/tests/auto/qcoapmessage/tst_qcoapmessage.cpp b/tests/auto/qcoapmessage/tst_qcoapmessage.cpp
new file mode 100644
index 0000000..90dcd64
--- /dev/null
+++ b/tests/auto/qcoapmessage/tst_qcoapmessage.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <QtCoap/qcoapmessage.h>
+
+class tst_QCoapMessage : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void copyAndDetach();
+ void setMessageType_data();
+ void setMessageType();
+ void addOption_string();
+ void addOption_uint_data();
+ void addOption_uint();
+ void removeOption();
+ void urlOptions();
+};
+
+void tst_QCoapMessage::copyAndDetach()
+{
+ QCoapMessage a;
+ a.setMessageId(3);
+ a.setPayload("payload");
+ a.setToken("token");
+ a.setType(QCoapMessage::Acknowledgment);
+ a.setVersion(5);
+
+ // Test the copy
+ QCoapMessage b(a);
+ QVERIFY2(b.messageId() == 3, "Message not copied correctly");
+ QVERIFY2(b.payload() == "payload", "Message not copied correctly");
+ QVERIFY2(b.token() == "token", "Message not copied correctly");
+ QVERIFY2(b.type() == QCoapMessage::Acknowledgment, "Message not copied correctly");
+ QVERIFY2(b.version() == 5, "Message not copied correctly");
+
+ // Detach
+ b.setMessageId(9);
+ QCOMPARE(b.messageId(), 9);
+ QCOMPARE(a.messageId(), 3);
+}
+
+void tst_QCoapMessage::setMessageType_data()
+{
+ QTest::addColumn<QCoapMessage::MessageType>("type");
+
+ QTest::newRow("acknowledgment") << QCoapMessage::Acknowledgment;
+ QTest::newRow("confirmable") << QCoapMessage::Confirmable;
+ QTest::newRow("non-confirmable") << QCoapMessage::NonConfirmable;
+ QTest::newRow("reset") << QCoapMessage::Reset;
+}
+
+void tst_QCoapMessage::setMessageType()
+{
+ QFETCH(QCoapMessage::MessageType, type);
+ QCoapMessage message;
+ message.setType(type);
+ QCOMPARE(message.type(), type);
+
+ //! TODO extend QCoapMessage tests
+}
+
+void tst_QCoapMessage::addOption_string()
+{
+ //! TODO with one and more than one identical options
+}
+
+void tst_QCoapMessage::addOption_uint_data()
+{
+ QTest::addColumn<quint32>("value");
+ QTest::addColumn<int>("size");
+
+ QTest::newRow("4 bytes") << (quint32)0xF0aF0010 << 4;
+ QTest::newRow("3 bytes") << (quint32)0x300010 << 3;
+ QTest::newRow("2 bytes") << (quint32)0x5010 << 2;
+ QTest::newRow("1 byte") << (quint32)0x80 << 1;
+}
+
+void tst_QCoapMessage::addOption_uint()
+{
+ QFETCH(quint32, value);
+ QFETCH(int, size);
+
+ QCoapOption option(QCoapOption::Block1, value);
+
+ QCOMPARE(option.valueToInt(), value);
+ QCOMPARE(option.value().size(), size);
+}
+
+void tst_QCoapMessage::removeOption()
+{
+ //! TODO with one and more than one identical options
+}
+
+void tst_QCoapMessage::urlOptions()
+{
+ //! TODO Test the following from the RFC:
+ // For example, the following three URIs are equivalent and cause the
+ // same options and option values to appear in the CoAP messages:
+ // coap://example.com:5683/~sensors/temp.xml
+ // coap://EXAMPLE.com/%7Esensors/temp.xml
+ // coap://EXAMPLE.com:/%7esensors/temp.xml
+}
+
+QTEST_APPLESS_MAIN(tst_QCoapMessage)
+
+#include "tst_qcoapmessage.moc"
diff --git a/tests/auto/qcoapoption/qcoapoption.pro b/tests/auto/qcoapoption/qcoapoption.pro
new file mode 100644
index 0000000..411dc4c
--- /dev/null
+++ b/tests/auto/qcoapoption/qcoapoption.pro
@@ -0,0 +1,4 @@
+QT = testlib core-private core coap coap-private
+CONFIG += testcase
+
+SOURCES += tst_qcoapoption.cpp
diff --git a/tests/auto/qcoapoption/tst_qcoapoption.cpp b/tests/auto/qcoapoption/tst_qcoapoption.cpp
new file mode 100644
index 0000000..2541fad
--- /dev/null
+++ b/tests/auto/qcoapoption/tst_qcoapoption.cpp
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+
+#include <QtCoap/qcoapoption.h>
+
+class tst_QCoapOption : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void constructWithQByteArray();
+ void constructWithQStringView();
+ void constructWithCString();
+ void constructWithInteger();
+ void constructWithUtf8Characters();
+};
+
+void tst_QCoapOption::constructWithQByteArray()
+{
+ QByteArray ba = "some data";
+ QCoapOption option(QCoapOption::LocationPath, ba);
+
+ QCOMPARE(option.value(), ba);
+}
+
+void tst_QCoapOption::constructWithQStringView()
+{
+ QString str = "some data";
+ QCoapOption option(QCoapOption::LocationPath, str);
+
+ QCOMPARE(option.value(), str.toUtf8());
+}
+
+void tst_QCoapOption::constructWithCString()
+{
+ const char *str = "some data";
+ QCoapOption option(QCoapOption::LocationPath, str);
+
+ QCOMPARE(option.value(), QByteArray(str));
+}
+
+void tst_QCoapOption::constructWithInteger()
+{
+ quint32 value = 64000;
+ QCoapOption option(QCoapOption::Size1, value);
+
+ QCOMPARE(option.valueToInt(), value);
+}
+
+void tst_QCoapOption::constructWithUtf8Characters()
+{
+ QByteArray ba = "\xc3\xa9~\xce\xbb\xe2\x82\xb2";
+ QCoapOption option(QCoapOption::LocationPath, ba);
+
+ QCOMPARE(option.value(), ba);
+}
+
+QTEST_APPLESS_MAIN(tst_QCoapOption)
+
+#include "tst_qcoapoption.moc"
diff --git a/tests/auto/qcoapreply/qcoapreply.pro b/tests/auto/qcoapreply/qcoapreply.pro
new file mode 100644
index 0000000..acd82b9
--- /dev/null
+++ b/tests/auto/qcoapreply/qcoapreply.pro
@@ -0,0 +1,4 @@
+QT = testlib core-private network core coap coap-private
+CONFIG += testcase
+
+SOURCES += tst_qcoapreply.cpp
diff --git a/tests/auto/qcoapreply/tst_qcoapreply.cpp b/tests/auto/qcoapreply/tst_qcoapreply.cpp
new file mode 100644
index 0000000..b1f9006
--- /dev/null
+++ b/tests/auto/qcoapreply/tst_qcoapreply.cpp
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <QtCoap/qcoapreply.h>
+#include <private/qcoapreply_p.h>
+
+class tst_QCoapReply : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void updateReply_data();
+ void updateReply();
+ void requestData();
+ void abortRequest();
+};
+
+class QCoapReplyForTests : public QCoapReply
+{
+public:
+ QCoapReplyForTests(const QCoapRequest &req) : QCoapReply (req) {}
+
+ void setRunning(const QCoapToken &token, QCoapMessageId messageId)
+ {
+ Q_D(QCoapReply);
+ d->_q_setRunning(token, messageId);
+ }
+};
+
+void tst_QCoapReply::updateReply_data()
+{
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<QtCoap::ResponseCode>("responseCode");
+ QTest::addColumn<QtCoap::Error>("error");
+
+ QTest::newRow("success")
+ << QByteArray("Some data")
+ << QtCoap::Content
+ << QtCoap::NoError;
+ QTest::newRow("content error")
+ << QByteArray("Error")
+ << QtCoap::BadRequest
+ << QtCoap::NoError;
+ QTest::newRow("finished error")
+ << QByteArray("Error")
+ << QtCoap::Content
+ << QtCoap::BadRequestError;
+ QTest::newRow("content & finished errors")
+ << QByteArray("2Errors")
+ << QtCoap::BadGateway
+ << QtCoap::BadRequestError;
+}
+
+void tst_QCoapReply::updateReply()
+{
+ QFETCH(QByteArray, payload);
+ QFETCH(QtCoap::ResponseCode, responseCode);
+ QFETCH(QtCoap::Error, error);
+
+ QByteArray token = "\xAF\x01\xC2";
+ int id = 645;
+
+ QCoapReply reply((QCoapRequest()));
+ QCoapMessage message;
+ message.setToken(token);
+ message.setMessageId(id);
+ message.setPayload(payload);
+
+ QSignalSpy spyReplyFinished(&reply, &QCoapReply::finished);
+ QSignalSpy spyReplyNotified(&reply, &QCoapReply::notified);
+ QSignalSpy spyReplyError(&reply, &QCoapReply::error);
+ QSignalSpy spyReplyAborted(&reply, &QCoapReply::aborted);
+
+ QMetaObject::invokeMethod(&reply, "_q_setContent",
+ Q_ARG(QHostAddress, QHostAddress()),
+ Q_ARG(QCoapMessage, message),
+ Q_ARG(QtCoap::ResponseCode, responseCode));
+ QMetaObject::invokeMethod(&reply, "_q_setFinished",
+ Q_ARG(QtCoap::Error, error));
+
+ QCOMPARE(spyReplyFinished.count(), 1);
+ QCOMPARE(spyReplyNotified.count(), 0);
+ QCOMPARE(spyReplyAborted.count(), 0);
+ if (error != QtCoap::NoError || QtCoap::isError(responseCode)) {
+ QVERIFY(spyReplyError.count() > 0);
+ QCOMPARE(reply.isSuccessful(), false);
+ } else {
+ QCOMPARE(spyReplyError.count(), 0);
+ QCOMPARE(reply.isSuccessful(), true);
+ }
+
+ QCOMPARE(reply.readAll(), payload);
+ QCOMPARE(reply.readAll(), QByteArray());
+ QCOMPARE(reply.responseCode(), responseCode);
+ QCOMPARE(reply.message().token(), token);
+ QCOMPARE(reply.message().messageId(), id);
+}
+
+void tst_QCoapReply::requestData()
+{
+#ifdef QT_BUILD_INTERNAL
+ QCoapReplyForTests reply((QCoapRequest()));
+ reply.setRunning("token", 543);
+
+ QCOMPARE(reply.request().token(), QByteArray("token"));
+ QCOMPARE(reply.request().messageId(), 543);
+#else
+ QSKIP("Not an internal build, skipping this test");
+#endif
+}
+
+void tst_QCoapReply::abortRequest()
+{
+#ifdef QT_BUILD_INTERNAL
+ QCoapReplyForTests reply((QCoapRequest()));
+ reply.setRunning("token", 543);
+
+ QSignalSpy spyAborted(&reply, &QCoapReply::aborted);
+ QSignalSpy spyFinished(&reply, &QCoapReply::finished);
+ reply.abortRequest();
+
+ QTRY_COMPARE_WITH_TIMEOUT(spyAborted.count(), 1, 1000);
+ QList<QVariant> arguments = spyAborted.takeFirst();
+ QTRY_COMPARE_WITH_TIMEOUT(spyFinished.count(), 1, 1000);
+ QVERIFY(arguments.at(0).toByteArray() == "token");
+ QCOMPARE(reply.isSuccessful(), false);
+#else
+ QSKIP("Not an internal build, skipping this test");
+#endif
+}
+
+QTEST_MAIN(tst_QCoapReply)
+
+#include "tst_qcoapreply.moc"
diff --git a/tests/auto/qcoaprequest/qcoaprequest.pro b/tests/auto/qcoaprequest/qcoaprequest.pro
new file mode 100644
index 0000000..e25ce28
--- /dev/null
+++ b/tests/auto/qcoaprequest/qcoaprequest.pro
@@ -0,0 +1,4 @@
+QT = testlib core-private network core coap coap-private
+CONFIG += testcase
+
+SOURCES += tst_qcoaprequest.cpp
diff --git a/tests/auto/qcoaprequest/tst_qcoaprequest.cpp b/tests/auto/qcoaprequest/tst_qcoaprequest.cpp
new file mode 100644
index 0000000..98329fe
--- /dev/null
+++ b/tests/auto/qcoaprequest/tst_qcoaprequest.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <QtCoap/qcoapglobal.h>
+#include <QtCoap/qcoapnamespace.h>
+#include <QtCoap/qcoaprequest.h>
+#include <QtCoap/qcoapconnection.h>
+
+class tst_QCoapRequest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void ctor_data();
+ void ctor();
+ void setUrl_data();
+ void setUrl();
+ void setMethod_data();
+ void setMethod();
+ void enableObserve();
+ void copyAndDetach();
+};
+
+void tst_QCoapRequest::ctor_data()
+{
+ QTest::addColumn<QUrl>("url");
+
+ QTest::newRow("empty") << QUrl();
+ QTest::newRow("coap") << QUrl("coap://vs0.inf.ethz.ch:5683/test");
+}
+
+void tst_QCoapRequest::ctor()
+{
+ QFETCH(QUrl, url);
+
+ QCoapRequest request(url);
+ QCOMPARE(request.url(), url);
+}
+
+void tst_QCoapRequest::setUrl_data()
+{
+ QTest::addColumn<QUrl>("inputUrl");
+ QTest::addColumn<QUrl>("expectedUrl");
+
+ QTest::newRow("empty") << QUrl() << QUrl();
+ QTest::newRow("coap") << QUrl("coap://10.11.12.13:5683/test") << QUrl("coap://10.11.12.13:5683/test");
+ QTest::newRow("other_port") << QUrl("coap://10.11.12.13:8888/test") << QUrl("coap://10.11.12.13:8888/test");
+ QTest::newRow("no_port") << QUrl("coap://vs0.inf.ethz.ch/test") << QUrl("coap://vs0.inf.ethz.ch:5683/test");
+ QTest::newRow("no_scheme_no_port") << QUrl("vs0.inf.ethz.ch/test") << QUrl("coap://vs0.inf.ethz.ch:5683/test");
+ QTest::newRow("incorrect_scheme") << QUrl("http://vs0.inf.ethz.ch:5683/test") << QUrl();
+ QTest::newRow("invalid") << QUrl("-coap://vs0.inf.ethz.ch:5683/test") << QUrl();
+}
+
+void tst_QCoapRequest::setUrl()
+{
+ QFETCH(QUrl, inputUrl);
+ QFETCH(QUrl, expectedUrl);
+
+ QCoapRequest request;
+ request.setUrl(inputUrl);
+ QCOMPARE(request.url(), expectedUrl);
+}
+
+void tst_QCoapRequest::setMethod_data()
+{
+ QTest::addColumn<QtCoap::Method>("method");
+
+ QTest::newRow("get") << QtCoap::Get;
+ QTest::newRow("put") << QtCoap::Put;
+ QTest::newRow("post") << QtCoap::Post;
+ QTest::newRow("delete") << QtCoap::Delete;
+ QTest::newRow("other") << QtCoap::Other;
+}
+
+void tst_QCoapRequest::setMethod()
+{
+ QFETCH(QtCoap::Method, method);
+
+ QCoapRequest request;
+ request.setMethod(method);
+ QCOMPARE(request.method(), method);
+}
+
+void tst_QCoapRequest::enableObserve()
+{
+ QCoapRequest request;
+
+ QCOMPARE(request.isObserve(), false);
+ request.enableObserve();
+
+ QCOMPARE(request.isObserve(), true);
+}
+
+void tst_QCoapRequest::copyAndDetach()
+{
+ QCoapRequest a;
+ a.setMessageId(3);
+ a.setPayload("payload");
+ a.setToken("token");
+ a.setType(QCoapMessage::Acknowledgment);
+ a.setVersion(5);
+ a.setMethod(QtCoap::Delete);
+ QUrl testUrl("coap://url:500/resource");
+ a.setUrl(testUrl);
+ QUrl testProxyUrl("test://proxyurl");
+ a.setProxyUrl(testProxyUrl);
+
+ // Test the QCoapMessage copy
+ QCoapMessage b(a);
+ QVERIFY2(b.messageId() == 3, "Message not copied correctly");
+ QVERIFY2(b.payload() == "payload", "Message not copied correctly");
+ QVERIFY2(b.token() == "token", "Message not copied correctly");
+ QVERIFY2(b.type() == QCoapMessage::Acknowledgment, "Message not copied correctly");
+ QVERIFY2(b.version() == 5, "Message not copied correctly");
+
+ // Test the QCoapRequest copy
+ QCoapRequest c(a);
+ QVERIFY2(c.method() == QtCoap::Delete, "Request not copied correctly");
+ QVERIFY2(c.url() == testUrl, "Request not copied correctly");
+ QVERIFY2(c.proxyUrl() == testProxyUrl, "Request not copied correctly");
+
+ // Detach
+ c.setMessageId(9);
+ QCOMPARE(c.messageId(), 9);
+ QCOMPARE(a.messageId(), 3);
+}
+
+QTEST_APPLESS_MAIN(tst_QCoapRequest)
+
+#include "tst_qcoaprequest.moc"
diff --git a/tests/auto/qcoapresource/qcoapresource.pro b/tests/auto/qcoapresource/qcoapresource.pro
new file mode 100644
index 0000000..6687ad2
--- /dev/null
+++ b/tests/auto/qcoapresource/qcoapresource.pro
@@ -0,0 +1,4 @@
+QT = testlib network core-private core coap
+CONFIG += testcase
+
+SOURCES += tst_qcoapresource.cpp
diff --git a/tests/auto/qcoapresource/tst_qcoapresource.cpp b/tests/auto/qcoapresource/tst_qcoapresource.cpp
new file mode 100644
index 0000000..63fe88f
--- /dev/null
+++ b/tests/auto/qcoapresource/tst_qcoapresource.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Witekio.
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCoap module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QCoreApplication>
+
+#include <QtCoap/qcoapresource.h>
+#include <QtCoap/qcoapprotocol.h>
+
+class tst_QCoapResource : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void parseCoreLink_data();
+ void parseCoreLink();
+};
+
+void tst_QCoapResource::parseCoreLink_data()
+{
+ QTest::addColumn<int>("resourceNumber");
+ QTest::addColumn<QString>("senderAddress");
+ QTest::addColumn<QList<QString>>("pathList");
+ QTest::addColumn<QList<QString>>("titleList");
+ QTest::addColumn<QList<QString>>("resourceTypeList");
+ QTest::addColumn<QList<uint>>("contentFormatList");
+ QTest::addColumn<QList<QString>>("interfaceList");
+ QTest::addColumn<QList<int>>("maximumSizeList");
+ QTest::addColumn<QList<bool>>("observableList");
+ QTest::addColumn<QByteArray>("coreLinkList");
+
+ QList<QString> pathList;
+ pathList << "/obs" << "/separate" << "/seg1" << "/seg1/seg2" << "/large-separate"
+ << "/.well-known/core" << "/multi-format" << "/path"
+ << "/path/sub1" << "/link1" << "/validate" << "/test"
+ << "/query" << "/large-post" << "/obs-non" << "/shutdown";
+
+ QList<QString> titleList;
+ titleList << "Observable resource which changes every 5 seconds"
+ << "Resource which cannot be served immediately and which cannot be acknowledged in a piggy-backed way"
+ << "Long path resource"
+ << "Long path resource"
+ << "Large resource"
+ << ""
+ << "Resource that exists in different content formats (text/plain utf8 and application/xml)"
+ << "Hierarchical link description entry"
+ << "Hierarchical link description sub-resource"
+ << "Link test resource"
+ << "Resource which varies"
+ << "Default test resource"
+ << "Resource accepting query parameters"
+ << "Handle PostOperation with two-way blockwise transfer"
+ << "Observable resource which changes every 5 seconds"
+ << "";
+
+ QList<QString> resourceTypeList;
+ resourceTypeList << "observe" << "" << "" << "" << "block" << "" << "" << "" << ""
+ << "Type1 Type2" << "" << "" << "" << "block" << "observe" << "";
+
+ QList<uint> contentFormatList;
+ contentFormatList << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 40 << 0 << 0 << 0 << 0
+ << 0 << 0 << 0 << 0;
+
+ QList<QString> interfaceList;
+ interfaceList << "" << "" << "" << "" << "" << "" << "" << "" << "" << "If1"
+ << "" << "" << "" << "" << "" << "";
+
+ QList<int> maximumSizeList;
+ maximumSizeList << -1 << -1 << -1 << -1 << 1280 << -1 << -1 << -1 << -1 << -1
+ << -1 << -1 << -1 << -1 << -1 << -1;
+
+ QList<bool> observableList;
+ observableList << true << false << false << false << false << false << false
+ << false << false << false << false << false << false << false
+ << true << false;
+
+ QByteArray coreLinks;
+ // Resources are separated by a comma
+ coreLinks.append("</obs>;obs;rt=\"observe\";title=\"Observable resource which changes every"
+ " 5 seconds\",</separate>;title=\"Resource which cannot be served immediately"
+ " and which cannot be acknowledged in a piggy-backed way\",</seg1>;title=\""
+ "Long path resource\",</seg1/seg2>;title=\"Long path resource\","
+ "</large-separate>;rt=\"block\";sz=1280;title=\"Large resource\","
+ "</.well-known/core>,</multi-format>;ct=\"0 41\";title=\"Resource that exists"
+ " in different content formats (text/plain utf8 and application/xml)\","
+ "</path>;ct=40;title=\"Hierarchical link description entry\",</path/sub1>;"
+ "title=\"Hierarchical link description sub-resource\",</link1>;if=\"If1\";"
+ "rt=\"Type1 Type2\";title=\"Link test resource\",</validate>;title=\"Resource"
+ " which varies\",</test>;title=\"Default test resource\",</query>;"
+ "title=\"Resource accepting query parameters\",</large-post>;rt=\"block\";"
+ "title=\"Handle PostOperation with two-way blockwise transfer\",</obs-non>;"
+ "obs;rt=\"observe\";title=\"Observable resource which changes every 5 "
+ "seconds\",</shutdown>");
+
+ QTest::newRow("parse") << 16
+ << QString("10.20.30.40")
+ << pathList
+ << titleList
+ << resourceTypeList
+ << contentFormatList
+ << interfaceList
+ << maximumSizeList
+ << observableList
+ << coreLinks;
+}
+
+void tst_QCoapResource::parseCoreLink()
+{
+ QFETCH(int, resourceNumber);
+ QFETCH(QString, senderAddress);
+ QFETCH(QList<QString>, pathList);
+ QFETCH(QList<QString>, titleList);
+ QFETCH(QList<QString>, resourceTypeList);
+ QFETCH(QList<uint>, contentFormatList);
+ QFETCH(QList<QString>, interfaceList);
+ QFETCH(QList<int>, maximumSizeList);
+ QFETCH(QList<bool>, observableList);
+ QFETCH(QByteArray, coreLinkList);
+
+ const QVector<QCoapResource> resourceList = QCoapProtocol::resourcesFromCoreLinkList(QHostAddress(senderAddress), coreLinkList);
+
+ QCOMPARE(resourceList.size(), resourceNumber);
+
+ int resourceIndex = 0;
+ for (const auto &resource : resourceList) {
+ QCOMPARE(resource.host(), QHostAddress(senderAddress));
+ QCOMPARE(resource.path(), pathList[resourceIndex]);
+ QCOMPARE(resource.title(), titleList[resourceIndex]);
+ QCOMPARE(resource.resourceType(), resourceTypeList[resourceIndex]);
+ QCOMPARE(resource.contentFormat(), contentFormatList[resourceIndex]);
+ QCOMPARE(resource.interface(), interfaceList[resourceIndex]);
+ QCOMPARE(resource.maximumSize(), maximumSizeList[resourceIndex]);
+ QCOMPARE(resource.observable(), observableList[resourceIndex]);
+ ++resourceIndex;
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_QCoapResource)
+
+#include "tst_qcoapresource.moc"
diff --git a/tests/tests.pro b/tests/tests.pro
new file mode 100644
index 0000000..157ef34
--- /dev/null
+++ b/tests/tests.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += auto