Commit 0262ae005d9d197aaff285737300970fdbc1152c

Authored by Wiebe Cazemier
1 parent 121e04de

Retained messages tests, plus fix

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 &amp;topic, const std:: @@ -148,6 +148,9 @@ void SubscriptionStore::setRetainedMessage(const std::string &amp;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