diff options
-rw-r--r-- | .qmake.conf | 4 | ||||
-rw-r--r-- | tests/auto/auto.pro | 6 | ||||
-rw-r--r-- | tests/auto/coapnetworksettings.h | 142 | ||||
-rw-r--r-- | tests/auto/coaptestserver.pri | 8 | ||||
-rw-r--r-- | tests/auto/qcoapclient/qcoapclient.pro | 3 | ||||
-rw-r--r-- | tests/auto/qcoapclient/testdata/ca_cert.pem | 12 | ||||
-rw-r--r-- | tests/auto/qcoapclient/testdata/local_cert.pem | 13 | ||||
-rw-r--r-- | tests/auto/qcoapclient/testdata/privkey.pem | 40 | ||||
-rw-r--r-- | tests/auto/qcoapclient/tst_qcoapclient.cpp | 390 | ||||
-rw-r--r-- | tests/auto/qcoapqudpconnection/qcoapqudpconnection.pro | 3 | ||||
-rw-r--r-- | tests/auto/qcoapqudpconnection/tst_qcoapqudpconnection.cpp | 10 | ||||
-rwxr-xr-x | tests/testserver/californium/californium.sh | 34 | ||||
-rw-r--r-- | tests/testserver/docker-compose.yml | 44 | ||||
-rwxr-xr-x | tests/testserver/freecoap/freecoap.sh | 34 |
14 files changed, 627 insertions, 116 deletions
diff --git a/.qmake.conf b/.qmake.conf index 1ed62c5..f6895bb 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,5 @@ load(qt_build_config) -MODULE_VERSION = 5.13.1 +DEFINES += QT_NO_FOREACH QT_NO_JAVA_STYLE_ITERATORS QT_NO_LINKED_LIST + +MODULE_VERSION = 5.14.0 diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 5bfc66e..32e4ac4 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -1,17 +1,15 @@ TEMPLATE = subdirs -# TODO: enable disabled tests, when CI is configured properly - SUBDIRS += \ cmake \ -# qcoapclient \ + qcoapclient \ qcoapmessage \ qcoapoption \ qcoaprequest \ qcoapresource qtConfig(private_tests): SUBDIRS += \ -# qcoapqudpconnection \ + qcoapqudpconnection \ qcoapinternalrequest \ qcoapinternalreply \ qcoapreply diff --git a/tests/auto/coapnetworksettings.h b/tests/auto/coapnetworksettings.h index 0e1ee04..7f425ff 100644 --- a/tests/auto/coapnetworksettings.h +++ b/tests/auto/coapnetworksettings.h @@ -31,6 +31,8 @@ #include <QtTest> #include <QtCore/qstring.h> #include <QtNetwork/qhostinfo.h> +#include <QtCoap/qcoapclient.h> +#include <QtCoap/qcoapsecurityconfiguration.h> /*! \internal @@ -49,23 +51,139 @@ */ namespace QtCoapNetworkSettings { - QString testServerHost() - { + +#if defined(COAP_TEST_SERVER_IP) || defined(QT_TEST_SERVER) +#define CHECK_FOR_COAP_SERVER +#else +#define CHECK_FOR_COAP_SERVER \ + QSKIP("CoAP server is not setup, skipping the test..."); \ + return; +#endif + +#if defined(QT_TEST_SERVER) && !defined(COAP_TEST_SERVER_IP) +static QString tryToResolveHostName(const QString &hostName) +{ + const auto hostInfo = QHostInfo::fromName(hostName); + if (!hostInfo.addresses().empty()) + return hostInfo.addresses().first().toString(); + + qWarning() << "Could not resolve the hostname"<< hostName; + return hostName; +} +#endif + +static QString getHostAddress(const QString &serverName) +{ #if defined(COAP_TEST_SERVER_IP) - return QStringLiteral(COAP_TEST_SERVER_IP); + Q_UNUSED(serverName); + return QStringLiteral(COAP_TEST_SERVER_IP); +#elif defined(QT_TEST_SERVER_NAME) + QString hostname = serverName % "." % QString(QT_TEST_SERVER_DOMAIN); + return tryToResolveHostName(hostname); +#elif defined(QT_TEST_SERVER) + Q_UNUSED(serverName); + QString hostname = "qt-test-server." % QString(QT_TEST_SERVER_DOMAIN); + return tryToResolveHostName(hostname); #else - static_assert(false, "COAP_TEST_SERVER_IP variable must be set"); + Q_UNUSED(serverName); + qWarning("This test will fail, " + "please set the COAP_TEST_SERVER_IP variable to specify the CoAP server."); + return ""; #endif - } +} + +QString testServerHost() +{ + static QString testServerHostAddress = getHostAddress("californium"); + return testServerHostAddress; +} - QString testServerUrl() - { - return QStringLiteral("coap://") + testServerHost() + QStringLiteral(":") - + QString::number(QtCoap::DefaultPort); +QString timeServerUrl() +{ + static QString timeServerHostAddress = getHostAddress("freecoap"); + return QStringLiteral("coaps://") + timeServerHostAddress + QStringLiteral(":5685/time"); +} + +QString testServerUrl() +{ + return QStringLiteral("coap://") + testServerHost() + QStringLiteral(":") + + QString::number(QtCoap::DefaultPort); +} + +QString testServerResource() +{ + return testServerHost() + QStringLiteral("/test"); +} + +QCoapSecurityConfiguration createConfiguration(QtCoap::SecurityMode securityMode) +{ + QCoapSecurityConfiguration configuration; + + if (securityMode == QtCoap::SecurityMode::PreSharedKey) { + configuration.setPreSharedKeyIdentity("Client_identity"); + configuration.setPreSharedKey("secretPSK"); + } else if (securityMode == QtCoap::SecurityMode::Certificate) { + const QString directory = QFINDTESTDATA("testdata"); + if (directory.isEmpty()) { + qWarning() << "Found no testdata/, cannot load certificates."; + return configuration; + } + + const auto localCertPath = directory + QDir::separator() +"local_cert.pem"; + const auto localCerts = QSslCertificate::fromPath(localCertPath); + if (localCerts.isEmpty()) { + qWarning() << "Failed to load local certificates, the" + << localCertPath + << "file was not found or it is not valid."; + } else { + configuration.setLocalCertificateChain(localCerts.toVector()); + } + + const auto caCertPath = directory + QDir::separator() + "ca_cert.pem"; + const auto caCerts = QSslCertificate::fromPath(caCertPath); + if (caCerts.isEmpty()) { + qWarning() << "Failed to load CA certificates, the" + << caCertPath + << "file was not found or it is not valid."; + } else { + configuration.setCaCertificates(caCerts.toVector()); + } + + const auto privateKeyPath = directory + QDir::separator() + "privkey.pem"; + QFile privateKey(privateKeyPath); + if (privateKey.open(QIODevice::ReadOnly)) { + QCoapPrivateKey key(privateKey.readAll(), QSsl::Ec); + if (key.isNull()) { + qWarning() << "Failed to set a private key, the key" << privateKeyPath + << "is not valid."; + } else { + configuration.setPrivateKey(key); + } + } else { + qWarning() << "Failed to read the private key" << privateKeyPath; + } } - QString testServerResource() - { - return testServerUrl() + QStringLiteral("/test"); + return configuration; +} + +bool waitForHost(const QUrl &url, QtCoap::SecurityMode security = QtCoap::SecurityMode::NoSecurity, + quint8 retries = 10) +{ + while (retries-- > 0) { + QCoapClient client(security); + if (security != QtCoap::SecurityMode::NoSecurity) + client.setSecurityConfiguration(createConfiguration(security)); + + QSignalSpy spyClientFinished(&client, SIGNAL(finished(QCoapReply *))); + client.get(url); + + spyClientFinished.wait(1000); + + if (spyClientFinished.count() == 1) + return true; } + return false; +} + } diff --git a/tests/auto/coaptestserver.pri b/tests/auto/coaptestserver.pri index 420aa89..2399826 100644 --- a/tests/auto/coaptestserver.pri +++ b/tests/auto/coaptestserver.pri @@ -1,7 +1,5 @@ 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.) +!isEmpty(COAP_TEST_SERVER_IP) { + DEFINES += COAP_TEST_SERVER_IP=\\\"$${COAP_TEST_SERVER_IP}\\\" + message(CoAP plugtest server IP set to $$COAP_TEST_SERVER_IP) } - -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 index 5c7fe1e..59cfc4f 100644 --- a/tests/auto/qcoapclient/qcoapclient.pro +++ b/tests/auto/qcoapclient/qcoapclient.pro @@ -6,3 +6,6 @@ include(../coaptestserver.pri) HEADERS += ../coapnetworksettings.h SOURCES += tst_qcoapclient.cpp + +CONFIG += unsupported/testserver +QT_TEST_SERVER_LIST = californium freecoap diff --git a/tests/auto/qcoapclient/testdata/ca_cert.pem b/tests/auto/qcoapclient/testdata/ca_cert.pem new file mode 100644 index 0000000..4f08468 --- /dev/null +++ b/tests/auto/qcoapclient/testdata/ca_cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrTCCAVOgAwIBAgIMWBtPBCPy5ZOTqJ69MAoGCCqGSM49BAMCMDgxEzARBgNV +BAMTCmR1bW15L3Jvb3QxETAPBgNVBAsTCFNvZnR3YXJlMQ4wDAYDVQQKEwVEdW1t +eTAeFw0xNjExMDMxNDUxNDhaFw0yNjExMDExNDUxNDhaMDgxEzARBgNVBAMTCmR1 +bW15L3Jvb3QxETAPBgNVBAsTCFNvZnR3YXJlMQ4wDAYDVQQKEwVEdW1teTBZMBMG +ByqGSM49AgEGCCqGSM49AwEHA0IABMJ6XvBwMC3sZRyBatX95w+/AieUhN1cNfWI +Uc4HA0IXp+WwDN7QXd7mm1FDV/wQmNx/y2MJ7OQMJUvSozRPpfKjQzBBMA8GA1Ud +EwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcEADAdBgNVHQ4EFgQUsib+R29JtRKp +G6duvLrDjM8PySUwCgYIKoZIzj0EAwIDSAAwRQIgQC6ZVZi6sXnYypt1CKlDSS2Q +W+CV62TyOdE9j9phNfECIQDno7uEc8sXHnwkCcfuZhFVAkEfE8KBPhEF7ZmqJz5c +BQ== +-----END CERTIFICATE----- diff --git a/tests/auto/qcoapclient/testdata/local_cert.pem b/tests/auto/qcoapclient/testdata/local_cert.pem new file mode 100644 index 0000000..0f51a36 --- /dev/null +++ b/tests/auto/qcoapclient/testdata/local_cert.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB4jCCAYigAwIBAgIMWBtPBCa+RXwaSLCOMAoGCCqGSM49BAMCMDgxEzARBgNV +BAMTCmR1bW15L3Jvb3QxETAPBgNVBAsTCFNvZnR3YXJlMQ4wDAYDVQQKEwVEdW1t +eTAeFw0xNjExMDMxNDUxNDhaFw0yNjExMDExNDUxNDhaMDoxFTATBgNVBAMTDGR1 +bW15L2NsaWVudDERMA8GA1UECxMIU29mdHdhcmUxDjAMBgNVBAoTBUR1bW15MFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcrIeFXrfmAu/fiT49ToNubG6u/w5GAQb +xI0V7vmFCxFSn4ttoPfEUMlUKCTFYc6HbzmFgUjct1KFGBuAfZ13M6N2MHQwDAYD +VR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDAjAPBgNVHQ8BAf8EBQMDB4AA +MB0GA1UdDgQWBBSc2q9vjtlLW1IUSzDa9X2qnJYU1DAfBgNVHSMEGDAWgBQEbS8W +F1883YRUvUnCBnkSs/NhmjAKBggqhkjOPQQDAgNIADBFAiEA9fZYVTMn9My9erGO +j5tFYsGj4A7CkWHFRj50KirZN4ECIFLEmt5SbmipqFdkiRBxkjkJEMYC+iLBeMqI +Cgi5tYDF +-----END CERTIFICATE----- diff --git a/tests/auto/qcoapclient/testdata/privkey.pem b/tests/auto/qcoapclient/testdata/privkey.pem new file mode 100644 index 0000000..75dee45 --- /dev/null +++ b/tests/auto/qcoapclient/testdata/privkey.pem @@ -0,0 +1,40 @@ +Public Key Info: + Public Key Algorithm: EC/ECDSA + Key Security Level: High (256 bits) + +curve: SECP256R1 +private key: + 00:f7:ea:23:23:78:12:38:e3:8f:e0:2b:05:72:2f:6a + 24:91:a0:54:c6:3d:ed:68:ce:6e:60:8d:0f:fd:9f:39 + c6: + +x: + 72:b2:1e:15:7a:df:98:0b:bf:7e:24:f8:f5:3a:0d:b9 + b1:ba:bb:fc:39:18:04:1b:c4:8d:15:ee:f9:85:0b:11 + + +y: + 52:9f:8b:6d:a0:f7:c4:50:c9:54:28:24:c5:61:ce:87 + 6f:39:85:81:48:dc:b7:52:85:18:1b:80:7d:9d:77:33 + + + +Public Key ID: 9C:DA:AF:6F:8E:D9:4B:5B:52:14:4B:30:DA:F5:7D:AA:9C:96:14:D4 +Public key's random art: ++--[SECP256R1]----+ +| o.=. | +| o = +E. | +| . . + . o| +| . . . . ..| +| S o . | +| o + + | +| . . o B | +| *.= | +| +=B. | ++-----------------+ + +-----BEGIN EC PRIVATE KEY----- +MHgCAQEEIQD36iMjeBI444/gKwVyL2okkaBUxj3taM5uYI0P/Z85xqAKBggqhkjO +PQMBB6FEA0IABHKyHhV635gLv34k+PU6Dbmxurv8ORgEG8SNFe75hQsRUp+LbaD3 +xFDJVCgkxWHOh285hYFI3LdShRgbgH2ddzM= +-----END EC PRIVATE KEY----- diff --git a/tests/auto/qcoapclient/tst_qcoapclient.cpp b/tests/auto/qcoapclient/tst_qcoapclient.cpp index 5ca695c..b7844b7 100644 --- a/tests/auto/qcoapclient/tst_qcoapclient.cpp +++ b/tests/auto/qcoapclient/tst_qcoapclient.cpp @@ -37,6 +37,7 @@ #include <QtCoap/qcoapresourcediscoveryreply.h> #include <QtCore/qbuffer.h> #include <QtNetwork/qnetworkdatagram.h> +#include <QtNetwork/qsslcipher.h> #include <private/qcoapclient_p.h> #include <private/qcoapqudpconnection_p.h> #include <private/qcoapprotocol_p.h> @@ -51,6 +52,7 @@ class tst_QCoapClient : public QObject Q_OBJECT private Q_SLOTS: + void initTestCase(); void incorrectUrls_data(); void incorrectUrls(); void methods_data(); @@ -65,6 +67,7 @@ private Q_SLOTS: void setBlockSize(); void requestWithQIODevice_data(); void requestWithQIODevice(); + void multipleRequests_data(); void multipleRequests(); void blockwiseReply_data(); void blockwiseReply(); @@ -81,8 +84,51 @@ private Q_SLOTS: void setMinimumTokenSize(); }; -#ifdef QT_BUILD_INTERNAL +class QCoapClientForSecurityTests : public QCoapClient +{ +public: + QCoapClientForSecurityTests(QtCoap::SecurityMode security) + : QCoapClient(security) + , securityMode(security) + { + if (security != QtCoap::SecurityMode::NoSecurity) + setSecurityConfiguration(createConfiguration(security)); + } + bool securitySetupMissing() const + { +#if QT_CONFIG(dtls) + if (securityMode == QtCoap::SecurityMode::PreSharedKey) { + // The Californium test server accepts only PSK-AES128-CCM8 and PSK-AES128-CBC-SHA256 + // ciphers. Make sure that the required ciphers are present, otherwise the test + // should be skipped. + const auto ciphers = QSslConfiguration::defaultDtlsConfiguration().ciphers(); + const auto it = std::find_if(ciphers.cbegin(), ciphers.cend(), + [](const QSslCipher &cipher) { + return cipher.name() == "PSK-AES128-CCM8" + || cipher.name() == "PSK-AES128-CBC-SHA256"; + }); + return it == ciphers.cend(); + } + // For all other modes the setup should be OK, return false. + return false; +#else + // If dtls is not configured, the setup for the secure modes is missing, + // but it is OK if security is not used. + return isSecure(); +#endif + } + + bool isSecure() const + { + return securityMode != QtCoap::SecurityMode::NoSecurity; + } + +private: + QtCoap::SecurityMode securityMode; +}; + +#ifdef QT_BUILD_INTERNAL class QCoapQUdpConnectionSocketTestsPrivate : public QCoapQUdpConnectionPrivate { bool bind() override @@ -194,6 +240,17 @@ public slots: } }; +void tst_QCoapClient::initTestCase() +{ +#if defined(COAP_TEST_SERVER_IP) || defined(QT_TEST_SERVER) + QVERIFY2(waitForHost(testServerHost()), "Failed to connect to Californium plugtest server."); +#if QT_CONFIG(dtls) + QVERIFY2(waitForHost(timeServerUrl(), QtCoap::SecurityMode::Certificate), + "Failed to connect to FreeCoAP sample time server."); +#endif +#endif +} + void tst_QCoapClient::incorrectUrls_data() { QWARN("Expect warnings here..."); @@ -235,32 +292,73 @@ void tst_QCoapClient::methods_data() { QTest::addColumn<QUrl>("url"); QTest::addColumn<QtCoap::Method>("method"); - - QTest::newRow("get") << QUrl(testServerResource()) << QtCoap::Method::Get; - QTest::newRow("get_no_port") << QUrl("coap://" + testServerHost() + "/test") - << QtCoap::Method::Get; - QTest::newRow("get_no_scheme_no_port") << QUrl(testServerHost() + "/test") - << QtCoap::Method::Get; - QTest::newRow("post") << QUrl(testServerResource()) - << QtCoap::Method::Post; - QTest::newRow("post_no_scheme_no_port") << QUrl(testServerHost() + "/test") - << QtCoap::Method::Post; - QTest::newRow("put") << QUrl(testServerResource()) - << QtCoap::Method::Put; - QTest::newRow("put_no_scheme_no_port") << QUrl(testServerHost() + "/test") - << QtCoap::Method::Put; - QTest::newRow("delete") << QUrl(testServerResource()) - << QtCoap::Method::Delete; - QTest::newRow("delete_no_scheme_no_port") << QUrl(testServerHost() + "/test") - << QtCoap::Method::Delete; + QTest::addColumn<QtCoap::SecurityMode>("security"); + + QTest::newRow("get") + << QUrl(testServerResource()) + << QtCoap::Method::Get + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("get_no_port") + << QUrl("coap://" + testServerHost() + "/test") + << QtCoap::Method::Get + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("get_no_scheme_no_port") + << QUrl(testServerHost() + "/test") + << QtCoap::Method::Get + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("get_psk") + << QUrl(testServerResource()) << QtCoap::Method::Get + << QtCoap::SecurityMode::PreSharedKey; + QTest::newRow("get_cert") + << QUrl(timeServerUrl()) << QtCoap::Method::Get + << QtCoap::SecurityMode::Certificate; + QTest::newRow("post") + << QUrl(testServerResource()) + << QtCoap::Method::Post + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("post_no_scheme_no_port") + << QUrl(testServerHost() + "/test") + << QtCoap::Method::Post + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("post_psk") + << QUrl(testServerResource()) << QtCoap::Method::Post + << QtCoap::SecurityMode::PreSharedKey; + QTest::newRow("put") + << QUrl(testServerResource()) + << QtCoap::Method::Put + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("put_no_scheme_no_port") + << QUrl(testServerHost() + "/test") + << QtCoap::Method::Put + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("put_psk") + << QUrl(testServerResource()) << QtCoap::Method::Put + << QtCoap::SecurityMode::PreSharedKey; + QTest::newRow("delete") + << QUrl(testServerResource()) + << QtCoap::Method::Delete + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("delete_no_scheme_no_port") + << QUrl(testServerHost() + "/test") + << QtCoap::Method::Delete + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("delete_psk") + << QUrl(testServerResource()) << QtCoap::Method::Delete + << QtCoap::SecurityMode::PreSharedKey; } void tst_QCoapClient::methods() { + CHECK_FOR_COAP_SERVER; + QFETCH(QUrl, url); QFETCH(QtCoap::Method, method); + QFETCH(QtCoap::SecurityMode, security); + + QCoapClientForSecurityTests client(security); + if (client.securitySetupMissing()) + QSKIP("Skipping this test, security is not configured properly"); - QCoapClient client; QCoapRequest request(url); QSignalSpy spyClientFinished(&client, SIGNAL(finished(QCoapReply *))); @@ -281,7 +379,8 @@ void tst_QCoapClient::methods() QVERIFY2(!reply.isNull(), "Request failed unexpectedly"); #ifdef QT_BUILD_INTERNAL - QCOMPARE(reply->url(), QCoapRequestPrivate::adjustedUrl(url, false)); + QCOMPARE(reply->url(), + QCoapRequestPrivate::adjustedUrl(url, client.isSecure())); #endif QSignalSpy spyReplyFinished(reply.data(), SIGNAL(finished(QCoapReply *))); QTRY_COMPARE(spyReplyFinished.count(), 1); @@ -312,6 +411,8 @@ void tst_QCoapClient::methods() void tst_QCoapClient::separateMethod() { + CHECK_FOR_COAP_SERVER; + QCoapClient client; QScopedPointer<QCoapReply> reply(client.get(QUrl(testServerUrl() + "/separate"))); @@ -327,6 +428,8 @@ void tst_QCoapClient::separateMethod() void tst_QCoapClient::removeReply() { + CHECK_FOR_COAP_SERVER; + QCoapClient client; QCoapReply *reply = client.get(QUrl(testServerResource())); QVERIFY2(reply != nullptr, "Request failed unexpectedly"); @@ -384,6 +487,8 @@ void tst_QCoapClient::requestWithQIODevice_data() void tst_QCoapClient::requestWithQIODevice() { + CHECK_FOR_COAP_SERVER; + QFETCH(QUrl, url); QCoapClient client; @@ -414,54 +519,63 @@ void tst_QCoapClient::requestWithQIODevice() } } +void tst_QCoapClient::multipleRequests_data() +{ + QTest::addColumn<QtCoap::SecurityMode>("security"); + + QTest::newRow("multiple_requests") << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("multiple_requests_secure") << QtCoap::SecurityMode::PreSharedKey; +} + void tst_QCoapClient::multipleRequests() { - QCoapClient client; + CHECK_FOR_COAP_SERVER; + + QFETCH(QtCoap::SecurityMode, security); + + QCoapClientForSecurityTests client(security); + if (client.securitySetupMissing()) + QSKIP("Skipping this test, security is not configured properly"); + 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); + const uint8_t requestCount = 4; + QVector<QSharedPointer<QCoapReply>> replies; + QVector<QSharedPointer<QSignalSpy>> signalSpies; + for (uint8_t i = 0; i < requestCount; ++i) { + QCoapRequest request; + const auto token = "token" + QByteArray::number(i); + request.setToken(token); + request.setUrl(url); + + QSharedPointer<QCoapReply> reply(client.get(request)); + const auto errorMsg = QStringLiteral("Request number %1 failed unexpectedly").arg(i); + QVERIFY2(!reply.isNull(), qPrintable(errorMsg)); + replies.push_back(reply); + + QSharedPointer<QSignalSpy> signalSpy( + new QSignalSpy(reply.data(), SIGNAL(finished(QCoapReply *)))); + signalSpies.push_back(signalSpy); + } + + for (const auto &signalSpy : signalSpies) + QTRY_COMPARE(signalSpy->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::ResponseCode::Content); - QCOMPARE(replyGet2->responseCode(), QtCoap::ResponseCode::Content); - QCOMPARE(replyGet3->responseCode(), QtCoap::ResponseCode::Content); - QCOMPARE(replyGet4->responseCode(), QtCoap::ResponseCode::Content); - - QVERIFY(replyData1 != replyData2); - QVERIFY(replyData1 != replyData3); - QVERIFY(replyData1 != replyData4); - QVERIFY(replyData2 != replyData3); - QVERIFY(replyData2 != replyData4); - QVERIFY(replyData3 != replyData4); + for (uint8_t i = 0; i < requestCount; ++i) { + QCOMPARE(replies[i]->responseCode(), QtCoap::ResponseCode::Content); + QByteArray replyData = replies[i]->readAll(); + const auto token = "token" + QByteArray::number(i); + QVERIFY(replyData.contains(token.toHex())); + } } void tst_QCoapClient::socketError() { #ifdef QT_BUILD_INTERNAL + CHECK_FOR_COAP_SERVER; + QCoapClientForSocketErrorTests client; QUrl url = QUrl(testServerResource()); @@ -494,6 +608,8 @@ void tst_QCoapClient::timeout_data() void tst_QCoapClient::timeout() { #ifdef QT_BUILD_INTERNAL + CHECK_FOR_COAP_SERVER; + QFETCH(uint, timeout); QFETCH(uint, maximumRetransmitCount); @@ -542,10 +658,13 @@ void tst_QCoapClient::timeout() void tst_QCoapClient::abort() { + CHECK_FOR_COAP_SERVER; + QCoapClient client; QUrl url = QUrl(testServerUrl() + "/large"); QScopedPointer<QCoapReply> reply(client.get(url)); + QVERIFY(!reply.isNull()); QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished); QSignalSpy spyReplyAborted(reply.data(), &QCoapReply::aborted); QSignalSpy spyReplyError(reply.data(), &QCoapReply::error); @@ -566,6 +685,7 @@ void tst_QCoapClient::blockwiseReply_data() QTest::addColumn<QUrl>("url"); QTest::addColumn<QCoapMessage::Type>("type"); QTest::addColumn<QByteArray>("replyData"); + QTest::addColumn<QtCoap::SecurityMode>("security"); QByteArray data; data.append("/-------------------------------------------------------------\\\n"); @@ -592,36 +712,58 @@ void tst_QCoapClient::blockwiseReply_data() QTest::newRow("get_large") << QUrl(testServerUrl() + "/large") << QCoapMessage::Type::NonConfirmable - << data; + << data + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("get_large_secure") + << QUrl(testServerHost() + "/large") + << QCoapMessage::Type::NonConfirmable + << data + << QtCoap::SecurityMode::PreSharedKey; QTest::newRow("get_large_separate") - << QUrl(testServerUrl() + "/large-separate") + << QUrl(testServerHost() + "/large-separate") << QCoapMessage::Type::NonConfirmable - << data; + << data + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("get_large_confirmable") << QUrl(testServerUrl() + "/large") << QCoapMessage::Type::Confirmable - << data; + << data + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("get_large_confirmable_secure") + << QUrl(testServerHost() + "/large") + << QCoapMessage::Type::Confirmable + << data + << QtCoap::SecurityMode::PreSharedKey; QTest::newRow("get_large_separate_confirmable") << QUrl(testServerUrl() + "/large-separate") << QCoapMessage::Type::Confirmable - << data; + << data + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("get_large_16bits") << QUrl(testServerUrl() + "/large") << QCoapMessage::Type::NonConfirmable - << data; + << data + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("get_large_16bits_confirmable") << QUrl(testServerUrl() + "/large") << QCoapMessage::Type::Confirmable - << data; + << data + << QtCoap::SecurityMode::NoSecurity; } void tst_QCoapClient::blockwiseReply() { + CHECK_FOR_COAP_SERVER; + QFETCH(QUrl, url); QFETCH(QCoapMessage::Type, type); QFETCH(QByteArray, replyData); + QFETCH(QtCoap::SecurityMode, security); + + QCoapClientForSecurityTests client(security); + if (client.securitySetupMissing()) + QSKIP("Skipping this test, security is not configured properly"); - QCoapClient client; QCoapRequest request(url); if (qstrncmp(QTest::currentDataTag(), "get_large_16bits", 16) == 0) @@ -629,6 +771,7 @@ void tst_QCoapClient::blockwiseReply() request.setType(type); QScopedPointer<QCoapReply> reply(client.get(request)); + QVERIFY(!reply.isNull()); QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished); QSignalSpy spyReplyError(reply.data(), &QCoapReply::error); Helper helper; @@ -646,33 +789,51 @@ void tst_QCoapClient::blockwiseRequest_data() QTest::addColumn<QByteArray>("requestData"); QTest::addColumn<QtCoap::ResponseCode>("responseCode"); QTest::addColumn<QByteArray>("replyData"); + QTest::addColumn<QtCoap::SecurityMode>("security"); 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::Type::NonConfirmable - << data - << QtCoap::ResponseCode::MethodNotAllowed - << QByteArray(); - QTest::newRow("large_post_large_reply") << QUrl(testServerUrl() + "/large-post") - << QCoapMessage::Type::NonConfirmable - << data - << QtCoap::ResponseCode::Changed - << data.toUpper(); + QTest::newRow("large_post_empty_reply") + << QUrl(testServerUrl() + "/query") + << QCoapMessage::Type::NonConfirmable + << data + << QtCoap::ResponseCode::MethodNotAllowed + << QByteArray() + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("large_post_large_reply") + << QUrl(testServerUrl() + "/large-post") + << QCoapMessage::Type::NonConfirmable + << data + << QtCoap::ResponseCode::Changed + << data.toUpper() + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("large_post_large_reply_secure") + << QUrl(testServerHost() + "/large-post") + << QCoapMessage::Type::NonConfirmable + << data + << QtCoap::ResponseCode::Changed + << data.toUpper() + << QtCoap::SecurityMode::PreSharedKey; } void tst_QCoapClient::blockwiseRequest() { + CHECK_FOR_COAP_SERVER; + QFETCH(QUrl, url); QFETCH(QCoapMessage::Type, type); QFETCH(QByteArray, requestData); QFETCH(QtCoap::ResponseCode, responseCode); QFETCH(QByteArray, replyData); + QFETCH(QtCoap::SecurityMode, security); + + QCoapClientForSecurityTests client(security); + if (client.securitySetupMissing()) + QSKIP("Skipping this test, security is not configured properly"); - QCoapClient client; client.setBlockSize(16); QCoapRequest request(url); @@ -680,6 +841,7 @@ void tst_QCoapClient::blockwiseRequest() request.addOption(QCoapOption::ContentFormat); QScopedPointer<QCoapReply> reply(client.post(request, requestData)); + QVERIFY(!reply.isNull()); QSignalSpy spyReplyFinished(reply.data(), SIGNAL(finished(QCoapReply *))); QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.count(), 1, 30000); @@ -693,29 +855,41 @@ void tst_QCoapClient::discover_data() { QTest::addColumn<QUrl>("url"); QTest::addColumn<int>("resourceNumber"); + QTest::addColumn<QtCoap::SecurityMode>("security"); // Californium test server exposes 29 resources - QTest::newRow("discover") << QUrl(testServerUrl()) - << 29; - QTest::newRow("discover_no_scheme_no_port") << QUrl(testServerHost()) - << 29; + QTest::newRow("discover") + << QUrl(testServerHost()) + << 29 + << QtCoap::SecurityMode::NoSecurity; + QTest::newRow("discover_secure") + << QUrl(testServerHost()) + << 29 + << QtCoap::SecurityMode::PreSharedKey; } void tst_QCoapClient::discover() { + CHECK_FOR_COAP_SERVER; + QFETCH(QUrl, url); QFETCH(int, resourceNumber); + QFETCH(QtCoap::SecurityMode, security); - QCoapClient client; + QCoapClientForSecurityTests client(security); + if (client.securitySetupMissing()) + QSKIP("Skipping this test, security is not configured properly"); QScopedPointer<QCoapResourceDiscoveryReply> resourcesReply(client.discover(url)); // /.well-known/core + QVERIFY(!resourcesReply.isNull()); QSignalSpy spyReplyFinished(resourcesReply.data(), SIGNAL(finished(QCoapReply *))); QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.count(), 1, 30000); const auto discoverUrl = QUrl(url.toString() + "/.well-known/core"); #ifdef QT_BUILD_INTERNAL - QCOMPARE(resourcesReply->url(), QCoapRequestPrivate::adjustedUrl(discoverUrl, false)); + QCOMPARE(resourcesReply->url(), + QCoapRequestPrivate::adjustedUrl(discoverUrl, client.isSecure())); #endif QCOMPARE(resourcesReply->resources().length(), resourceNumber); QCOMPARE(resourcesReply->request().method(), QtCoap::Method::Get); @@ -728,62 +902,90 @@ 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::Type>("type"); + QTest::addColumn<QtCoap::SecurityMode>("security"); QTest::newRow("observe") << QUrl(testServerUrl() + "/obs") - << QCoapMessage::Type::NonConfirmable; + << QCoapMessage::Type::NonConfirmable + << QtCoap::SecurityMode::NoSecurity; + + QTest::newRow("observe_secure") + << QUrl(testServerHost() + "/obs") + << QCoapMessage::Type::NonConfirmable + << QtCoap::SecurityMode::PreSharedKey; QTest::newRow("observe_no_scheme_no_port") << QUrl(testServerHost() + "/obs") - << QCoapMessage::Type::NonConfirmable; + << QCoapMessage::Type::NonConfirmable + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("observe_confirmable") << QUrl(testServerUrl() + "/obs") - << QCoapMessage::Type::Confirmable; + << QCoapMessage::Type::Confirmable + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("observe_receive") << QUrl(testServerUrl() + "/obs-non") - << QCoapMessage::Type::NonConfirmable; + << QCoapMessage::Type::NonConfirmable + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("observe_receive_confirmable") << QUrl(testServerUrl() + "/obs-non") - << QCoapMessage::Type::Confirmable; + << QCoapMessage::Type::Confirmable + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("observe_large") << QUrl(testServerUrl() + "/obs-large") - << QCoapMessage::Type::NonConfirmable; + << QCoapMessage::Type::NonConfirmable + << QtCoap::SecurityMode::NoSecurity; + + QTest::newRow("observe_large_secure") + << QUrl(testServerHost() + "/obs-large") + << QCoapMessage::Type::NonConfirmable + << QtCoap::SecurityMode::PreSharedKey; QTest::newRow("observe_large_confirmable") << QUrl(testServerUrl() + "/obs-large") - << QCoapMessage::Type::Confirmable; + << QCoapMessage::Type::Confirmable + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("observe_pumping") << QUrl(testServerUrl() + "/obs-pumping") - << QCoapMessage::Type::NonConfirmable; + << QCoapMessage::Type::NonConfirmable + << QtCoap::SecurityMode::NoSecurity; QTest::newRow("observe_pumping_confirmable") << QUrl(testServerUrl() + "/obs-pumping") - << QCoapMessage::Type::Confirmable; + << QCoapMessage::Type::Confirmable + << QtCoap::SecurityMode::NoSecurity; } void tst_QCoapClient::observe() { + CHECK_FOR_COAP_SERVER; + QFETCH(QUrl, url); QFETCH(QCoapMessage::Type, type); + QFETCH(QtCoap::SecurityMode, security); + + QCoapClientForSecurityTests client(security); + if (client.securitySetupMissing()) + QSKIP("Skipping this test, security is not configured properly"); - QCoapClient client; QCoapRequest request(url); request.setType(type); QSharedPointer<QCoapReply> reply(client.observe(request), &QObject::deleteLater); + QVERIFY(!reply.isNull()); QSignalSpy spyReplyNotified(reply.data(), &QCoapReply::notified); QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished); QTRY_COMPARE_WITH_TIMEOUT(spyReplyNotified.count(), 3, 30000); client.cancelObserve(reply.data()); #ifdef QT_BUILD_INTERNAL - QCOMPARE(reply->url(), QCoapRequestPrivate::adjustedUrl(url, false)); + QCOMPARE(reply->url(), + QCoapRequestPrivate::adjustedUrl(url, client.isSecure())); #endif QCOMPARE(reply->request().method(), QtCoap::Method::Get); @@ -907,7 +1109,7 @@ void tst_QCoapClient::setMinimumTokenSize() QScopedPointer<QCoapReply> reply; reply.reset(client.get(QCoapRequest("127.0.0.1"))); - QTRY_COMPARE_WITH_TIMEOUT(spyClientError.count(), 1, 10); + QTRY_COMPARE_WITH_TIMEOUT(spyClientError.count(), 1, 100); QVERIFY(reply->request().tokenLength() >= expectedMinSize); QVERIFY(reply->request().tokenLength() <= maxSize); } diff --git a/tests/auto/qcoapqudpconnection/qcoapqudpconnection.pro b/tests/auto/qcoapqudpconnection/qcoapqudpconnection.pro index c10d6e6..984d62d 100644 --- a/tests/auto/qcoapqudpconnection/qcoapqudpconnection.pro +++ b/tests/auto/qcoapqudpconnection/qcoapqudpconnection.pro @@ -7,3 +7,6 @@ HEADERS += ../coapnetworksettings.h SOURCES += \ tst_qcoapqudpconnection.cpp + +CONFIG += unsupported/testserver +QT_TEST_SERVER_LIST = californium diff --git a/tests/auto/qcoapqudpconnection/tst_qcoapqudpconnection.cpp b/tests/auto/qcoapqudpconnection/tst_qcoapqudpconnection.cpp index 8876214..fb30392 100644 --- a/tests/auto/qcoapqudpconnection/tst_qcoapqudpconnection.cpp +++ b/tests/auto/qcoapqudpconnection/tst_qcoapqudpconnection.cpp @@ -50,6 +50,7 @@ class tst_QCoapQUdpConnection : public QObject Q_OBJECT private Q_SLOTS: + void initTestCase(); void ctor(); void connectToHost(); void reconnect(); @@ -72,6 +73,13 @@ public: } }; +void tst_QCoapQUdpConnection::initTestCase() +{ +#if defined(COAP_TEST_SERVER_IP) || defined(QT_TEST_SERVER) + QVERIFY2(waitForHost(testServerHost()), "Failed to connect to Californium plugtest server."); +#endif +} + void tst_QCoapQUdpConnection::ctor() { QCoapQUdpConnection connection; @@ -166,6 +174,8 @@ void tst_QCoapQUdpConnection::sendRequest_data() void tst_QCoapQUdpConnection::sendRequest() { + CHECK_FOR_COAP_SERVER; + QFETCH(QString, protocol); QFETCH(QString, host); QFETCH(QString, path); diff --git a/tests/testserver/californium/californium.sh b/tests/testserver/californium/californium.sh new file mode 100755 index 0000000..82072e4 --- /dev/null +++ b/tests/testserver/californium/californium.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of the Qt Toolkit. +## +## $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$ +## +############################################################################# + +set -ex + +java -jar /root/src/californium/demo-apps/run/cf-plugtest-server-1.1.0-SNAPSHOT.jar & diff --git a/tests/testserver/docker-compose.yml b/tests/testserver/docker-compose.yml new file mode 100644 index 0000000..44ca4ae --- /dev/null +++ b/tests/testserver/docker-compose.yml @@ -0,0 +1,44 @@ +version: '2.1' + +# The tag of images is used by docker compose file to launch the corresponding +# docker containers. The value of tag comes from the provisioning script +# (coin/provisioning/.../testserver/docker_testserver.sh). The script gets SHA-1 +# of each server context as the tag of docker images. If one of the server +# contexts gets changes, please make sure to update this compose file as well. +# You can run command 'docker images' to list all the tag of test server images. +# For example: +# REPOSITORY TAG +# qt-test-server-apache2 537fe302f61851d1663f41495230d8e3554a4a13 + +services: + californium: + extends: + file: ${SHARED_DATA}/docker-compose-common.yml + service: ${SHARED_SERVICE} + container_name: qt-test-server-californium + hostname: ${HOST_NAME:-californium} + build: + context: . + args: + provisioningImage: qt-test-server-californium:dd4a2ab0b113fbd45c5d6e92b43341f5eb521926 + serviceDir: ./californium + ports: + - "5683:5683/udp" + - "5684:5684/udp" + entrypoint: ./startup.sh + command: service/californium.sh + freecoap: + extends: + file: ${SHARED_DATA}/docker-compose-common.yml + service: ${SHARED_SERVICE} + container_name: qt-test-server-freecoap + hostname: ${HOST_NAME:-freecoap} + build: + context: . + args: + provisioningImage: qt-test-server-freecoap:e2d7208ea82e623e2769d9ee1f052b58537d2f35 + serviceDir: ./freecoap + ports: + - "5685:5685/udp" + entrypoint: ./startup.sh + command: service/freecoap.sh diff --git a/tests/testserver/freecoap/freecoap.sh b/tests/testserver/freecoap/freecoap.sh new file mode 100755 index 0000000..4b15df6 --- /dev/null +++ b/tests/testserver/freecoap/freecoap.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +############################################################################# +## +## Copyright (C) 2019 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of the test suite of the Qt Toolkit. +## +## $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$ +## +############################################################################# + +set -ex + +(cd /root/src/FreeCoAP/sample/time_server && ./time_server 0.0.0.0 5685 &) |