// message_test.cpp // // Unit tests for the message class in the Paho MQTT C++ library. // /******************************************************************************* * Copyright (c) 2016-2020 Frank Pagliughi * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Frank Pagliughi - initial implementation and documentation *******************************************************************************/ #define UNIT_TESTS #include #include "catch2/catch.hpp" #include "mqtt/message.h" using namespace mqtt; static const std::string EMPTY_STR; static const int DFLT_QOS = 0; static const bool DFLT_RETAINED = false; static const bool DFLT_DUP = false; static const std::string TOPIC { "hello" }; static const char* BUF = "Hello there"; static const size_t N = std::strlen(BUF); static const std::string PAYLOAD = std::string(BUF); static const int QOS = 1; // -------------------------------------------------------------------------- // Test the default constructor // -------------------------------------------------------------------------- TEST_CASE("default constructor", "[message]") { mqtt::message msg; REQUIRE(EMPTY_STR == msg.get_payload_str()); REQUIRE(DFLT_QOS == msg.get_qos()); REQUIRE(DFLT_RETAINED == msg.is_retained()); REQUIRE(DFLT_DUP == msg.is_duplicate()); const auto& c_struct = msg.c_struct(); REQUIRE(0 == c_struct.payloadlen); REQUIRE(nullptr == c_struct.payload); REQUIRE(DFLT_QOS == c_struct.qos); REQUIRE(DFLT_RETAINED == (c_struct.retained != 0)); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); } // -------------------------------------------------------------------------- // Test the raw buffer (void*) and length constructor // -------------------------------------------------------------------------- TEST_CASE("buf len constructor", "[message]") { mqtt::message msg(TOPIC, BUF, N); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(DFLT_QOS == msg.get_qos()); REQUIRE(DFLT_RETAINED == msg.is_retained()); REQUIRE(DFLT_DUP == msg.is_duplicate()); const auto& c_struct = msg.c_struct(); REQUIRE(int(N) == c_struct.payloadlen); REQUIRE(0 == memcmp(BUF, c_struct.payload, N)); REQUIRE(DFLT_QOS == c_struct.qos); REQUIRE(DFLT_RETAINED == (c_struct.retained != 0)); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); } // -------------------------------------------------------------------------- // Test the raw buffer (void*) constructor // -------------------------------------------------------------------------- TEST_CASE("buf constructor", "[message]") { mqtt::message msg(TOPIC, BUF, N, QOS, true); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); REQUIRE(DFLT_DUP == msg.is_duplicate()); const auto& c_struct = msg.c_struct(); REQUIRE(int(N) == c_struct.payloadlen); REQUIRE(0 == memcmp(BUF, c_struct.payload, N)); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); } // -------------------------------------------------------------------------- // Test the string buffer constructor // -------------------------------------------------------------------------- TEST_CASE("string constructor", "[message]") { mqtt::message msg(TOPIC, PAYLOAD); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(DFLT_QOS == msg.get_qos()); REQUIRE(!msg.is_retained()); REQUIRE(DFLT_DUP == msg.is_duplicate()); const auto& c_struct = msg.c_struct(); REQUIRE(int(PAYLOAD.size()) == c_struct.payloadlen); REQUIRE(0 == memcmp(PAYLOAD.data(), c_struct.payload, PAYLOAD.size())); REQUIRE(DFLT_QOS == c_struct.qos); REQUIRE(DFLT_RETAINED == (c_struct.retained != 0)); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); } // -------------------------------------------------------------------------- // Test the string buffer with QoS constructor // -------------------------------------------------------------------------- TEST_CASE("string qos constructor", "[message]") { mqtt::message msg(TOPIC, PAYLOAD, QOS, true); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); REQUIRE(DFLT_DUP == msg.is_duplicate()); const auto& c_struct = msg.c_struct(); REQUIRE(int(PAYLOAD.size()) == c_struct.payloadlen); REQUIRE(0 == memcmp(PAYLOAD.data(), c_struct.payload, PAYLOAD.size())); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); } // -------------------------------------------------------------------------- // Test the initialization by C struct // -------------------------------------------------------------------------- TEST_CASE("c struct constructor", "[message]") { MQTTAsync_message c_msg = MQTTAsync_message_initializer; c_msg.payload = const_cast(BUF); c_msg.payloadlen = N; c_msg.qos = QOS; c_msg.retained = 1; c_msg.dup = 1; mqtt::message msg(TOPIC, c_msg); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); REQUIRE(msg.is_duplicate()); const auto& c_struct = msg.c_struct(); REQUIRE(int(N) == c_struct.payloadlen); REQUIRE(0 == memcmp(BUF, c_struct.payload, N)); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(c_struct.dup != 0); } // -------------------------------------------------------------------------- // Test the copy constructor // -------------------------------------------------------------------------- TEST_CASE("copy constructor", "[message]") { auto orgMsg = mqtt::message(TOPIC, PAYLOAD, QOS, true); mqtt::message msg(orgMsg); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); const auto& c_struct = msg.c_struct(); REQUIRE(int(PAYLOAD.size()) == c_struct.payloadlen); REQUIRE(0 == memcmp(PAYLOAD.data(), c_struct.payload, PAYLOAD.size())); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); // Make sure it's a true copy, not linked to the original orgMsg.set_payload(EMPTY_STR); orgMsg.set_qos(DFLT_QOS); orgMsg.set_retained(false); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); } // -------------------------------------------------------------------------- // Test the move constructor // -------------------------------------------------------------------------- TEST_CASE("move constructor", "[message]") { auto orgMsg = mqtt::message(TOPIC, PAYLOAD, QOS, true); mqtt::message msg(std::move(orgMsg)); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); const auto& c_struct = msg.c_struct(); REQUIRE(int(PAYLOAD.size()) == c_struct.payloadlen); REQUIRE(0 == memcmp(PAYLOAD.data(), c_struct.payload, PAYLOAD.size())); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); // Check that the original was moved REQUIRE(size_t(0) == orgMsg.get_payload().size()); } // -------------------------------------------------------------------------- // Test the copy assignment operator=(const&) // -------------------------------------------------------------------------- TEST_CASE("copy assignment", "[message]") { auto orgMsg = mqtt::message(TOPIC, PAYLOAD, QOS, true); mqtt::message msg; msg = orgMsg; REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); const auto& c_struct = msg.c_struct(); REQUIRE(int(PAYLOAD.size()) == c_struct.payloadlen); REQUIRE(0 == memcmp(PAYLOAD.data(), c_struct.payload, PAYLOAD.size())); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); // Make sure it's a true copy, not linked to the original orgMsg.set_payload(EMPTY_STR); orgMsg.set_qos(DFLT_QOS); orgMsg.set_retained(false); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); // Self assignment should cause no harm msg = msg; REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); } // -------------------------------------------------------------------------- // Test the move assignment, operator=(&&) // -------------------------------------------------------------------------- TEST_CASE("move assignment", "[message]") { auto orgMsg = mqtt::message(TOPIC, PAYLOAD, QOS, true); mqtt::message msg; msg = std::move(orgMsg); REQUIRE(TOPIC == msg.get_topic()); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); const auto& c_struct = msg.c_struct(); REQUIRE(int(PAYLOAD.size()) == c_struct.payloadlen); REQUIRE(0 == memcmp(PAYLOAD.data(), c_struct.payload, PAYLOAD.size())); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); // Check that the original was moved REQUIRE(size_t(0) == orgMsg.get_payload().size()); // Self assignment should cause no harm // (clang++ is smart enough to warn about this) #if !defined(__clang__) msg = std::move(msg); REQUIRE(PAYLOAD == msg.get_payload_str()); REQUIRE(QOS == msg.get_qos()); REQUIRE(msg.is_retained()); #endif } // -------------------------------------------------------------------------- // Test the validate_qos() // -------------------------------------------------------------------------- TEST_CASE("validate qos", "[message]") { REQUIRE_THROWS_AS(mqtt::message::validate_qos(-1), mqtt::exception); REQUIRE_THROWS_AS(mqtt::message::validate_qos(3), mqtt::exception); REQUIRE_NOTHROW(mqtt::message::validate_qos(0)); } ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // Test the default builder // -------------------------------------------------------------------------- TEST_CASE("default builder", "[message]") { auto msg = mqtt::message_ptr_builder().finalize(); REQUIRE(EMPTY_STR == msg->get_payload_str()); REQUIRE(DFLT_QOS == msg->get_qos()); REQUIRE(DFLT_RETAINED == msg->is_retained()); REQUIRE(DFLT_DUP == msg->is_duplicate()); const auto& c_struct = msg->c_struct(); REQUIRE(0 == c_struct.payloadlen); REQUIRE(nullptr == c_struct.payload); REQUIRE(DFLT_QOS == c_struct.qos); REQUIRE(DFLT_RETAINED == (c_struct.retained != 0)); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); } // -------------------------------------------------------------------------- // Test a complete builder // -------------------------------------------------------------------------- TEST_CASE("builder", "[message]") { auto msg = mqtt::message_ptr_builder() .topic(TOPIC) .payload(PAYLOAD) .qos(QOS) .retained(true) .finalize(); REQUIRE(TOPIC == msg->get_topic()); REQUIRE(PAYLOAD == msg->get_payload_str()); REQUIRE(QOS == msg->get_qos()); REQUIRE(msg->is_retained()); REQUIRE(DFLT_DUP == msg->is_duplicate()); const auto& c_struct = msg->c_struct(); REQUIRE(int(PAYLOAD.size()) == c_struct.payloadlen); REQUIRE(0 == memcmp(PAYLOAD.data(), c_struct.payload, PAYLOAD.size())); REQUIRE(QOS == c_struct.qos); REQUIRE(c_struct.retained != 0); REQUIRE(DFLT_DUP == (c_struct.dup != 0)); }