1
0
mirror of https://github.com/eclipse/paho.mqtt.cpp.git synced 2025-05-12 05:03:06 +08:00
paho.mqtt.cpp/test/cppunit/token_test.h

380 lines
12 KiB
C++

// token_test.h
// Unit tests for the token class in the Paho MQTT C++ library.
/*******************************************************************************
* Copyright (c) 2016 Guilherme M. Ferreira <guilherme.maciel.ferreira@gmail.com>
* Copyright (c) 2016-2019 Frank Pagliughi <fpagliughi@mindspring.com>
*
* 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:
* Guilherme M. Ferreira - initial implementation and documentation
* Frank Pagliughi - additional tests. Made this test a friend of token.
* Frank Pagliughi - Updated for token::Type
*******************************************************************************/
#ifndef __mqtt_token_test_h
#define __mqtt_token_test_h
#include <algorithm>
#include <memory>
#include <stdexcept>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
#include "mqtt/token.h"
#include "dummy_async_client.h"
#include "dummy_action_listener.h"
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
class token_test : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE( token_test );
CPPUNIT_TEST( test_user_constructor_client );
CPPUNIT_TEST( test_user_constructor_client_token );
CPPUNIT_TEST( test_user_constructor_client_string );
CPPUNIT_TEST( test_user_constructor_client_vector );
CPPUNIT_TEST( test_on_success_with_data );
CPPUNIT_TEST( test_on_success_without_data );
CPPUNIT_TEST( test_on_failure_with_data );
CPPUNIT_TEST( test_on_failure_without_data );
CPPUNIT_TEST( test_action_callback );
CPPUNIT_TEST( test_wait_success );
CPPUNIT_TEST( test_wait_failure );
CPPUNIT_TEST( test_wait_for_timeout );
CPPUNIT_TEST_SUITE_END();
using milliseconds = std::chrono::milliseconds;
using steady_clock = std::chrono::steady_clock;
mqtt::test::dummy_async_client cli;
static constexpr token::Type TYPE = token::Type::CONNECT;
public:
void setUp() {}
void tearDown() {}
// ----------------------------------------------------------------------
// Test user constructor (iasync_client)
// ----------------------------------------------------------------------
void test_user_constructor_client() {
mqtt::token tok{ TYPE, cli };
CPPUNIT_ASSERT_EQUAL(0, tok.get_message_id());
CPPUNIT_ASSERT_EQUAL(dynamic_cast<mqtt::iasync_client*>(&cli), tok.get_client());
CPPUNIT_ASSERT(nullptr == tok.get_user_context());
CPPUNIT_ASSERT(nullptr == tok.get_action_callback());
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
CPPUNIT_ASSERT(nullptr == tok.get_topics());
}
// ----------------------------------------------------------------------
// Test user constructor (iasync_client, MQTTAsync_token)
// ----------------------------------------------------------------------
void test_user_constructor_client_token() {
MQTTAsync_token id {2};
mqtt::token tok{ TYPE, cli, id };
CPPUNIT_ASSERT_EQUAL(id, tok.get_message_id());
CPPUNIT_ASSERT_EQUAL(dynamic_cast<mqtt::iasync_client*>(&cli), tok.get_client());
CPPUNIT_ASSERT(nullptr == tok.get_user_context());
CPPUNIT_ASSERT(nullptr == tok.get_action_callback());
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
CPPUNIT_ASSERT(nullptr == tok.get_topics());
}
// ----------------------------------------------------------------------
// Test user constructor (iasync_client, string)
// ----------------------------------------------------------------------
void test_user_constructor_client_string() {
std::string topic { "topic" };
mqtt::token tok{ TYPE, cli, topic };
CPPUNIT_ASSERT_EQUAL(0, tok.get_message_id());
CPPUNIT_ASSERT_EQUAL(dynamic_cast<mqtt::iasync_client*>(&cli), tok.get_client());
CPPUNIT_ASSERT(nullptr == tok.get_user_context());
CPPUNIT_ASSERT(nullptr == tok.get_action_callback());
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
CPPUNIT_ASSERT(nullptr != tok.get_topics());
CPPUNIT_ASSERT_EQUAL(size_t(1), tok.get_topics()->size());
CPPUNIT_ASSERT_EQUAL(topic, (*tok.get_topics())[0]);
}
// ----------------------------------------------------------------------
// Test user constructor (iasync_client, vector<string>)
// ----------------------------------------------------------------------
void test_user_constructor_client_vector() {
auto topics = string_collection::create({ "topic1", "topic2" });
mqtt::token tok{ TYPE, cli, topics };
CPPUNIT_ASSERT_EQUAL(0, tok.get_message_id());
CPPUNIT_ASSERT_EQUAL(dynamic_cast<mqtt::iasync_client*>(&cli), tok.get_client());
CPPUNIT_ASSERT_EQUAL(static_cast<void*>(nullptr), tok.get_user_context());
CPPUNIT_ASSERT_EQUAL(static_cast<mqtt::iaction_listener*>(nullptr), tok.get_action_callback());
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
CPPUNIT_ASSERT(nullptr != tok.get_topics());
CPPUNIT_ASSERT_EQUAL(size_t(2), tok.get_topics()->size());
CPPUNIT_ASSERT_EQUAL((*topics)[0], (*tok.get_topics())[0]);
CPPUNIT_ASSERT_EQUAL((*topics)[1], (*tok.get_topics())[1]);
}
// ----------------------------------------------------------------------
// Test on success with data
// ----------------------------------------------------------------------
void test_on_success_with_data() {
mqtt::token tok{ TYPE, cli };
constexpr int MESSAGE_ID = 12;
MQTTAsync_successData data = {
.token = MESSAGE_ID,
};
data.alt.connect.serverURI = const_cast<char*>("tcp://some_server.com");
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
token::on_success(&tok, &data);
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
CPPUNIT_ASSERT_EQUAL(MESSAGE_ID, tok.get_message_id());
}
// ----------------------------------------------------------------------
// Test on success without data
// ----------------------------------------------------------------------
void test_on_success_without_data() {
mqtt::token tok{ TYPE, cli };
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
token::on_success(&tok, nullptr);
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
}
// ----------------------------------------------------------------------
// Test on failure with data
// ----------------------------------------------------------------------
void test_on_failure_with_data() {
mqtt::token tok{ TYPE, cli };
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
constexpr int MESSAGE_ID = 12;
MQTTAsync_failureData data = {
.token = MESSAGE_ID,
.code = 13,
.message = nullptr,
};
token::on_failure(&tok, &data);
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
CPPUNIT_ASSERT_EQUAL(MESSAGE_ID, tok.get_message_id());
}
// ----------------------------------------------------------------------
// Test on failure without data
// ----------------------------------------------------------------------
void test_on_failure_without_data() {
mqtt::token tok{ TYPE, cli };
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
token::on_failure(&tok, nullptr);
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
CPPUNIT_ASSERT_EQUAL(0, tok.get_message_id());
}
// ----------------------------------------------------------------------
// Test set callbacks
// ----------------------------------------------------------------------
void test_action_callback() {
mqtt::test::dummy_action_listener listener;
mqtt::token tok{ TYPE, cli };
tok.set_action_callback(listener);
CPPUNIT_ASSERT_EQUAL(dynamic_cast<mqtt::iaction_listener*>(&listener), tok.get_action_callback());
CPPUNIT_ASSERT_EQUAL(false, listener.on_success_called);
token::on_success(&tok, nullptr);
CPPUNIT_ASSERT_EQUAL(true, listener.on_success_called);
CPPUNIT_ASSERT_EQUAL(false, listener.on_failure_called);
token::on_failure(&tok, nullptr);
CPPUNIT_ASSERT_EQUAL(true, listener.on_failure_called);
}
// ----------------------------------------------------------------------
// Test wait for completion on success case
// All wait's should succeed immediately on successful completion.
// ----------------------------------------------------------------------
void test_wait_success() {
const auto TIMEOUT = milliseconds(10);
mqtt::token tok{ TYPE, cli };
// NOTE: Make sure the complete flag is already true and the return
// code (rc) is MQTTASYNC_SUCCESS, so the token::wait()
// returns immediately. Otherwise we will get stuck in a single thread
// that can't change the complete flag.
token::on_success(&tok, nullptr);
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
// A wait does not reset the "complete" flag.
try {
tok.wait();
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
}
catch (...) {
CPPUNIT_FAIL("token::wait() should not throw on success");
}
// try_wait()
try {
CPPUNIT_ASSERT_EQUAL(true, tok.try_wait());
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
}
catch (...) {
CPPUNIT_FAIL("token::wait() should not throw on success");
}
// wait_for()
try {
CPPUNIT_ASSERT_EQUAL(true, tok.wait_for(TIMEOUT));
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
}
catch (...) {
CPPUNIT_FAIL("token::wait_for() should not throw on success");
}
// wait_until()
const auto TO = steady_clock::now() + TIMEOUT;
try {
CPPUNIT_ASSERT_EQUAL(true, tok.wait_until(TO));
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
}
catch (...) {
CPPUNIT_FAIL("token::wait_until() should not throw on success");
}
}
// ----------------------------------------------------------------------
// Test wait for completion on failure case
// All wait's should throw if the action failed
// ----------------------------------------------------------------------
void test_wait_failure() {
const auto TIMEOUT = milliseconds(10);
mqtt::token tok{ TYPE, cli };
// NOTE: Make sure the complete flag is already true and the return
// code (rc) is MQTTASYNC_FAILURE, so the token::wait()
// returns immediately. Otherwise we will get stuck in a single thread
// that can't change the complete flag.
constexpr int MESSAGE_ID = 12;
MQTTAsync_failureData data = {
.token = MESSAGE_ID,
.code = MQTTASYNC_FAILURE,
.message = nullptr,
};
token::on_failure(&tok, &data);
CPPUNIT_ASSERT_EQUAL(true, tok.is_complete());
CPPUNIT_ASSERT_EQUAL(MESSAGE_ID, tok.get_message_id());
CPPUNIT_ASSERT_EQUAL(MQTTASYNC_FAILURE, tok.get_return_code());
try {
tok.wait();
CPPUNIT_FAIL("token::wait() should throw on failure");
}
catch (mqtt::exception& ex) {
CPPUNIT_ASSERT_EQUAL(MQTTASYNC_FAILURE, ex.get_return_code());
}
try {
tok.try_wait();
CPPUNIT_FAIL("token::try_wait() should throw on failure");
}
catch (mqtt::exception& ex) {
CPPUNIT_ASSERT_EQUAL(MQTTASYNC_FAILURE, ex.get_return_code());
}
try {
tok.wait_for(TIMEOUT);
CPPUNIT_FAIL("token::wait_for() should throw on failure");
}
catch (mqtt::exception& ex) {
CPPUNIT_ASSERT_EQUAL(MQTTASYNC_FAILURE, ex.get_return_code());
}
try {
tok.wait_until(steady_clock::now() + TIMEOUT);
CPPUNIT_FAIL("token::wait_until() should throw on failure");
}
catch (mqtt::exception& ex) {
CPPUNIT_ASSERT_EQUAL(MQTTASYNC_FAILURE, ex.get_return_code());
}
}
// ----------------------------------------------------------------------
// Test wait for completion on failure due timeout case
// All waits should return false, but not throw, on a timeout
// ----------------------------------------------------------------------
void test_wait_for_timeout() {
const auto TIMEOUT = milliseconds(10);
mqtt::token tok{ TYPE, cli };
// Test for timeout on non-signaled token.
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
// try_wait()
try {
CPPUNIT_ASSERT_EQUAL(false, tok.try_wait());
}
catch (...) {
CPPUNIT_FAIL("token::try_wait() should not throw");
}
// wait_for()
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
try {
CPPUNIT_ASSERT_EQUAL(false, tok.wait_for(TIMEOUT));
}
catch (...) {
CPPUNIT_FAIL("token::wait_for() should not throw on timeout");
}
// wait_until()
const auto TO = steady_clock::now() + TIMEOUT;
CPPUNIT_ASSERT_EQUAL(false, tok.is_complete());
try {
CPPUNIT_ASSERT_EQUAL(false, tok.wait_until(TO));
}
catch (...) {
CPPUNIT_FAIL("token::wait_until() should not throw on timeout");
}
}
};
/////////////////////////////////////////////////////////////////////////////
// end namespace mqtt
}
#endif // __mqtt_token_test_h