diff --git a/src/mqttclient.cpp b/src/mqttclient.cpp index 2f7aef9..333c4d4 100644 --- a/src/mqttclient.cpp +++ b/src/mqttclient.cpp @@ -73,6 +73,12 @@ MqttClient::MqttClient(const std::string& _clientId, const std::functiondisconnect(); @@ -83,11 +89,6 @@ MqttClient::~MqttClient() m_principalClient.swap(principalClient); } - LogDebug( "MqttClient", std::string( m_clientId + " - dtor stop queue" ) ); - m_eventQueue.stop(); - if (m_workerThread.joinable()) { - m_workerThread.join(); - } LogDebug( "MqttClient", std::string( m_clientId + " - dtor ready" ) ); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1471cf8..835cfc3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,24 +7,26 @@ # # Build rules for the MQTT Library -add_executable(topictest +add_executable(mqtt_test + helperclasses/PublisherClass.h TopicLengthTest.cpp + SledgeHammerTest.cpp ) -target_include_directories(topictest PRIVATE +target_include_directories(mqtt_test PRIVATE ${CMAKE_CIRRENT_SOURECE_DIR} ../include ) -target_link_libraries(topictest PRIVATE +target_link_libraries(mqtt_test PRIVATE gmock_main gmock gtest mqtt-cpp ) -add_test(NAME topictest COMMAND topictest) +add_test(NAME mqtt_test COMMAND mqtt_test) -set_tests_properties(topictest PROPERTIES +set_tests_properties(mqtt_test PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) diff --git a/test/SledgeHammerTest.cpp b/test/SledgeHammerTest.cpp new file mode 100644 index 0000000..7f31e7f --- /dev/null +++ b/test/SledgeHammerTest.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** + * COpyright (c) 2023 Open Systems Development B.V. + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include + +#include "helperclasses/PublisherClass.h" + +/// Every test does basically the same. +/// 1. Create a Publisher object +/// 2. Connect to the MQTT broker +/// 3. Publish 10 message(s) +/// 4. Disconnect from the MQTT broker +/// 5. Destroy the Publisher object +/// 6. Repeat 10 times. + +const std::string sledge_maintopic = "SledgeHammerTest/"; + +enum TIME_RES +{ + T_MICRO, + T_MILLI, + T_SECONDS +}; + +std::uint64_t getEpochUSecs() +{ + auto tsUSec =std::chrono::time_point_cast(std::chrono::system_clock::now()); + return static_cast(tsUSec.time_since_epoch().count()); +} + + +void sleepcp( int number, TIME_RES resolution = T_MILLI ) // Cross-platform sleep function +{ + int factor = 0; // Should not happen.. + + switch( resolution ) + { + case T_MICRO: + factor = 1; + break; + + case T_MILLI: + factor = 1000; + break; + + case T_SECONDS: + factor = 1000000; + break; + } + + usleep( number * factor ); +} + +/// Test a single connection, 1 time +TEST(SledgeHammerTest, SingleConnectionCleanExit) +{ + Publisher *publisher = nullptr; + for (int test_counter = 0; test_counter < 10; test_counter++) + { + publisher = new Publisher(std::to_string(getEpochUSecs())); + if(publisher) + { + publisher->connect("localhost"); + publisher->publish(sledge_maintopic + "Single Connection Clean Exit Test/" + std::to_string(test_counter), "Hello World. I'm alive.."); + publisher->disconnect(); + delete publisher; + publisher = nullptr; + } + } +} + +TEST(SledgeHammerTest, SingleConnectionForcedExit) +{ + Publisher *publisher = nullptr; + for (int test_counter = 0; test_counter < 10; test_counter++) + { + publisher = new Publisher(std::to_string(getEpochUSecs())); + if(publisher) + { + publisher->connect("localhost"); + publisher->publish(sledge_maintopic + "Single Connection Forced Exit/Test/" + std::to_string(test_counter), "Hello World. I'm alive.."); + delete publisher; + publisher = nullptr; + } + } +} + +TEST(SledgeHammerTest, MultipleConnections_10_CleanExit) +{ + std::unordered_map publishers; + + for (int test_counter = 0; test_counter < 10; test_counter++) + { + publishers["Publisher" + std::to_string(test_counter)] = new Publisher(std::to_string(getEpochUSecs())); + if(publishers["Publisher" + std::to_string(test_counter)]) + { + publishers["Publisher" + std::to_string(test_counter)]->connect("localhost"); + } + } + + for (int test_counter = 0; test_counter < 10; test_counter++) + { + for (int nCount = 0; nCount < 10; nCount++) + { + publishers["Publisher" + std::to_string(test_counter)]->publish(sledge_maintopic + "Multiple Connections [10]/Clean Exit/Test " + std::to_string(test_counter), "Hello World. I'm alive.."); + } + publishers["Publisher" + std::to_string(test_counter)]->disconnect(); + delete publishers["Publisher" + std::to_string(test_counter)]; + publishers.erase("Publisher" + std::to_string(test_counter)); + } +} + +TEST(SledgeHammerTest, MultipleConnections_BurnTest) +{ + std::unordered_map publishers; + + const int max_run = 100; + const int max_connections = 5; + const int max_messages = 100; + + for (int test_run = 0; test_run < max_run; test_run++) + { + std::cout << "Creating " << max_connections << " connections for test run " << test_run << " " << std::endl; + for (int test_counter = 0; test_counter < max_connections; test_counter++) + { + publishers["Publisher" + std::to_string(test_counter)] = new Publisher(std::to_string(getEpochUSecs())); + if(publishers["Publisher" + std::to_string(test_counter)]) + { + publishers["Publisher" + std::to_string(test_counter)]->connect("localhost"); + std::cout << "."; + } + } + std::cout << std::endl; + + // Wait a second for the connections to be established. + sleepcp(1, T_SECONDS); + + std::cout << "Publishing " << max_messages << " messages for test run " << test_run << std::endl; + for (int test_counter = 0; test_counter < max_connections; test_counter++) + { + std::cout << "\tConnection : " << test_counter << " "; + for (int nCount = 0; nCount < max_messages; nCount++) + { + if(publishers["Publisher" + std::to_string(test_counter)]) + { + std::cout << "."; + publishers["Publisher" + std::to_string(test_counter)]->publish(sledge_maintopic + + "Multiple Connections [" + std::to_string(max_connections) + "]/Multi Messages [" + std::to_string(max_messages) + "]/Test Run " + + std::to_string(test_run) + "/" + "[" + std::to_string(test_counter) + "]" + + "[" + std::to_string(nCount) + "]", "Hello World. I'm alive.."); + } + } + std::cout << std::endl; + } + + // Wait another second to update the broker. + sleepcp(1, T_SECONDS); + + std::cout << "Disconnecting " << max_connections << " connections for test run " << test_run << " "; + for (int test_counter = 0; test_counter < max_connections; test_counter++) + { + if(publishers["Publisher" + std::to_string(test_counter)]) + { + std::cout << "."; + publishers["Publisher" + std::to_string(test_counter)]->disconnect(); + delete publishers["Publisher" + std::to_string(test_counter)]; + } + } + std::cout << std::endl; + publishers.clear(); + std::cout << std::string(200,'=') << std::endl; + + // Wait for another second before re-running another 100 times. + sleepcp(1, T_SECONDS); + } +} \ No newline at end of file diff --git a/test/TopicLengthTest.cpp b/test/TopicLengthTest.cpp index b36bc78..3a9f817 100644 --- a/test/TopicLengthTest.cpp +++ b/test/TopicLengthTest.cpp @@ -12,7 +12,7 @@ using namespace osdev::components::mqtt; using namespace osdev::components::log; -static const std::string main_topic = "test/"; +static const std::string main_topic = "Topic Length Test/"; /**************************************************************************** * H E L P E R C L A S S E S diff --git a/test/helperclasses/PublisherClass.h b/test/helperclasses/PublisherClass.h new file mode 100644 index 0000000..8d12cb7 --- /dev/null +++ b/test/helperclasses/PublisherClass.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * Copyright (c)2024 Open Systems Development B.V. + ****************************************************************************/ + +#include "mqttclient.h" + +#include + +using namespace osdev::components::mqtt; +using namespace osdev::components::log; + +class Publisher +{ +public: + Publisher(const std::string &unique_id) : m_mqtt_client("SledgeHammerTest" + unique_id){} + virtual ~Publisher() {} + + void connect( const std::string &hostname, + int portnumber = 1883, + const std::string &username = std::string(), + const std::string &password = std::string(), + const std::string &lwt_topic = std::string(), + const std::string &lwt_message = std::string() + ) + { + m_mqtt_client.connect(hostname, portnumber, + Credentials(username, password), + mqtt_LWT(lwt_topic, lwt_message), + true, + LogSettings + { + LogLevel::Debug, + LogMask::None + }); + } + + void disconnect() + { + m_mqtt_client.disconnect(); + } + + void publish(const std::string &message_topic, const std::string &message_payload) + { + MqttMessage message(message_topic, true, false, message_payload); + Token t_result = m_mqtt_client.publish(message, 0); + } + +private: + osdev::components::mqtt::MqttClient m_mqtt_client; +}; \ No newline at end of file