Commit 0262ae005d9d197aaff285737300970fdbc1152c
1 parent
121e04de
Retained messages tests, plus fix
Showing
9 changed files
with
292 additions
and
8 deletions
FlashMQTests/FlashMQTests.pro
| 1 | QT += testlib | 1 | QT += testlib |
| 2 | QT -= gui | 2 | QT -= gui |
| 3 | -Qt += network | 3 | +QT += network |
| 4 | +QT += qmqtt | ||
| 4 | 5 | ||
| 5 | DEFINES += TESTING | 6 | DEFINES += TESTING |
| 6 | 7 | ||
| @@ -12,7 +13,34 @@ CONFIG -= app_bundle | @@ -12,7 +13,34 @@ CONFIG -= app_bundle | ||
| 12 | TEMPLATE = app | 13 | TEMPLATE = app |
| 13 | 14 | ||
| 14 | SOURCES += tst_maintests.cpp \ | 15 | SOURCES += tst_maintests.cpp \ |
| 15 | - ../cirbuf.cpp | 16 | + ../MqttPacket.cpp \ |
| 17 | + ../cirbuf.cpp \ | ||
| 18 | + ../client.cpp \ | ||
| 19 | + ../exceptions.cpp \ | ||
| 20 | + ../mainapp.cpp \ | ||
| 21 | + ../mqttpacket.cpp \ | ||
| 22 | + ../retainedmessage.cpp \ | ||
| 23 | + ../rwlockguard.cpp \ | ||
| 24 | + ../subscriptionstore.cpp \ | ||
| 25 | + ../threaddata.cpp \ | ||
| 26 | + ../types.cpp \ | ||
| 27 | + ../utils.cpp \ | ||
| 28 | + mainappthread.cpp \ | ||
| 29 | + twoclienttestcontext.cpp | ||
| 30 | + | ||
| 16 | 31 | ||
| 17 | HEADERS += \ | 32 | HEADERS += \ |
| 18 | - ../cirbuf.h | 33 | + ../cirbuf.h \ |
| 34 | + ../client.h \ | ||
| 35 | + ../exceptions.h \ | ||
| 36 | + ../forward_declarations.h \ | ||
| 37 | + ../mainapp.h \ | ||
| 38 | + ../mqttpacket.h \ | ||
| 39 | + ../retainedmessage.h \ | ||
| 40 | + ../rwlockguard.h \ | ||
| 41 | + ../subscriptionstore.h \ | ||
| 42 | + ../threaddata.h \ | ||
| 43 | + ../types.h \ | ||
| 44 | + ../utils.h \ | ||
| 45 | + mainappthread.h \ | ||
| 46 | + twoclienttestcontext.h |
FlashMQTests/mainappthread.cpp
0 โ 100644
| 1 | +#include "mainappthread.h" | ||
| 2 | + | ||
| 3 | +MainAppThread::MainAppThread(QObject *parent) : QThread(parent) | ||
| 4 | +{ | ||
| 5 | + appInstance = MainApp::getMainApp(); | ||
| 6 | +} | ||
| 7 | + | ||
| 8 | +void MainAppThread::run() | ||
| 9 | +{ | ||
| 10 | + appInstance->start(); | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +void MainAppThread::stopApp() | ||
| 14 | +{ | ||
| 15 | + appInstance->quit(); | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +void MainAppThread::waitForStarted() | ||
| 19 | +{ | ||
| 20 | + int n = 0; | ||
| 21 | + while(!appInstance->getStarted()) | ||
| 22 | + { | ||
| 23 | + QThread::msleep(10); | ||
| 24 | + | ||
| 25 | + if (n++ > 500) | ||
| 26 | + throw new std::runtime_error("Waiting for app to start failed."); | ||
| 27 | + } | ||
| 28 | +} |
FlashMQTests/mainappthread.h
0 โ 100644
| 1 | +#ifndef MAINAPPTHREAD_H | ||
| 2 | +#define MAINAPPTHREAD_H | ||
| 3 | + | ||
| 4 | +#include <QObject> | ||
| 5 | +#include <QThread> | ||
| 6 | +#include <mainapp.h> | ||
| 7 | + | ||
| 8 | +class MainAppThread : public QThread | ||
| 9 | +{ | ||
| 10 | + Q_OBJECT | ||
| 11 | + MainApp *appInstance = nullptr; | ||
| 12 | +public: | ||
| 13 | + explicit MainAppThread(QObject *parent = nullptr); | ||
| 14 | + | ||
| 15 | +public slots: | ||
| 16 | + void run() override; | ||
| 17 | + void stopApp(); | ||
| 18 | + void waitForStarted(); | ||
| 19 | + | ||
| 20 | +signals: | ||
| 21 | + | ||
| 22 | +}; | ||
| 23 | + | ||
| 24 | +#endif // MAINAPPTHREAD_H |
FlashMQTests/tst_maintests.cpp
| 1 | #include <QtTest> | 1 | #include <QtTest> |
| 2 | 2 | ||
| 3 | + | ||
| 4 | +#include <QtQmqtt/qmqtt.h> | ||
| 5 | +#include <QScopedPointer> | ||
| 6 | +#include <QHostInfo> | ||
| 7 | + | ||
| 3 | #include "cirbuf.h" | 8 | #include "cirbuf.h" |
| 9 | +#include "mainapp.h" | ||
| 10 | +#include "mainappthread.h" | ||
| 11 | +#include "twoclienttestcontext.h" | ||
| 4 | 12 | ||
| 5 | class MainTests : public QObject | 13 | class MainTests : public QObject |
| 6 | { | 14 | { |
| 7 | Q_OBJECT | 15 | Q_OBJECT |
| 8 | 16 | ||
| 17 | + MainAppThread mainApp; | ||
| 18 | + | ||
| 9 | public: | 19 | public: |
| 10 | MainTests(); | 20 | MainTests(); |
| 11 | ~MainTests(); | 21 | ~MainTests(); |
| 12 | 22 | ||
| 13 | private slots: | 23 | private slots: |
| 14 | - void test_case1(); | 24 | + void cleanupTestCase(); |
| 25 | + | ||
| 15 | void test_circbuf(); | 26 | void test_circbuf(); |
| 16 | void test_circbuf_unwrapped_doubling(); | 27 | void test_circbuf_unwrapped_doubling(); |
| 17 | void test_circbuf_wrapped_doubling(); | 28 | void test_circbuf_wrapped_doubling(); |
| 18 | void test_circbuf_full_wrapped_buffer_doubling(); | 29 | void test_circbuf_full_wrapped_buffer_doubling(); |
| 19 | 30 | ||
| 31 | + void test_retained(); | ||
| 32 | + void test_retained_changed(); | ||
| 33 | + void test_retained_removed(); | ||
| 34 | + | ||
| 20 | }; | 35 | }; |
| 21 | 36 | ||
| 22 | MainTests::MainTests() | 37 | MainTests::MainTests() |
| 23 | { | 38 | { |
| 24 | - | 39 | + mainApp.start(); |
| 40 | + mainApp.waitForStarted(); | ||
| 25 | } | 41 | } |
| 26 | 42 | ||
| 27 | MainTests::~MainTests() | 43 | MainTests::~MainTests() |
| @@ -29,9 +45,9 @@ MainTests::~MainTests() | @@ -29,9 +45,9 @@ MainTests::~MainTests() | ||
| 29 | 45 | ||
| 30 | } | 46 | } |
| 31 | 47 | ||
| 32 | -void MainTests::test_case1() | 48 | +void MainTests::cleanupTestCase() |
| 33 | { | 49 | { |
| 34 | - | 50 | + mainApp.stopApp(); |
| 35 | } | 51 | } |
| 36 | 52 | ||
| 37 | void MainTests::test_circbuf() | 53 | void MainTests::test_circbuf() |
| @@ -243,6 +259,85 @@ void MainTests::test_circbuf_full_wrapped_buffer_doubling() | @@ -243,6 +259,85 @@ void MainTests::test_circbuf_full_wrapped_buffer_doubling() | ||
| 243 | QVERIFY(true); | 259 | QVERIFY(true); |
| 244 | } | 260 | } |
| 245 | 261 | ||
| 246 | -QTEST_APPLESS_MAIN(MainTests) | 262 | +void MainTests::test_retained() |
| 263 | +{ | ||
| 264 | + TwoClientTestContext testContext; | ||
| 265 | + | ||
| 266 | + QByteArray payload = "We are testing"; | ||
| 267 | + QString topic = "retaintopic"; | ||
| 268 | + | ||
| 269 | + testContext.connectSender(); | ||
| 270 | + testContext.publishRetained(topic, payload); | ||
| 271 | + testContext.publishRetained("dummy2", "Nobody sees this"); | ||
| 272 | + | ||
| 273 | + testContext.connectReceiver(); | ||
| 274 | + testContext.subscribeReceiver("dummy"); | ||
| 275 | + testContext.subscribeReceiver(topic); | ||
| 276 | + testContext.waitReceiverReceived(); | ||
| 277 | + | ||
| 278 | + QVERIFY2(testContext.receivedMessages.count() == 1, "There must be one message in the received list"); | ||
| 279 | + | ||
| 280 | + QMQTT::Message msg = testContext.receivedMessages.first(); | ||
| 281 | + QCOMPARE(msg.payload(), payload); | ||
| 282 | + QVERIFY(msg.retain()); | ||
| 283 | + | ||
| 284 | + testContext.receivedMessages.clear(); | ||
| 285 | + | ||
| 286 | + testContext.publishRetained(topic, payload); | ||
| 287 | + testContext.waitReceiverReceived(); | ||
| 288 | + | ||
| 289 | + QVERIFY2(testContext.receivedMessages.count() == 1, "There must be one message in the received list"); | ||
| 290 | + QMQTT::Message msg2 = testContext.receivedMessages.first(); | ||
| 291 | + QCOMPARE(msg2.payload(), payload); | ||
| 292 | + QVERIFY2(!msg2.retain(), "Getting a retained message while already being subscribed must be marked as normal, not retain."); | ||
| 293 | +} | ||
| 294 | + | ||
| 295 | +void MainTests::test_retained_changed() | ||
| 296 | +{ | ||
| 297 | + TwoClientTestContext testContext; | ||
| 298 | + | ||
| 299 | + QByteArray payload = "We are testing"; | ||
| 300 | + QString topic = "retaintopic"; | ||
| 301 | + | ||
| 302 | + testContext.connectSender(); | ||
| 303 | + testContext.publishRetained(topic, payload); | ||
| 304 | + | ||
| 305 | + payload = "Changed payload"; | ||
| 306 | + | ||
| 307 | + testContext.publishRetained(topic, payload); | ||
| 308 | + | ||
| 309 | + testContext.connectReceiver(); | ||
| 310 | + testContext.subscribeReceiver(topic); | ||
| 311 | + testContext.waitReceiverReceived(); | ||
| 312 | + | ||
| 313 | + QVERIFY2(testContext.receivedMessages.count() == 1, "There must be one message in the received list"); | ||
| 314 | + | ||
| 315 | + QMQTT::Message msg = testContext.receivedMessages.first(); | ||
| 316 | + QCOMPARE(msg.payload(), payload); | ||
| 317 | + QVERIFY(msg.retain()); | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | +void MainTests::test_retained_removed() | ||
| 321 | +{ | ||
| 322 | + TwoClientTestContext testContext; | ||
| 323 | + | ||
| 324 | + QByteArray payload = "We are testing"; | ||
| 325 | + QString topic = "retaintopic"; | ||
| 326 | + | ||
| 327 | + testContext.connectSender(); | ||
| 328 | + testContext.publishRetained(topic, payload); | ||
| 329 | + | ||
| 330 | + payload = ""; | ||
| 331 | + | ||
| 332 | + testContext.publishRetained(topic, payload); | ||
| 333 | + | ||
| 334 | + testContext.connectReceiver(); | ||
| 335 | + testContext.subscribeReceiver(topic); | ||
| 336 | + testContext.waitReceiverReceived(); | ||
| 337 | + | ||
| 338 | + QVERIFY2(testContext.receivedMessages.empty(), "We erased the retained message. We shouldn't have received any."); | ||
| 339 | +} | ||
| 340 | + | ||
| 341 | +QTEST_GUILESS_MAIN(MainTests) | ||
| 247 | 342 | ||
| 248 | #include "tst_maintests.moc" | 343 | #include "tst_maintests.moc" |
FlashMQTests/twoclienttestcontext.cpp
0 โ 100644
| 1 | +#include "twoclienttestcontext.h" | ||
| 2 | + | ||
| 3 | +#include <QEventLoop> | ||
| 4 | +#include <QTimer> | ||
| 5 | + | ||
| 6 | +TwoClientTestContext::TwoClientTestContext(QObject *parent) : QObject(parent) | ||
| 7 | +{ | ||
| 8 | + QHostInfo targetHostInfo = QHostInfo::fromName("localhost"); | ||
| 9 | + QHostAddress targetHost(targetHostInfo.addresses().first()); | ||
| 10 | + sender.reset(new QMQTT::Client(targetHost)); | ||
| 11 | + receiver.reset(new QMQTT::Client(targetHost)); | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +void TwoClientTestContext::publishRetained(const QString &topic, const QByteArray &payload) | ||
| 15 | +{ | ||
| 16 | + QMQTT::Message msg; | ||
| 17 | + msg.setTopic(topic); | ||
| 18 | + msg.setRetain(true); | ||
| 19 | + msg.setQos(0); | ||
| 20 | + msg.setPayload(payload); | ||
| 21 | + sender->publish(msg); | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +void TwoClientTestContext::connectSender() | ||
| 25 | +{ | ||
| 26 | + sender->connectToHost(); | ||
| 27 | + QEventLoop waiter; | ||
| 28 | + connect(sender.data(), &QMQTT::Client::connected, &waiter, &QEventLoop::quit); | ||
| 29 | + waiter.exec(); | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +void TwoClientTestContext::connectReceiver() | ||
| 33 | +{ | ||
| 34 | + connect(receiver.data(), &QMQTT::Client::received, this, &TwoClientTestContext::onReceiverReceived); | ||
| 35 | + | ||
| 36 | + receiver->connectToHost(); | ||
| 37 | + QEventLoop waiter; | ||
| 38 | + connect(receiver.data(), &QMQTT::Client::connected, &waiter, &QEventLoop::quit); | ||
| 39 | + waiter.exec(); | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +void TwoClientTestContext::disconnectReceiver() | ||
| 43 | +{ | ||
| 44 | + receiver->disconnectFromHost(); | ||
| 45 | + QEventLoop waiter; | ||
| 46 | + connect(sender.data(), &QMQTT::Client::disconnected, &waiter, &QEventLoop::quit); | ||
| 47 | + waiter.exec(); | ||
| 48 | +} | ||
| 49 | + | ||
| 50 | +void TwoClientTestContext::subscribeReceiver(const QString &topic) | ||
| 51 | +{ | ||
| 52 | + receiver->subscribe(topic); | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +void TwoClientTestContext::waitReceiverReceived() | ||
| 56 | +{ | ||
| 57 | + QEventLoop waiter; | ||
| 58 | + QTimer timeout; | ||
| 59 | + timeout.setSingleShot(true); | ||
| 60 | + timeout.setInterval(1000); | ||
| 61 | + connect(&timeout, &QTimer::timeout, &waiter, &QEventLoop::quit); | ||
| 62 | + connect(receiver.data(), &QMQTT::Client::received, &waiter, &QEventLoop::quit); | ||
| 63 | + timeout.start(); | ||
| 64 | + waiter.exec(); | ||
| 65 | +} | ||
| 66 | + | ||
| 67 | +void TwoClientTestContext::onReceiverReceived(const QMQTT::Message &message) | ||
| 68 | +{ | ||
| 69 | + receivedMessages.append(message); | ||
| 70 | +} |
FlashMQTests/twoclienttestcontext.h
0 โ 100644
| 1 | +#ifndef RETAINTESTCONTEXT_H | ||
| 2 | +#define RETAINTESTCONTEXT_H | ||
| 3 | + | ||
| 4 | +#include <QObject> | ||
| 5 | +#include <QtQmqtt/qmqtt.h> | ||
| 6 | +#include <QHostInfo> | ||
| 7 | + | ||
| 8 | +class TwoClientTestContext : public QObject | ||
| 9 | +{ | ||
| 10 | + Q_OBJECT | ||
| 11 | + | ||
| 12 | + QScopedPointer<QMQTT::Client> sender; | ||
| 13 | + QScopedPointer<QMQTT::Client> receiver; | ||
| 14 | + | ||
| 15 | +private slots: | ||
| 16 | + void onReceiverReceived(const QMQTT::Message& message); | ||
| 17 | + | ||
| 18 | +public: | ||
| 19 | + explicit TwoClientTestContext(QObject *parent = nullptr); | ||
| 20 | + void publishRetained(const QString &topic, const QByteArray &payload); | ||
| 21 | + void connectSender(); | ||
| 22 | + void connectReceiver(); | ||
| 23 | + void disconnectReceiver(); | ||
| 24 | + void subscribeReceiver(const QString &topic); | ||
| 25 | + void waitReceiverReceived(); | ||
| 26 | + | ||
| 27 | + QList<QMQTT::Message> receivedMessages; | ||
| 28 | + | ||
| 29 | +signals: | ||
| 30 | + | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | +#endif // RETAINTESTCONTEXT_H |
mainapp.cpp
| @@ -171,6 +171,7 @@ void MainApp::start() | @@ -171,6 +171,7 @@ void MainApp::start() | ||
| 171 | 171 | ||
| 172 | uint next_thread_index = 0; | 172 | uint next_thread_index = 0; |
| 173 | 173 | ||
| 174 | + started = true; | ||
| 174 | while (running) | 175 | while (running) |
| 175 | { | 176 | { |
| 176 | int num_fds = epoll_wait(epoll_fd_accept, events, MAX_EVENTS, 100); | 177 | int num_fds = epoll_wait(epoll_fd_accept, events, MAX_EVENTS, 100); |
mainapp.h
| @@ -21,6 +21,7 @@ class MainApp | @@ -21,6 +21,7 @@ class MainApp | ||
| 21 | { | 21 | { |
| 22 | static MainApp *instance; | 22 | static MainApp *instance; |
| 23 | 23 | ||
| 24 | + bool started = false; | ||
| 24 | bool running = true; | 25 | bool running = true; |
| 25 | std::vector<std::shared_ptr<ThreadData>> threads; | 26 | std::vector<std::shared_ptr<ThreadData>> threads; |
| 26 | std::shared_ptr<SubscriptionStore> subscriptionStore; | 27 | std::shared_ptr<SubscriptionStore> subscriptionStore; |
| @@ -32,6 +33,7 @@ public: | @@ -32,6 +33,7 @@ public: | ||
| 32 | static MainApp *getMainApp(); | 33 | static MainApp *getMainApp(); |
| 33 | void start(); | 34 | void start(); |
| 34 | void quit(); | 35 | void quit(); |
| 36 | + bool getStarted() const {return started;} | ||
| 35 | }; | 37 | }; |
| 36 | 38 | ||
| 37 | #endif // MAINAPP_H | 39 | #endif // MAINAPP_H |
subscriptionstore.cpp
| @@ -148,6 +148,9 @@ void SubscriptionStore::setRetainedMessage(const std::string &topic, const std:: | @@ -148,6 +148,9 @@ void SubscriptionStore::setRetainedMessage(const std::string &topic, const std:: | ||
| 148 | return; | 148 | return; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | + if (retained_found) | ||
| 152 | + retainedMessages.erase(rm); | ||
| 153 | + | ||
| 151 | retainedMessages.insert(std::move(rm)); | 154 | retainedMessages.insert(std::move(rm)); |
| 152 | } | 155 | } |
| 153 | 156 |