mirror of https://github.com/qt/qtcoap.git
1103 lines
38 KiB
C++
1103 lines
38 KiB
C++
// Copyright (C) 2017 Witekio.
|
|
// Copyright (C) 2018 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
|
|
|
#include <QtTest>
|
|
#include <QCoreApplication>
|
|
|
|
#include <QtCoap/qcoapclient.h>
|
|
#include <QtCoap/qcoaprequest.h>
|
|
#include <QtCoap/qcoapreply.h>
|
|
#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>
|
|
#include <private/qcoaprequest_p.h>
|
|
|
|
#include "../coapnetworksettings.h"
|
|
|
|
using namespace QtCoapNetworkSettings;
|
|
|
|
class tst_QCoapClient : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private Q_SLOTS:
|
|
void initTestCase();
|
|
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_data();
|
|
void multipleRequests();
|
|
void blockwiseReply_data();
|
|
void blockwiseReply();
|
|
void blockwiseRequest_data();
|
|
void blockwiseRequest();
|
|
void discover_data();
|
|
void discover();
|
|
void observe_data();
|
|
void observe();
|
|
void confirmableMulticast();
|
|
void multicast();
|
|
void multicast_blockwise();
|
|
void setMinimumTokenSize_data();
|
|
void setMinimumTokenSize();
|
|
};
|
|
|
|
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
|
|
{
|
|
// Force a socket binding error
|
|
QUdpSocket anotherSocket;
|
|
anotherSocket.bind(QHostAddress::Any, 6080);
|
|
return socket()->bind(QHostAddress::Any, 6080);
|
|
}
|
|
};
|
|
|
|
class QCoapQUdpConnectionSocketTests : public QCoapQUdpConnection
|
|
{
|
|
public:
|
|
QCoapQUdpConnectionSocketTests() :
|
|
QCoapQUdpConnection(*new QCoapQUdpConnectionSocketTestsPrivate)
|
|
{
|
|
createSocket();
|
|
}
|
|
|
|
private:
|
|
Q_DECLARE_PRIVATE(QCoapQUdpConnectionSocketTests)
|
|
};
|
|
|
|
class QCoapClientForSocketErrorTests : public QCoapClient
|
|
{
|
|
public:
|
|
QCoapClientForSocketErrorTests()
|
|
{
|
|
QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
|
|
privateClient->setConnection(new QCoapQUdpConnectionSocketTests());
|
|
}
|
|
|
|
QCoapQUdpConnection *connection()
|
|
{
|
|
QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
|
|
return qobject_cast<QCoapQUdpConnection*>(privateClient->connection);
|
|
}
|
|
};
|
|
|
|
class QCoapClientForTests : public QCoapClient
|
|
{
|
|
public:
|
|
QCoapClientForTests() {}
|
|
|
|
QCoapProtocol *protocol()
|
|
{
|
|
QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
|
|
return privateClient->protocol;
|
|
}
|
|
QCoapQUdpConnection *connection()
|
|
{
|
|
QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
|
|
return qobject_cast<QCoapQUdpConnection*>(privateClient->connection);
|
|
}
|
|
};
|
|
|
|
class QCoapConnectionMulticastTests : public QCoapConnection
|
|
{
|
|
public:
|
|
~QCoapConnectionMulticastTests() override = default;
|
|
|
|
void bind(const QString &host, quint16 port) override
|
|
{
|
|
Q_UNUSED(host)
|
|
Q_UNUSED(port)
|
|
// Do nothing
|
|
}
|
|
|
|
void writeData(const QByteArray &data, const QString &host, quint16 port) override
|
|
{
|
|
Q_UNUSED(data)
|
|
Q_UNUSED(host)
|
|
Q_UNUSED(port)
|
|
// Do nothing
|
|
}
|
|
|
|
void close() override {}
|
|
};
|
|
|
|
class QCoapClientForMulticastTests : public QCoapClient
|
|
{
|
|
public:
|
|
QCoapClientForMulticastTests()
|
|
{
|
|
QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
|
|
privateClient->setConnection(new QCoapConnectionMulticastTests());
|
|
}
|
|
|
|
QCoapConnection *connection()
|
|
{
|
|
QCoapClientPrivate *privateClient = static_cast<QCoapClientPrivate *>(d_func());
|
|
return privateClient->connection;
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
class Helper : public QObject
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
Helper() {}
|
|
|
|
public slots:
|
|
void onError(QCoapReply *, QtCoap::Error error)
|
|
{
|
|
qWarning() << "Network error" << error << "occurred";
|
|
}
|
|
};
|
|
|
|
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()
|
|
{
|
|
qWarning("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 = QLatin1String("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::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");
|
|
|
|
QCoapRequest request(url);
|
|
|
|
QSignalSpy spyClientFinished(&client, &QCoapClient::finished);
|
|
|
|
const QByteArray payload = "test payload";
|
|
QScopedPointer<QCoapReply> reply;
|
|
if (qstrncmp(QTest::currentDataTag(), "get", 3) == 0) {
|
|
reply.reset(client.get(request));
|
|
} else if (qstrncmp(QTest::currentDataTag(), "post", 4) == 0) {
|
|
request.setPayload(payload);
|
|
reply.reset(client.post(request));
|
|
} else if (qstrncmp(QTest::currentDataTag(), "put", 3) == 0) {
|
|
request.setPayload(payload);
|
|
reply.reset(client.put(request));
|
|
} else if (qstrncmp(QTest::currentDataTag(), "delete", 6) == 0) {
|
|
reply.reset(client.deleteResource(request));
|
|
} else {
|
|
QString error = QLatin1String("Unrecognized method '") + QTest::currentDataTag() + "'";
|
|
QFAIL(qPrintable(error));
|
|
}
|
|
|
|
QVERIFY2(!reply.isNull(), "Request failed unexpectedly");
|
|
#ifdef QT_BUILD_INTERNAL
|
|
QCOMPARE(reply->url(),
|
|
QCoapRequestPrivate::adjustedUrl(url, client.isSecure()));
|
|
#endif
|
|
QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished);
|
|
QTRY_COMPARE(spyReplyFinished.size(), 1);
|
|
QTRY_COMPARE(spyClientFinished.size(), 1);
|
|
|
|
QByteArray replyData;
|
|
if (!reply.isNull()) {
|
|
replyData = reply->readAll();
|
|
if (qstrncmp(QTest::currentDataTag(), "get", 3) == 0) {
|
|
QVERIFY(!replyData.isEmpty());
|
|
QCOMPARE(reply->responseCode(), QtCoap::ResponseCode::Content);
|
|
} else if (qstrncmp(QTest::currentDataTag(), "post", 4) == 0) {
|
|
QVERIFY(replyData.isEmpty());
|
|
QCOMPARE(reply->responseCode(), QtCoap::ResponseCode::Created);
|
|
QCOMPARE(reply->request().payload(), payload);
|
|
} else if (qstrncmp(QTest::currentDataTag(), "put", 3) == 0) {
|
|
QVERIFY(replyData.isEmpty());
|
|
QCOMPARE(reply->responseCode(), QtCoap::ResponseCode::Changed);
|
|
QCOMPARE(reply->request().payload(), payload);
|
|
} else if (qstrncmp(QTest::currentDataTag(), "delete", 6) == 0) {
|
|
QVERIFY(replyData.isEmpty());
|
|
QCOMPARE(reply->responseCode(), QtCoap::ResponseCode::Deleted);
|
|
} else {
|
|
QString error = QLatin1String("Unrecognized method '") + QTest::currentDataTag() + "'";
|
|
QFAIL(qPrintable(error));
|
|
}
|
|
}
|
|
QCOMPARE(reply->request().method(), method);
|
|
}
|
|
|
|
void tst_QCoapClient::separateMethod()
|
|
{
|
|
CHECK_FOR_COAP_SERVER;
|
|
|
|
QCoapClient client;
|
|
QScopedPointer<QCoapReply> reply(client.get(QUrl(testServerUrl() + "/separate")));
|
|
|
|
QVERIFY2(!reply.isNull(), "Request failed unexpectedly");
|
|
QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished);
|
|
QTRY_COMPARE(spyReplyFinished.size(), 1);
|
|
|
|
QByteArray replyData = reply->readAll();
|
|
|
|
QVERIFY(!replyData.isEmpty());
|
|
QCOMPARE(reply->responseCode(), QtCoap::ResponseCode::Content);
|
|
}
|
|
|
|
void tst_QCoapClient::removeReply()
|
|
{
|
|
CHECK_FOR_COAP_SERVER;
|
|
|
|
QCoapClient client;
|
|
QCoapReply *reply = client.get(QUrl(testServerResource()));
|
|
QVERIFY2(reply != nullptr, "Request failed unexpectedly");
|
|
connect(reply, &QCoapReply::finished, this, [reply]() { reply->deleteLater(); });
|
|
|
|
try {
|
|
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()
|
|
{
|
|
#ifdef QT_BUILD_INTERNAL
|
|
QFETCH(int, blockSizeSet);
|
|
QFETCH(int, blockSizeExpected);
|
|
|
|
QCoapClientForTests client;
|
|
client.setBlockSize(static_cast<quint16>(blockSizeSet));
|
|
|
|
QEventLoop eventLoop;
|
|
QTimer::singleShot(1000, &eventLoop, &QEventLoop::quit);
|
|
eventLoop.exec();
|
|
|
|
QCOMPARE(client.protocol()->blockSize(), blockSizeExpected);
|
|
#else
|
|
QSKIP("Not an internal build, skipping this test");
|
|
#endif
|
|
}
|
|
|
|
void tst_QCoapClient::requestWithQIODevice_data()
|
|
{
|
|
QTest::addColumn<QUrl>("url");
|
|
|
|
QTest::newRow("post") << QUrl(testServerResource());
|
|
QTest::newRow("put") << QUrl(testServerResource());
|
|
}
|
|
|
|
void tst_QCoapClient::requestWithQIODevice()
|
|
{
|
|
CHECK_FOR_COAP_SERVER;
|
|
|
|
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(), &QCoapReply::finished);
|
|
QTRY_COMPARE(spyReplyFinished.size(), 1);
|
|
|
|
QByteArray replyData = reply->readAll();
|
|
|
|
if (qstrcmp(QTest::currentDataTag(), "post") == 0) {
|
|
QVERIFY(replyData.isEmpty());
|
|
QCOMPARE(reply->responseCode(), QtCoap::ResponseCode::Created);
|
|
} else if (qstrcmp(QTest::currentDataTag(), "put") == 0) {
|
|
QVERIFY(replyData.isEmpty());
|
|
QCOMPARE(reply->responseCode(), QtCoap::ResponseCode::Changed);
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
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, &QCoapClient::finished);
|
|
|
|
const uint8_t requestCount = 4;
|
|
QList<QSharedPointer<QCoapReply>> replies;
|
|
QList<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(), &QCoapReply::finished));
|
|
signalSpies.push_back(signalSpy);
|
|
}
|
|
|
|
for (const auto &signalSpy : signalSpies)
|
|
QTRY_COMPARE(signalSpy->size(), 1);
|
|
QTRY_COMPARE(spyClientFinished.size(), 4);
|
|
|
|
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);
|
|
// The californium server now returns the hex token in uppercase
|
|
QVERIFY(replyData.contains(token.toHex().toUpper()));
|
|
}
|
|
}
|
|
|
|
void tst_QCoapClient::socketError()
|
|
{
|
|
#ifdef QT_BUILD_INTERNAL
|
|
CHECK_FOR_COAP_SERVER;
|
|
|
|
QCoapClientForSocketErrorTests client;
|
|
QUrl url = QUrl(testServerResource());
|
|
|
|
const auto connection = client.connection();
|
|
QVERIFY2(connection, "Failed to get coap connection!");
|
|
QUdpSocket *socket = connection->socket();
|
|
QVERIFY2(socket, "Socket not properly created with connection");
|
|
QSignalSpy spySocketError(socket, &QUdpSocket::errorOccurred);
|
|
QScopedPointer<QCoapReply> reply(client.get(url));
|
|
QSignalSpy spyClientError(&client, &QCoapClient::error);
|
|
|
|
QTRY_COMPARE_WITH_TIMEOUT(spySocketError.size(), 1, 10000);
|
|
QTRY_COMPARE_WITH_TIMEOUT(spyClientError.size(), 1, 1000);
|
|
QCOMPARE(qvariant_cast<QtCoap::Error>(spyClientError.first().at(1)),
|
|
QtCoap::Error::AddressInUse);
|
|
#else
|
|
QSKIP("Not an internal build, skipping this test");
|
|
#endif
|
|
}
|
|
void tst_QCoapClient::timeout_data()
|
|
{
|
|
QTest::addColumn<uint>("timeout");
|
|
QTest::addColumn<uint>("maximumRetransmitCount");
|
|
|
|
QTest::newRow("2000/0") << 2000u << 0u;
|
|
QTest::newRow("2000/2") << 2000u << 2u;
|
|
QTest::newRow("4000/0") << 4000u << 0u;
|
|
}
|
|
|
|
void tst_QCoapClient::timeout()
|
|
{
|
|
#ifdef QT_BUILD_INTERNAL
|
|
CHECK_FOR_COAP_SERVER;
|
|
|
|
QFETCH(uint, timeout);
|
|
QFETCH(uint, maximumRetransmitCount);
|
|
|
|
QCoapClientForTests client;
|
|
// Trigger a network timeout
|
|
client.protocol()->setAckTimeout(timeout);
|
|
client.protocol()->setAckRandomFactor(1);
|
|
client.protocol()->setMaximumRetransmitCount(maximumRetransmitCount);
|
|
QUrl url = QUrl("coap://192.0.2.0:5683/"); // Need an url that returns nothing
|
|
|
|
QElapsedTimer timeoutTimer;
|
|
timeoutTimer.start();
|
|
QScopedPointer<QCoapReply> reply(
|
|
client.get(QCoapRequest(url, QCoapMessage::Type::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
|
|
uint transmissions = maximumRetransmitCount + 1;
|
|
|
|
// 10% Precision expected at least, plus timer precision
|
|
QTRY_COMPARE_WITH_TIMEOUT(spyReplyError.size(), 1, static_cast<int>(
|
|
1.1 * client.protocol()->maximumTransmitWait() + 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()->maximumTransmitWait()));
|
|
|
|
// 10% Precision expected at least, minus timer precision
|
|
QVERIFY2(elapsedTime > 0.9 * client.protocol()->maximumTransmitWait() - 20 * transmissions,
|
|
qPrintable(errorMessage));
|
|
|
|
QCOMPARE(qvariant_cast<QtCoap::Error>(spyReplyError.first().at(1)),
|
|
QtCoap::Error::TimeOut);
|
|
QCOMPARE(spyReplyFinished.size(), 1);
|
|
QCOMPARE(spyReplyAborted.size(), 0);
|
|
QCOMPARE(spyClientError.size(), 1);
|
|
#else
|
|
QSKIP("Not an internal build, skipping this test");
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
|
|
reply->abortRequest();
|
|
|
|
QEventLoop eventLoop;
|
|
QTimer::singleShot(1000, &eventLoop, &QEventLoop::quit);
|
|
eventLoop.exec();
|
|
|
|
QCOMPARE(spyReplyAborted.size(), 1);
|
|
QCOMPARE(spyReplyFinished.size(), 1);
|
|
QCOMPARE(spyReplyError.size(), 0);
|
|
}
|
|
|
|
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");
|
|
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::Type::NonConfirmable
|
|
<< 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(testServerHost() + "/large-separate")
|
|
<< QCoapMessage::Type::NonConfirmable
|
|
<< data
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
QTest::newRow("get_large_confirmable")
|
|
<< QUrl(testServerUrl() + "/large")
|
|
<< QCoapMessage::Type::Confirmable
|
|
<< 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
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
QTest::newRow("get_large_16bits")
|
|
<< QUrl(testServerUrl() + "/large")
|
|
<< QCoapMessage::Type::NonConfirmable
|
|
<< data
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
QTest::newRow("get_large_16bits_confirmable")
|
|
<< QUrl(testServerUrl() + "/large")
|
|
<< QCoapMessage::Type::Confirmable
|
|
<< 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");
|
|
|
|
QCoapRequest request(url);
|
|
|
|
if (qstrncmp(QTest::currentDataTag(), "get_large_16bits", 16) == 0)
|
|
client.setBlockSize(16);
|
|
|
|
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;
|
|
connect(reply.data(), &QCoapReply::error, &helper, &Helper::onError);
|
|
|
|
QCOMPARE(spyReplyError.size(), 0);
|
|
QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.size(), 1, 30000);
|
|
QCOMPARE(reply->readAll(), replyData);
|
|
}
|
|
|
|
void tst_QCoapClient::blockwiseRequest_data()
|
|
{
|
|
QTest::addColumn<QUrl>("url");
|
|
QTest::addColumn<QCoapMessage::Type>("type");
|
|
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()
|
|
<< 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");
|
|
|
|
client.setBlockSize(16);
|
|
|
|
QCoapRequest request(url);
|
|
request.setType(type);
|
|
request.addOption(QCoapOption::ContentFormat);
|
|
|
|
QScopedPointer<QCoapReply> reply(client.post(request, requestData));
|
|
QVERIFY(!reply.isNull());
|
|
QSignalSpy spyReplyFinished(reply.data(), &QCoapReply::finished);
|
|
|
|
QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.size(), 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");
|
|
QTest::addColumn<QtCoap::SecurityMode>("security");
|
|
|
|
// Californium test server exposes 31 resources
|
|
QTest::newRow("discover")
|
|
<< QUrl(testServerHost())
|
|
<< 31
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
QTest::newRow("discover_secure")
|
|
<< QUrl(testServerHost())
|
|
<< 31
|
|
<< QtCoap::SecurityMode::PreSharedKey;
|
|
}
|
|
|
|
void tst_QCoapClient::discover()
|
|
{
|
|
CHECK_FOR_COAP_SERVER;
|
|
|
|
QFETCH(QUrl, url);
|
|
QFETCH(int, resourceNumber);
|
|
QFETCH(QtCoap::SecurityMode, security);
|
|
|
|
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(), &QCoapReply::finished);
|
|
|
|
QTRY_COMPARE_WITH_TIMEOUT(spyReplyFinished.size(), 1, 30000);
|
|
|
|
const auto discoverUrl = QUrl(url.toString() + "/.well-known/core");
|
|
#ifdef QT_BUILD_INTERNAL
|
|
QCOMPARE(resourcesReply->url(),
|
|
QCoapRequestPrivate::adjustedUrl(discoverUrl, client.isSecure()));
|
|
#endif
|
|
QCOMPARE(resourcesReply->resources().size(), resourceNumber);
|
|
QCOMPARE(resourcesReply->request().method(), QtCoap::Method::Get);
|
|
|
|
//! TODO Test discovery content too
|
|
}
|
|
|
|
void tst_QCoapClient::observe_data()
|
|
{
|
|
qWarning("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
|
|
<< 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
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
|
|
QTest::newRow("observe_confirmable")
|
|
<< QUrl(testServerUrl() + "/obs")
|
|
<< QCoapMessage::Type::Confirmable
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
|
|
QTest::newRow("observe_receive")
|
|
<< QUrl(testServerUrl() + "/obs-non")
|
|
<< QCoapMessage::Type::NonConfirmable
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
|
|
QTest::newRow("observe_receive_confirmable")
|
|
<< QUrl(testServerUrl() + "/obs-non")
|
|
<< QCoapMessage::Type::Confirmable
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
|
|
QTest::newRow("observe_large")
|
|
<< QUrl(testServerUrl() + "/obs-large")
|
|
<< 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
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
|
|
QTest::newRow("observe_pumping")
|
|
<< QUrl(testServerUrl() + "/obs-pumping")
|
|
<< QCoapMessage::Type::NonConfirmable
|
|
<< QtCoap::SecurityMode::NoSecurity;
|
|
|
|
QTest::newRow("observe_pumping_confirmable")
|
|
<< QUrl(testServerUrl() + "/obs-pumping")
|
|
<< 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");
|
|
|
|
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.size(), 3, 30000);
|
|
client.cancelObserve(reply.data());
|
|
#ifdef QT_BUILD_INTERNAL
|
|
QCOMPARE(reply->url(),
|
|
QCoapRequestPrivate::adjustedUrl(url, client.isSecure()));
|
|
#endif
|
|
QCOMPARE(reply->request().method(), QtCoap::Method::Get);
|
|
|
|
QVERIFY2(!spyReplyNotified.wait(7000), "'Notify' signal received after cancelling observe");
|
|
QCOMPARE(spyReplyFinished.size(), 1);
|
|
|
|
for (QList<QVariant> receivedSignals : std::as_const(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));
|
|
}
|
|
}
|
|
|
|
void tst_QCoapClient::confirmableMulticast()
|
|
{
|
|
QCoapClient client;
|
|
const auto reply = client.get(QCoapRequest("224.0.1.187", QCoapMessage::Type::Confirmable));
|
|
QVERIFY2(!reply, "Confirmable multicast request didn't fail as expected.");
|
|
}
|
|
|
|
void tst_QCoapClient::multicast()
|
|
{
|
|
#ifdef QT_BUILD_INTERNAL
|
|
QCoapClientForMulticastTests client;
|
|
QCoapRequest request = QCoapRequest(QUrl("224.0.1.187"));
|
|
request.setToken("abc");
|
|
QCoapReply *reply = client.get(request);
|
|
QVERIFY(reply);
|
|
|
|
QHostAddress host0("10.20.30.40");
|
|
QHostAddress host1("10.20.30.41");
|
|
|
|
// Simulate sending unicast responses to the multicast request
|
|
emit client.connection()->readyRead("SE\xAD/abc\xC0\xFFReply0", host0);
|
|
emit client.connection()->readyRead("SE\xAD/abc\xC0\xFFReply1", host1);
|
|
|
|
QSignalSpy spyMulticastResponse(&client, &QCoapClient::responseToMulticastReceived);
|
|
QTRY_COMPARE(spyMulticastResponse.size(), 2);
|
|
|
|
QCoapMessage message0 = qvariant_cast<QCoapMessage>(spyMulticastResponse.at(0).at(1));
|
|
QCOMPARE(message0.payload(), "Reply0");
|
|
QHostAddress sender0 = qvariant_cast<QHostAddress>(spyMulticastResponse.at(0).at(2));
|
|
QCOMPARE(sender0, host0);
|
|
|
|
QCoapMessage message1 = qvariant_cast<QCoapMessage>(spyMulticastResponse.at(1).at(1));
|
|
QCOMPARE(message1.payload(), "Reply1");
|
|
QHostAddress sender1 = qvariant_cast<QHostAddress>(spyMulticastResponse.at(1).at(2));
|
|
QCOMPARE(sender1, host1);
|
|
#else
|
|
QSKIP("Not an internal build, skipping this test");
|
|
#endif
|
|
}
|
|
|
|
void tst_QCoapClient::multicast_blockwise()
|
|
{
|
|
#ifdef QT_BUILD_INTERNAL
|
|
QCoapClientForMulticastTests client;
|
|
QCoapRequest request = QCoapRequest(QUrl("224.0.1.187"));
|
|
request.setToken("abc");
|
|
QCoapReply *reply = client.get(request);
|
|
QVERIFY(reply);
|
|
|
|
QHostAddress host0("10.20.30.40");
|
|
QHostAddress host1("10.20.30.41");
|
|
|
|
// Simulate blockwise transfer responses coming from two different hosts
|
|
emit client.connection()->readyRead("SE#}abc\xC0\xB1\x1D\xFFReply1", host0);
|
|
emit client.connection()->readyRead("SE#}abc\xC0\xB1\x1D\xFFReply3", host1);
|
|
emit client.connection()->readyRead("SE#~abc\xC0\xB1%\xFFReply2", host0);
|
|
emit client.connection()->readyRead("SE#~abc\xC0\xB1%\xFFReply4", host1);
|
|
|
|
QSignalSpy spyMulticastResponse(&client, &QCoapClient::responseToMulticastReceived);
|
|
QTRY_COMPARE(spyMulticastResponse.size(), 2);
|
|
|
|
QCoapMessage message0 = qvariant_cast<QCoapMessage>(spyMulticastResponse.at(0).at(1));
|
|
QCOMPARE(message0.payload(), "Reply1Reply2");
|
|
QHostAddress sender0 = qvariant_cast<QHostAddress>(spyMulticastResponse.at(0).at(2));
|
|
QCOMPARE(sender0, host0);
|
|
|
|
QCoapMessage message1 = qvariant_cast<QCoapMessage>(spyMulticastResponse.at(1).at(1));
|
|
QCOMPARE(message1.payload(), "Reply3Reply4");
|
|
QHostAddress sender1 = qvariant_cast<QHostAddress>(spyMulticastResponse.at(1).at(2));
|
|
QCOMPARE(sender1, host1);
|
|
#else
|
|
QSKIP("Not an internal build, skipping this test");
|
|
#endif
|
|
}
|
|
|
|
void tst_QCoapClient::setMinimumTokenSize_data()
|
|
{
|
|
QTest::addColumn<int>("minTokenSize");
|
|
QTest::addColumn<int>("expectedMinSize");
|
|
|
|
QTest::newRow("in_range") << 6 << 6;
|
|
QTest::newRow("out_of_range_small") << 0 << 4;
|
|
QTest::newRow("out_of_range_big") << 9 << 4;
|
|
}
|
|
|
|
void noMessageOutput(QtMsgType, const QMessageLogContext &, const QString &) {}
|
|
|
|
void tst_QCoapClient::setMinimumTokenSize()
|
|
{
|
|
#ifdef QT_BUILD_INTERNAL
|
|
// Don't show warning messages for the out of range values
|
|
qInstallMessageHandler(noMessageOutput);
|
|
|
|
QFETCH(int, minTokenSize);
|
|
QFETCH(int, expectedMinSize);
|
|
|
|
const int maxSize = 8;
|
|
|
|
for (int i = 0; i < 20; ++i) {
|
|
QCoapClientForSocketErrorTests client;
|
|
client.setMinimumTokenSize(minTokenSize);
|
|
|
|
// With QCoapClientForSocketErrorTests the request will fail, but it doesn't matter,
|
|
// we are interested only in the generated token.
|
|
QSignalSpy spyClientError(&client, &QCoapClient::error);
|
|
|
|
QScopedPointer<QCoapReply> reply;
|
|
reply.reset(client.get(QCoapRequest("127.0.0.1")));
|
|
|
|
QTRY_COMPARE_WITH_TIMEOUT(spyClientError.size(), 1, 300);
|
|
QVERIFY(reply->request().tokenLength() >= expectedMinSize);
|
|
QVERIFY(reply->request().tokenLength() <= maxSize);
|
|
}
|
|
#else
|
|
QSKIP("Not an internal build, skipping this test");
|
|
#endif
|
|
}
|
|
|
|
QTEST_MAIN(tst_QCoapClient)
|
|
|
|
#include "tst_qcoapclient.moc"
|