1
0
mirror of https://github.com/eclipse/paho.mqtt.cpp.git synced 2025-05-09 11:21:24 +08:00

Continued fixing connect and disconnect copy and move functions with new unit tests. See #445

This commit is contained in:
fpagliughi 2023-11-17 09:36:57 -05:00
parent 5ff5484559
commit c502f84ecf
10 changed files with 366 additions and 193 deletions

View File

@ -55,7 +55,6 @@ Hopefully it will be out by the end of 2023. Just a few weeks away!
- Support for Catch2 v3.x for unit tests (v2.x also still supported).
- Changed the sample apps to use the newer "mqtt://" schemas.
- Connect option initializers for v5 and WebSockets.
- [#304](https://github.com/eclipse/paho.mqtt.cpp/issues/304) Missing create_options::DFLT_C_STRUCT symbol when linking with MSVC.
- [#429] (https://github.com/eclipse/paho.mqtt.cpp/issues/411) Remove declaration of connect_options::to_string() with missing implementation.
- [#411](https://github.com/eclipse/paho.mqtt.cpp/issues/411) Missing virtual keyword for some client methods

View File

@ -68,9 +68,6 @@ connect_options::connect_options(const connect_options& opt) : opts_(opt.opts_),
if (opts_.ssl)
set_ssl(opt.ssl_);
if (opts_.connectProperties)
set_properties(opt.props_);
update_c_struct();
}
@ -95,12 +92,19 @@ connect_options::connect_options(connect_options&& opt) : opts_(opt.opts_),
if (opts_.ssl)
opts_.ssl = &ssl_.opts_;
if (opts_.connectProperties)
opts_.connectProperties = const_cast<MQTTProperties*>(&props_.c_struct());
update_c_struct();
}
connect_options connect_options::v3()
{
return connect_options(DFLT_C_STRUCT);
}
connect_options connect_options::v5()
{
return connect_options(DFLT_C_STRUCT5);
}
// Unfortunately, with the existing implementation, there's no way to know
// if the (connect) properties, will and ssl options were set by looking at the C++ structs.
// In a major update, we can consider using a pointer or optional<> to
@ -154,7 +158,8 @@ void connect_options::update_c_struct()
// Connect Properties
opts_.connectProperties = const_cast<MQTTProperties*>(&props_.c_struct());
if (opts_.MQTTVersion >= MQTTVERSION_5)
opts_.connectProperties = const_cast<MQTTProperties*>(&props_.c_struct());
// HTTP & Proxy
@ -180,8 +185,7 @@ connect_options& connect_options::operator=(const connect_options& opt)
tok_ = opt.tok_;
serverURIs_ = opt.serverURIs_;
if (opts_.connectProperties)
set_properties(opt.props_);
props_ = opt.props_;
httpHeaders_ = opt.httpHeaders_;
httpProxy_ = opt.httpProxy_;
@ -209,8 +213,7 @@ connect_options& connect_options::operator=(connect_options&& opt)
tok_ = std::move(opt.tok_);
serverURIs_ = std::move(opt.serverURIs_);
if (opts_.connectProperties)
set_properties(std::move(opt.props_));
props_ = std::move(opt.props_);
httpHeaders_ = std::move(opt.httpHeaders_);
httpProxy_ = std::move(opt.httpProxy_);

View File

@ -7,21 +7,43 @@
namespace mqtt {
/////////////////////////////////////////////////////////////////////////////
// disconnect_options
const MQTTAsync_disconnectOptions disconnect_options::DFLT_C_STRUCT = MQTTAsync_disconnectOptions_initializer;
const MQTTAsync_disconnectOptions disconnect_options::DFLT_C_STRUCT
= MQTTAsync_disconnectOptions_initializer;
disconnect_options::disconnect_options() : opts_(DFLT_C_STRUCT)
{
const MQTTAsync_disconnectOptions disconnect_options::DFLT_C_STRUCT5
= MQTTAsync_disconnectOptions_initializer5;
disconnect_options::disconnect_options() : opts_{DFLT_C_STRUCT} {
}
disconnect_options::disconnect_options(const disconnect_options& opt)
: opts_(opt.opts_), tok_(opt.tok_), props_(opt.props_)
{
update_c_struct();
}
disconnect_options::disconnect_options(disconnect_options&& opt)
: opts_(opt.opts_), tok_(std::move(opt.tok_)), props_(std::move(opt.props_))
{
update_c_struct();
}
disconnect_options disconnect_options::v3()
{
return disconnect_options { DFLT_C_STRUCT };
}
disconnect_options disconnect_options::v5()
{
return disconnect_options { DFLT_C_STRUCT5 };
}
void disconnect_options::update_c_struct()
{
opts_.properties = props_.c_struct();
opts_.context = tok_.get();
}
disconnect_options& disconnect_options::operator=(const disconnect_options& opt)
@ -29,6 +51,7 @@ disconnect_options& disconnect_options::operator=(const disconnect_options& opt)
opts_ = opt.opts_;
tok_ = opt.tok_;
props_ = opt.props_;
update_c_struct();
return *this;
}
@ -37,6 +60,7 @@ disconnect_options& disconnect_options::operator=(disconnect_options&& opt)
opts_ = opt.opts_;
tok_ = std::move(opt.tok_);
props_ = std::move(opt.props_);
update_c_struct();
return *this;
}
@ -63,6 +87,19 @@ void disconnect_options::set_token(const token_ptr& tok, int mqttVersion)
}
}
/////////////////////////////////////////////////////////////////////////////
// disconnect_options_builder
disconnect_options_builder disconnect_options_builder::v3()
{
return disconnect_options_builder{disconnect_options::DFLT_C_STRUCT};
}
disconnect_options_builder disconnect_options_builder::v5()
{
return disconnect_options_builder{disconnect_options::DFLT_C_STRUCT5};
}
/////////////////////////////////////////////////////////////////////////////
// end namespace 'mqtt'
}

View File

@ -157,16 +157,12 @@ public:
* Creates default options for an MQTT v3.x connection.
* @return Default options for an MQTT v3.x connection.
*/
static connect_options v3() {
return connect_options(DFLT_C_STRUCT);
}
static connect_options v3();
/**
* Creates default options for an MQTT v5 connection.
* @return Default options for an MQTT v5 connection.
*/
static connect_options v5() {
return connect_options(DFLT_C_STRUCT5);
}
static connect_options v5();
/**
* Creates default options for an MQTT v3.x connection using WebSockets.
*
@ -316,7 +312,6 @@ public:
* fails, fall back to 3.1
* @li MQTTVERSION_3_1 (3) = only try version 3.1
* @li MQTTVERSION_3_1_1 (4) = only try version 3.1.1
* @li MQTTVERSION_5 (5) = only try version 5
*/
int get_mqtt_version() const { return opts_.MQTTVersion; }
/**
@ -510,9 +505,12 @@ public:
* Gets the connect properties.
* @return A const reference to the properties for the connect.
*/
const properties& get_properties() const {
return props_;
}
const properties& get_properties() const { return props_; }
/**
* Gets a mutable reference to the connect properties.
* @return A reference to the properties for the connect.
*/
properties& get_properties() { return props_; }
/**
* Sets the properties for the connect.
* @param props The properties to place into the message.

View File

@ -41,6 +41,9 @@ class disconnect_options
/** The default C struct */
static const MQTTAsync_disconnectOptions DFLT_C_STRUCT;
/** The default C struct */
static const MQTTAsync_disconnectOptions DFLT_C_STRUCT5;
/** The underlying C disconnect options */
MQTTAsync_disconnectOptions opts_;
@ -53,6 +56,17 @@ class disconnect_options
/** The client has special access */
friend class async_client;
/** The options builder has special access */
friend class disconnect_options_builder;
/**
* Updates the underlying C structure to match our cached data.
*/
void update_c_struct();
/** Construct options from a C struct */
disconnect_options(const MQTTAsync_disconnectOptions& copts) : opts_{copts} {}
public:
/**
* Create an empty delivery response object.
@ -84,6 +98,16 @@ public:
* @param opt Another object to move into this new one.
*/
disconnect_options(disconnect_options&& opt);
/**
* Creates default options for an MQTT v3.x connection.
* @return Default options for an MQTT v3.x connection.
*/
static disconnect_options v3();
/**
* Creates default options for an MQTT v5 connection.
* @return Default options for an MQTT v5 connection.
*/
static disconnect_options v5();
/**
* Copy assignment.
* @param opt Another object to copy.
@ -108,16 +132,15 @@ public:
return std::chrono::milliseconds(opts_.timeout);
}
/**
* Sets the timeout for disconnecting.
* Sets the disconnect timeout, in milliseconds.
* This allows for any remaining in-flight messages to be delivered.
* @param timeout The timeout (in milliseconds).
* @param timeout The disconnect timeout (in milliseconds).
*/
void set_timeout(int timeout) { opts_.timeout = timeout; }
/**
* Sets the connect timeout with a chrono duration.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param to The connect timeout in seconds.
* Sets the disconnect timeout with a duration.
* This allows for any remaining in-flight messages to be delivered.
* @param to The disconnect connect timeout.
*/
template <class Rep, class Period>
void set_timeout(const std::chrono::duration<Rep, Period>& to) {
@ -137,10 +160,15 @@ public:
*/
token_ptr get_token() const { return tok_; }
/**
* Gets the connect properties.
* @return A const reference to the properties for the connect.
* Gets the disconnect properties.
* @return A const reference to the properties for the disconnect.
*/
const properties& get_properties() const { return props_; }
/**
* Gets a mutable reference to the disconnect properties.
* @return A mutable reference to the properties for the disconnect.
*/
properties& get_properties() { return props_; }
/**
* Sets the properties for the connect.
* @param props The properties to place into the message.
@ -173,7 +201,6 @@ public:
}
};
/////////////////////////////////////////////////////////////////////////////
/**
@ -184,6 +211,9 @@ class disconnect_options_builder
/** The underlying options */
disconnect_options opts_;
/** Construct options builder from a C struct */
disconnect_options_builder(const MQTTAsync_disconnectOptions& copts) : opts_{copts} {}
public:
/** This class */
using self = disconnect_options_builder;
@ -191,6 +221,16 @@ public:
* Default constructor.
*/
disconnect_options_builder() {}
/**
* Creates default options builder for an MQTT v3.x connection.
* @return Default options builder for an MQTT v3.x connection.
*/
static disconnect_options_builder v3();
/**
* Creates default options builder for an MQTT v5 connection.
* @return Default options builder for an MQTT v5 connection.
*/
static disconnect_options_builder v5();
/**
* Sets the properties for the disconnect message.
* @param props The properties for the disconnect message.
@ -208,10 +248,9 @@ public:
return *this;
}
/**
* Sets the connect timeout with a chrono duration.
* This is the maximum time that the underlying library will wait for a
* connection before failing.
* @param to The connect timeout in seconds.
* Sets the disconnect connect timeout.
* This allows for any remaining in-flight messages to be delivered.
* @param to The disconnect timeout.
*/
template <class Rep, class Period>
auto timeout(const std::chrono::duration<Rep, Period>& to) -> self&{

View File

@ -253,6 +253,9 @@ inline string_pair get<string_pair>(const property& prop) {
*/
class properties
{
/** The default C struct */
static const MQTTProperties DFLT_C_STRUCT;
/** The underlying C properties struct */
MQTTProperties props_;
@ -267,9 +270,7 @@ public:
* Default constructor.
* Creates an empty properties list.
*/
properties() {
std::memset(&props_, 0, sizeof(MQTTProperties));
}
properties();
/**
* Copy constructor.
* @param other The property list to copy.
@ -309,26 +310,13 @@ public:
* @param rhs The other property list to copy into this one
* @return A reference to this object.
*/
properties& operator=(const properties& rhs) {
if (&rhs != this) {
::MQTTProperties_free(&props_);
props_ = ::MQTTProperties_copy(&rhs.props_);
}
return *this;
}
properties& operator=(const properties& rhs);
/**
* Move assignment.
* @param rhs The property list to move to this one.
* @return A reference to this object.
*/
properties& operator=(properties&& rhs) {
if (&rhs != this) {
::MQTTProperties_free(&props_);
props_ = rhs.props_;
std::memset(&rhs.props_, 0, sizeof(MQTTProperties));
}
return *this;
}
properties& operator=(properties&& rhs);
/**
* Determines if the property list is empty.
* @return @em true if there are no properties in the list, @em false if
@ -350,7 +338,9 @@ public:
/**
* Removes all the items from the property list.
*/
void clear();
void clear() {
::MQTTProperties_free(&props_);
}
/**
* Determines if the list contains a specific property.
* @param propid The property ID (code).

View File

@ -154,17 +154,35 @@ property& property::operator=(property&& rhs)
/////////////////////////////////////////////////////////////////////////////
properties::properties(std::initializer_list<property> props)
const MQTTProperties properties::DFLT_C_STRUCT = MQTTProperties_initializer;
properties::properties() : props_{DFLT_C_STRUCT}
{
}
properties::properties(std::initializer_list<property> props) : props_{DFLT_C_STRUCT}
{
std::memset(&props_, 0, sizeof(properties));
for (const auto& prop : props)
::MQTTProperties_add(&props_, &prop.c_struct());
}
void properties::clear()
properties& properties::operator=(const properties& rhs)
{
::MQTTProperties_free(&props_);
memset(&props_, 0, sizeof(MQTTProperties));
if (&rhs != this) {
::MQTTProperties_free(&props_);
props_ = ::MQTTProperties_copy(&rhs.props_);
}
return *this;
}
properties& properties::operator=(properties&& rhs)
{
if (&rhs != this) {
::MQTTProperties_free(&props_);
props_ = rhs.props_;
rhs.props_ = DFLT_C_STRUCT;
}
return *this;
}
property properties::get(property::code propid, size_t idx /*=0*/)

View File

@ -4,7 +4,7 @@
//
/*******************************************************************************
* Copyright (c) 2016-2020 Frank Pagliughi <fpagliughi@mindspring.com>
* Copyright (c) 2016-2023 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
@ -62,7 +62,7 @@ static const std::string HTTPS_PROXY { "https://localhost:443" };
TEST_CASE("connect_options default ctor", "[options]")
{
mqtt::connect_options opts;
connect_options opts;
REQUIRE(EMPTY_STR == opts.get_user_name());
REQUIRE(EMPTY_STR == opts.get_password_str());
@ -113,7 +113,7 @@ TEST_CASE("connect_options default ctor", "[options]")
TEST_CASE("connect_options user_constructor", "[options]")
{
mqtt::connect_options opts { USER, PASSWD };
connect_options opts { USER, PASSWD };
REQUIRE(opts.get_http_proxy().empty());
REQUIRE(opts.get_https_proxy().empty());
@ -144,10 +144,10 @@ TEST_CASE("connect_options user_constructor", "[options]")
TEST_CASE("connect_options copy ctor", "[options]")
{
mqtt::connect_options orgOpts { USER, PASSWD };
connect_options orgOpts { USER, PASSWD };
SECTION("simple options") {
mqtt::connect_options opts { orgOpts };
connect_options opts { orgOpts };
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
@ -172,7 +172,7 @@ TEST_CASE("connect_options copy ctor", "[options]")
SECTION("proxy options") {
orgOpts.set_http_proxy(HTTP_PROXY);
mqtt::connect_options opts { orgOpts };
connect_options opts { orgOpts };
REQUIRE(HTTP_PROXY == opts.get_http_proxy());
REQUIRE(opts.get_https_proxy().empty());
}
@ -180,17 +180,25 @@ TEST_CASE("connect_options copy ctor", "[options]")
SECTION("secure proxy options") {
orgOpts.set_https_proxy(HTTPS_PROXY);
mqtt::connect_options opts { orgOpts };
connect_options opts { orgOpts };
REQUIRE(HTTPS_PROXY == opts.get_https_proxy());
REQUIRE(opts.get_http_proxy().empty());
}
SECTION("properties") {
orgOpts.set_properties({{ mqtt::property::SESSION_EXPIRY_INTERVAL, 0 }});
orgOpts.set_properties({{ property::SESSION_EXPIRY_INTERVAL, 0 }});
mqtt::connect_options opts { orgOpts };
connect_options opts { orgOpts };
REQUIRE(opts.get_properties().contains(mqtt::property::SESSION_EXPIRY_INTERVAL));
const auto& copts = opts.c_struct();
const auto& orgCopts = orgOpts.c_struct();
// Make sure it's an actual copy
REQUIRE(copts.connectProperties->array != orgCopts.connectProperties->array);
orgOpts.get_properties().clear();
REQUIRE(1 == opts.get_properties().size());
REQUIRE(opts.get_properties().contains(property::SESSION_EXPIRY_INTERVAL));
REQUIRE(opts.c_struct().connectProperties == &opts.get_properties().c_struct());
}
}
@ -201,10 +209,124 @@ TEST_CASE("connect_options copy ctor", "[options]")
TEST_CASE("connect_options move_constructor", "[options]")
{
mqtt::connect_options orgOpts { USER, PASSWD };
connect_options orgOpts { USER, PASSWD };
SECTION("simple options") {
mqtt::connect_options opts { std::move(orgOpts) };
connect_options opts { std::move(orgOpts) };
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
const auto& copts = opts.c_struct();
REQUIRE(0 == memcmp(&copts.struct_id, CSIG, CSIG_LEN));
REQUIRE(0 == strcmp(USER.c_str(), copts.username));
REQUIRE(copts.password == nullptr);
REQUIRE(PASSWD.size() == size_t(copts.binarypwd.len));
REQUIRE(0 == memcmp(PASSWD.data(), copts.binarypwd.data, PASSWD.size()));
REQUIRE(nullptr == copts.connectProperties);
// Make sure it's a true copy, not linked to the original
orgOpts.set_user_name(EMPTY_STR);
orgOpts.set_password(EMPTY_STR);
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
// Check that the original was moved
REQUIRE(EMPTY_STR == orgOpts.get_user_name());
REQUIRE(EMPTY_STR == orgOpts.get_password_str());
}
SECTION("properties") {
orgOpts.set_properties({{ property::SESSION_EXPIRY_INTERVAL, 42 }});
connect_options opts { std::move(orgOpts) };
const auto& copts = opts.c_struct();
// Check that the original was moved
REQUIRE(orgOpts.get_properties().empty());
// Check that we got the correct properties
REQUIRE(1 == opts.get_properties().size());
REQUIRE(opts.get_properties().contains(property::SESSION_EXPIRY_INTERVAL));
REQUIRE(42 == get<int>(opts.get_properties(), property::SESSION_EXPIRY_INTERVAL));
REQUIRE(copts.connectProperties == &opts.get_properties().c_struct());
}
}
// ----------------------------------------------------------------------
// Test the copy assignment operator=(const&)
// ----------------------------------------------------------------------
TEST_CASE("connect_options copy_assignment", "[options]")
{
SECTION("v3") {
connect_options orgOpts { USER, PASSWD };
connect_options opts;
opts = orgOpts;
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
const auto& copts = opts.c_struct();
REQUIRE(0 == memcmp(&copts.struct_id, CSIG, CSIG_LEN));
REQUIRE(0 == strcmp(USER.c_str(), copts.username));
REQUIRE(copts.password == nullptr);
REQUIRE(PASSWD.size() == size_t(copts.binarypwd.len));
REQUIRE(0 == memcmp(PASSWD.data(), copts.binarypwd.data, PASSWD.size()));
REQUIRE(nullptr == copts.connectProperties);
// Make sure it's a true copy, not linked to the original
orgOpts.set_user_name(EMPTY_STR);
orgOpts.set_password(EMPTY_STR);
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
// Self assignment should cause no harm
opts = opts;
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
}
SECTION("v5") {
auto orgOpts = connect_options::v5();
orgOpts.set_properties({{ property::SESSION_EXPIRY_INTERVAL, 42 }});
connect_options opts = orgOpts;
const auto& copts = opts.c_struct();
const auto& orgCopts = orgOpts.c_struct();
// Make sure it's an actual copy
REQUIRE(copts.connectProperties->array != orgCopts.connectProperties->array);
orgOpts.get_properties().clear();
// Check that we got the correct properties
REQUIRE(1 == opts.get_properties().size());
REQUIRE(opts.get_properties().contains(property::SESSION_EXPIRY_INTERVAL));
REQUIRE(42 == get<int>(opts.get_properties(), property::SESSION_EXPIRY_INTERVAL));
REQUIRE(copts.connectProperties == &opts.get_properties().c_struct());
}
}
// ----------------------------------------------------------------------
// Test the move assignment, operator=(&&)
// ----------------------------------------------------------------------
TEST_CASE("connect_options move_assignment", "[options]")
{
SECTION("v3") {
connect_options orgOpts { USER, PASSWD };
connect_options opts { std::move(orgOpts) };
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
@ -228,99 +350,36 @@ TEST_CASE("connect_options move_constructor", "[options]")
// Check that the original was moved
REQUIRE(EMPTY_STR == orgOpts.get_user_name());
REQUIRE(EMPTY_STR == orgOpts.get_password_str());
}
// Self assignment should cause no harm
// (clang++ is smart enough to warn about this)
#if !defined(__clang__)
opts = std::move(opts);
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
#endif
}
SECTION("properties") {
orgOpts.set_properties({{ mqtt::property::SESSION_EXPIRY_INTERVAL, 0 }});
auto orgOpts = connect_options::v5();
orgOpts.set_properties({{ property::SESSION_EXPIRY_INTERVAL, 42 }});
mqtt::connect_options opts { std::move(orgOpts) };
connect_options opts = std::move(orgOpts);
REQUIRE(opts.get_properties().contains(mqtt::property::SESSION_EXPIRY_INTERVAL));
REQUIRE(opts.c_struct().connectProperties == &opts.get_properties().c_struct());
const auto& copts = opts.c_struct();
// Check that the original was moved
REQUIRE(orgOpts.get_properties().empty());
// Check that we got the correct properties
REQUIRE(1 == opts.get_properties().size());
REQUIRE(opts.get_properties().contains(property::SESSION_EXPIRY_INTERVAL));
REQUIRE(42 == get<int>(opts.get_properties(), property::SESSION_EXPIRY_INTERVAL));
REQUIRE(copts.connectProperties == &opts.get_properties().c_struct());
}
}
// ----------------------------------------------------------------------
// Test the copy assignment operator=(const&)
// ----------------------------------------------------------------------
TEST_CASE("connect_options copy_assignment", "[options]")
{
mqtt::connect_options orgOpts { USER, PASSWD };
mqtt::connect_options opts;
opts = orgOpts;
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
const auto& c_struct = opts.c_struct();
REQUIRE(0 == memcmp(&c_struct.struct_id, CSIG, CSIG_LEN));
REQUIRE(0 == strcmp(USER.c_str(), c_struct.username));
REQUIRE(c_struct.password == nullptr);
REQUIRE(PASSWD.size() == size_t(c_struct.binarypwd.len));
REQUIRE(0 == memcmp(PASSWD.data(), c_struct.binarypwd.data, PASSWD.size()));
// Make sure it's a true copy, not linked to the original
orgOpts.set_user_name(EMPTY_STR);
orgOpts.set_password(EMPTY_STR);
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
// Self assignment should cause no harm
opts = opts;
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
}
// ----------------------------------------------------------------------
// Test the move assignment, operator=(&&)
// ----------------------------------------------------------------------
TEST_CASE("connect_options move_assignment", "[options]")
{
mqtt::connect_options orgOpts { USER, PASSWD };
mqtt::connect_options opts { std::move(orgOpts) };
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
const auto& c_struct = opts.c_struct();
REQUIRE(0 == memcmp(&c_struct.struct_id, CSIG, CSIG_LEN));
REQUIRE(0 == strcmp(USER.c_str(), c_struct.username));
REQUIRE(c_struct.password == nullptr);
REQUIRE(PASSWD.size() == size_t(c_struct.binarypwd.len));
REQUIRE(0 == memcmp(PASSWD.data(), c_struct.binarypwd.data, PASSWD.size()));
// Make sure it's a true copy, not linked to the original
orgOpts.set_user_name(EMPTY_STR);
orgOpts.set_password(EMPTY_STR);
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
// Check that the original was moved
REQUIRE(EMPTY_STR == orgOpts.get_user_name());
REQUIRE(EMPTY_STR == orgOpts.get_password_str());
// Self assignment should cause no harm
// (clang++ is smart enough to warn about this)
#if !defined(__clang__)
opts = std::move(opts);
REQUIRE(USER == opts.get_user_name());
REQUIRE(PASSWD == opts.get_password_str());
#endif
}
// ----------------------------------------------------------------------
// Test set/get of the user and password.
@ -328,7 +387,7 @@ TEST_CASE("connect_options move_assignment", "[options]")
TEST_CASE("connect_options set_user", "[options]")
{
mqtt::connect_options opts;
connect_options opts;
const auto& c_struct = opts.c_struct();
opts.set_user_name(USER);
@ -359,7 +418,7 @@ TEST_CASE("connect_options set_long_user", "[options]")
passwd.push_back(by);
}
mqtt::connect_options orgOpts;
connect_options orgOpts;
orgOpts.set_user_name(user);
orgOpts.set_password(passwd);
@ -367,7 +426,7 @@ TEST_CASE("connect_options set_long_user", "[options]")
REQUIRE(user == orgOpts.get_user_name());
REQUIRE(passwd == orgOpts.get_password_str());
mqtt::connect_options opts;
connect_options opts;
opts = orgOpts;
REQUIRE(user == opts.get_user_name());
@ -387,11 +446,11 @@ TEST_CASE("connect_options set_long_user", "[options]")
TEST_CASE("connect_options set_will", "[options]")
{
mqtt::connect_options opts;
connect_options opts;
const auto& c_struct = opts.c_struct();
REQUIRE(nullptr == c_struct.will);
mqtt::will_options willOpts;
will_options willOpts;
opts.set_will(willOpts);
REQUIRE(nullptr != c_struct.will);
//REQUIRE(&opts.will_.opts_ == c_struct.will);
@ -403,11 +462,11 @@ TEST_CASE("connect_options set_will", "[options]")
TEST_CASE("connect_options set_ssl", "[options]")
{
mqtt::connect_options opts;
connect_options opts;
const auto& c_struct = opts.c_struct();
REQUIRE(nullptr == c_struct.ssl);
mqtt::ssl_options sslOpts;
ssl_options sslOpts;
opts.set_ssl(sslOpts);
REQUIRE(nullptr != c_struct.ssl);
//REQUIRE(&opts.ssl_.opts_ == c_struct.ssl);
@ -419,7 +478,7 @@ TEST_CASE("connect_options set_ssl", "[options]")
TEST_CASE("set_token", "[options]")
{
mqtt::connect_options opts;
connect_options opts;
const auto& c_struct = opts.c_struct();
REQUIRE(nullptr == c_struct.context);
@ -509,39 +568,39 @@ TEST_CASE("set_token", "[options]")
TEST_CASE("connect_options_builder default generator", "[options]")
{
mqtt::connect_options opts;
connect_options opts;
// Default is v3.x
opts = mqtt::connect_options_builder().finalize();
opts = connect_options_builder().finalize();
REQUIRE(MQTTVERSION_DEFAULT == opts.get_mqtt_version());
REQUIRE(DFLT_KEEP_ALIVE == (int) opts.get_keep_alive_interval().count());
// Explicit v3
opts = mqtt::connect_options_builder::v3().finalize();
opts = connect_options_builder::v3().finalize();
REQUIRE(MQTTVERSION_DEFAULT == opts.get_mqtt_version());
REQUIRE(DFLT_KEEP_ALIVE == (int) opts.get_keep_alive_interval().count());
// v5
opts = mqtt::connect_options_builder::v5().finalize();
opts = connect_options_builder::v5().finalize();
REQUIRE(MQTTVERSION_5 == opts.get_mqtt_version());
REQUIRE(DFLT_KEEP_ALIVE == (int) opts.get_keep_alive_interval().count());
// WebSocket
opts = mqtt::connect_options_builder::ws().finalize();
opts = connect_options_builder::ws().finalize();
REQUIRE(MQTTVERSION_DEFAULT == opts.get_mqtt_version());
REQUIRE(DFLT_WS_KEEP_ALIVE == (int) opts.get_keep_alive_interval().count());
// Explicit WebSocket v5
opts = mqtt::connect_options_builder::v5_ws().finalize();
opts = connect_options_builder::v5_ws().finalize();
REQUIRE(MQTTVERSION_5 == opts.get_mqtt_version());
REQUIRE(DFLT_WS_KEEP_ALIVE == (int) opts.get_keep_alive_interval().count());
@ -555,10 +614,10 @@ TEST_CASE("connect_options_builder set", "[options]")
{
const uint32_t INTERVAL = 80000;
mqtt::properties conn_props{
mqtt::property{mqtt::property::SESSION_EXPIRY_INTERVAL, INTERVAL}};
properties conn_props{
property{property::SESSION_EXPIRY_INTERVAL, INTERVAL}};
auto opts = mqtt::connect_options_builder()
auto opts = connect_options_builder()
.properties(conn_props)
.finalize();
@ -566,7 +625,7 @@ TEST_CASE("connect_options_builder set", "[options]")
REQUIRE(!props.empty());
REQUIRE(1 == props.size());
REQUIRE(INTERVAL == get<uint32_t>(props, mqtt::property::SESSION_EXPIRY_INTERVAL));
REQUIRE(INTERVAL == get<uint32_t>(props, property::SESSION_EXPIRY_INTERVAL));
const auto& copts = opts.c_struct();
REQUIRE(nullptr != copts.connectProperties);

View File

@ -3,6 +3,7 @@
/*******************************************************************************
* Copyright (c) 2016 Guilherme M. Ferreira <guilherme.maciel.ferreira@gmail.com>
* Copyright (c) 2016-2023 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
@ -42,7 +43,7 @@ static mock_async_client cli;
TEST_CASE("disconnect_options dflt constructor", "[options]")
{
mqtt::disconnect_options opts;
disconnect_options opts;
REQUIRE(DFLT_TIMEOUT == (int) opts.get_timeout().count());
REQUIRE(!opts.get_token());
@ -64,7 +65,7 @@ TEST_CASE("disconnect_options user constructor", "[options]")
const int TIMEOUT = 10;
auto tok = token::create(TOKEN_TYPE, cli);
mqtt::disconnect_options opts { TIMEOUT };
disconnect_options opts { TIMEOUT };
opts.set_token(tok, MQTTVERSION_DEFAULT);
const auto& c_struct = opts.c_struct();
@ -84,10 +85,10 @@ TEST_CASE("disconnect_options copy ctor", "[options]")
{
constexpr std::chrono::milliseconds TIMEOUT { 50 };
mqtt::disconnect_options orgOpts { TIMEOUT };
disconnect_options orgOpts { TIMEOUT };
SECTION("simple options") {
mqtt::disconnect_options opts { orgOpts };
disconnect_options opts { orgOpts };
REQUIRE(TIMEOUT == opts.get_timeout());
@ -99,12 +100,29 @@ TEST_CASE("disconnect_options copy ctor", "[options]")
}
SECTION("properties") {
orgOpts.set_properties({{ mqtt::property::SESSION_EXPIRY_INTERVAL, 0 }});
orgOpts.set_properties({{ property::SESSION_EXPIRY_INTERVAL, 42 }});
mqtt::disconnect_options opts { orgOpts };
disconnect_options opts { orgOpts };
const auto& copts = opts.c_struct();
const auto& orgCopts = orgOpts.c_struct();
// Make sure we copied the properties
REQUIRE(orgCopts.properties.array != copts.properties.array);
orgOpts.get_properties().clear();
// Check that the properties transferred over
REQUIRE(1 == opts.get_properties().size());
REQUIRE(opts.get_properties().contains(property::SESSION_EXPIRY_INTERVAL));
REQUIRE(42 == get<int>(opts.get_properties(), property::SESSION_EXPIRY_INTERVAL));
REQUIRE(opts.get_properties().contains(mqtt::property::SESSION_EXPIRY_INTERVAL));
REQUIRE(1 == opts.c_struct().properties.count);
REQUIRE(1 == MQTTProperties_propertyCount(
const_cast<MQTTProperties*>(&copts.properties),
MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL));
REQUIRE(42 == MQTTProperties_getNumericValue(
const_cast<MQTTProperties*>(&copts.properties),
MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL));
}
}
@ -116,23 +134,25 @@ TEST_CASE("disconnect_options move_constructor", "[options]")
{
constexpr std::chrono::milliseconds TIMEOUT { 50 };
mqtt::disconnect_options orgOpts { TIMEOUT };
disconnect_options orgOpts { TIMEOUT };
SECTION("simple options") {
mqtt::disconnect_options opts { std::move(orgOpts) };
disconnect_options opts { std::move(orgOpts) };
REQUIRE(TIMEOUT == opts.get_timeout());
REQUIRE(opts.get_properties().empty());
}
SECTION("properties") {
orgOpts.set_properties({{ mqtt::property::SESSION_EXPIRY_INTERVAL, 0 }});
orgOpts.set_properties({{ property::SESSION_EXPIRY_INTERVAL, 42 }});
mqtt::disconnect_options opts { std::move(orgOpts) };
disconnect_options opts { std::move(orgOpts) };
REQUIRE(opts.get_properties().contains(mqtt::property::SESSION_EXPIRY_INTERVAL));
REQUIRE(1 == opts.c_struct().properties.count);
const auto& copts = opts.c_struct();
REQUIRE(1 == opts.get_properties().size());
REQUIRE(opts.get_properties().contains(property::SESSION_EXPIRY_INTERVAL));
REQUIRE(1 == copts.properties.count);
// Check that the original was moved
REQUIRE(orgOpts.get_properties().empty());
@ -145,7 +165,7 @@ TEST_CASE("disconnect_options move_constructor", "[options]")
TEST_CASE("disconnect_options set timeout", "[options]")
{
mqtt::disconnect_options opts;
disconnect_options opts;
const auto& c_struct = opts.c_struct();
const int TIMEOUT = 5000; // ms
@ -168,14 +188,14 @@ TEST_CASE("disconnect_options set timeout", "[options]")
TEST_CASE("disconnect_options set token", "[options]")
{
auto tok = token::create(TOKEN_TYPE, cli);
mqtt::disconnect_options opts;
disconnect_options opts;
const auto& c_struct = opts.c_struct();
REQUIRE(nullptr == c_struct.onSuccess);
REQUIRE(nullptr == c_struct.onFailure);
opts.set_token(mqtt::token_ptr(), MQTTVERSION_DEFAULT);
opts.set_token(token_ptr(), MQTTVERSION_DEFAULT);
REQUIRE(nullptr == c_struct.onSuccess);
REQUIRE(nullptr == c_struct.onFailure);

View File

@ -538,6 +538,10 @@ TEST_CASE("properties copy and move", "[properties]") {
properties props { orgProps };
// Make sure it's a real copy, not a reference to org
const auto& cprops = props.c_struct();
const auto& orgCprops = orgProps.c_struct();
REQUIRE(orgCprops.array != cprops.array);
orgProps.clear();
REQUIRE(get<uint8_t>(props, property::PAYLOAD_FORMAT_INDICATOR) == FMT_IND);
@ -579,14 +583,20 @@ TEST_CASE("properties copy and move", "[properties]") {
REQUIRE(orgProps.empty());
REQUIRE(0 == orgProps.size());
}
const auto& orgCprops = orgProps.c_struct();
REQUIRE(nullptr == orgCprops.array);
}
SECTION("copy assignment") {
properties props;
props = orgProps;
// Make sure it's a real copy, not a reference to org
const auto& cprops = props.c_struct();
const auto& orgCprops = orgProps.c_struct();
REQUIRE(orgCprops.array != cprops.array);
orgProps.clear();
REQUIRE(get<uint8_t>(props, property::PAYLOAD_FORMAT_INDICATOR) == FMT_IND);