Fix user property encoding

Summary: related to T11798

Reviewers: ivica

Reviewed By: ivica

Subscribers: miljen, iljazovic

Differential Revision: https://repo.mireo.local/D27532
This commit is contained in:
Korina Šimičević
2024-01-24 13:00:44 +01:00
parent 21d90a9fda
commit 61f17f6e0f
3 changed files with 327 additions and 92 deletions

View File

@ -390,6 +390,10 @@ bool parse_to_prop(
if constexpr (async_mqtt5::is_vector<prop_type>) {
std::string value;
// key
rv = basic::utf8_.parse(iter, last, ctx, rctx, value);
if (rv) prop.push_back(std::move(value));
// value
rv = basic::utf8_.parse(iter, last, ctx, rctx, value);
if (rv) prop.push_back(std::move(value));
}
@ -439,7 +443,7 @@ public:
// either rv = false or property with prop_id was not found
if (!rv || iter == saved)
break;
return false;
}
first = iter;

View File

@ -370,7 +370,7 @@ using encoder_types = std::tuple<
prop_encoder_type<pp::content_type_t, basic::utf8_def>,
prop_encoder_type<pp::response_topic_t, basic::utf8_def>,
prop_encoder_type<pp::correlation_data_t, basic::utf8_def>,
prop_encoder_type<pp::subscription_identifier_t, basic::int_def<basic::varint_t>>, // varint
prop_encoder_type<pp::subscription_identifier_t, basic::int_def<basic::varint_t>>,
prop_encoder_type<pp::session_expiry_interval_t, basic::int_def<int32_t>>,
prop_encoder_type<pp::assigned_client_identifier_t, basic::utf8_def>,
prop_encoder_type<pp::server_keep_alive_t, basic::int_def<int16_t>>,
@ -454,12 +454,19 @@ public:
size_t byte_size() const {
if (_val.empty()) return 0;
size_t total_size = 0;
for (const auto& pr: _val) {
auto sval = encoder_for_prop<p>(pr);
size_t prop_size = sval.byte_size();
if (prop_size) total_size += 1 + prop_size;
for (size_t i = 0; i < _val.size() && i + 1 < _val.size(); i += 2) {
auto skey = encoder_for_prop<p>(_val[i]);
size_t key_size = skey.byte_size();
auto sval = encoder_for_prop<p>(_val[i + 1]);
size_t val_size = sval.byte_size();
if (key_size && val_size)
total_size += 1 + key_size + val_size;
}
return total_size;
}
@ -467,11 +474,16 @@ public:
if (_val.empty())
return s;
for (const auto& pr: _val) {
auto sval = encoder_for_prop<p>(pr);
for (size_t i = 0; i < _val.size() && i + 1 < _val.size(); i += 2) {
s.push_back(p);
auto skey = encoder_for_prop<p>(_val[i]);
skey.encode(s);
auto sval = encoder_for_prop<p>(_val[i + 1]);
sval.encode(s);
}
return s;
}
};

View File

@ -25,7 +25,8 @@ BOOST_AUTO_TEST_CASE(test_connect) {
uint16_t topic_alias_max = 1200;
uint8_t request_response_information = 1;
uint8_t request_problem_information = 0;
std::string_view user_property = "user prop";
std::string_view user_property_1 = "user prop";
std::string_view user_property_2 = "user prop val";
std::string_view auth_method = "method";
std::string_view auth_data = "data";
// will
@ -38,7 +39,8 @@ BOOST_AUTO_TEST_CASE(test_connect) {
std::string_view will_content_type = "will content type";
std::string_view will_response_topic = "response_topic";
std::string_view will_correlation_data = "correlation data";
std::string_view will_user_property = "will prop";
std::string_view will_user_property_1 = "will prop";
std::string_view will_user_property_2 = "will prop val";
connect_props cprops;
cprops[prop::session_expiry_interval] = session_expiry_interval;
@ -47,7 +49,8 @@ BOOST_AUTO_TEST_CASE(test_connect) {
cprops[prop::topic_alias_maximum] = topic_alias_max;
cprops[prop::request_response_information] = request_response_information;
cprops[prop::request_problem_information] = request_problem_information;
cprops[prop::user_property].emplace_back(user_property);
cprops[prop::user_property].emplace_back(user_property_1);
cprops[prop::user_property].emplace_back(user_property_2);
cprops[prop::authentication_method] = auth_method;
cprops[prop::authentication_data] = auth_data;
@ -58,7 +61,8 @@ BOOST_AUTO_TEST_CASE(test_connect) {
w[prop::content_type] = will_content_type;
w[prop::response_topic] = will_response_topic;
w[prop::correlation_data] = will_correlation_data;
w[prop::user_property].emplace_back(will_user_property);
w[prop::user_property].emplace_back(will_user_property_1);
w[prop::user_property].emplace_back(will_user_property_2);
std::optional<will> will_opt { std::move(w) };
auto msg = encoders::encode_connect(
@ -67,11 +71,11 @@ BOOST_AUTO_TEST_CASE(test_connect) {
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing CONNECT fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_connect(remain_length, it);
BOOST_CHECK_MESSAGE(rv, "Parsing CONNECT failed.");
BOOST_ASSERT(rv);
const auto& [client_id_, uname_, password_, keep_alive_, clean_start_, cprops_, w_] = *rv;
BOOST_CHECK_EQUAL(client_id_, client_id);
@ -81,14 +85,16 @@ BOOST_AUTO_TEST_CASE(test_connect) {
BOOST_CHECK_EQUAL(keep_alive_, keep_alive);
BOOST_CHECK_EQUAL(clean_start_, clean_start);
cprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
cprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*cprops_[prop::session_expiry_interval], session_expiry_interval);
BOOST_CHECK_EQUAL(*cprops_[prop::receive_maximum], receive_max);
BOOST_CHECK_EQUAL(*cprops_[prop::maximum_packet_size], maximum_packet_size);
BOOST_CHECK_EQUAL(*cprops_[prop::topic_alias_maximum], topic_alias_max);
BOOST_CHECK_EQUAL(*cprops_[prop::request_response_information], request_response_information);
BOOST_CHECK_EQUAL(*cprops_[prop::request_problem_information], request_problem_information);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property);
BOOST_ASSERT(cprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][1], user_property_2);
BOOST_CHECK_EQUAL(*cprops_[prop::authentication_method], auth_method);
BOOST_CHECK_EQUAL(*cprops_[prop::authentication_data], auth_data);
@ -97,15 +103,16 @@ BOOST_AUTO_TEST_CASE(test_connect) {
BOOST_CHECK_EQUAL((*w_).topic(), will_topic);
BOOST_CHECK_EQUAL((*w_).message(), will_message);
(*w_).visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
(*w_).visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*(*w_)[prop::will_delay_interval], will_delay_interval);
BOOST_CHECK_EQUAL(*(*w_)[prop::payload_format_indicator], will_payload_format_indicator);
BOOST_CHECK_EQUAL(*(*w_)[prop::message_expiry_interval], will_message_expiry_interval);
BOOST_CHECK_EQUAL(*(*w_)[prop::content_type], will_content_type);
BOOST_CHECK_EQUAL(*(*w_)[prop::response_topic], will_response_topic);
BOOST_CHECK_EQUAL(*(*w_)[prop::correlation_data], will_correlation_data);
BOOST_CHECK_EQUAL((*w_)[prop::user_property][0], will_user_property);
BOOST_ASSERT((*w_)[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL((*w_)[prop::user_property][0], will_user_property_1);
BOOST_CHECK_EQUAL((*w_)[prop::user_property][1], will_user_property_2);
}
BOOST_AUTO_TEST_CASE(test_connack) {
@ -121,7 +128,8 @@ BOOST_AUTO_TEST_CASE(test_connack) {
std::string assigned_client_id = "client_id";
uint16_t topic_alias_max = 128;
std::string reason_string = "some reason string";
std::string user_property = "property";
std::string user_property_1 = "property";
std::string user_property_2 = "property val";
uint8_t wildcard_sub = 1;
uint8_t sub_id = 1;
uint8_t shared_sub = 0;
@ -140,7 +148,8 @@ BOOST_AUTO_TEST_CASE(test_connack) {
cprops[prop::assigned_client_identifier] = assigned_client_id;
cprops[prop::topic_alias_maximum] = topic_alias_max;
cprops[prop::reason_string] = reason_string;
cprops[prop::user_property].push_back(user_property);
cprops[prop::user_property].push_back(user_property_1);
cprops[prop::user_property].push_back(user_property_2);
cprops[prop::wildcard_subscription_available] = wildcard_sub;
cprops[prop::subscription_identifier_available] = sub_id;
cprops[prop::shared_subscription_available] = shared_sub;
@ -154,17 +163,17 @@ BOOST_AUTO_TEST_CASE(test_connack) {
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing CONNACK fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_connack(remain_length, it);
BOOST_CHECK_MESSAGE(rv, "Parsing CONNACK failed.");
BOOST_ASSERT(rv);
const auto& [session_present_, reason_code_, cprops_] = *rv;
BOOST_CHECK_EQUAL(session_present_, session_present);
BOOST_CHECK_EQUAL(reason_code_, reason_code);
cprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
cprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*cprops_[prop::session_expiry_interval], session_expiry_interval);
BOOST_CHECK_EQUAL(*cprops_[prop::receive_maximum], receive_maximum);
BOOST_CHECK_EQUAL(*cprops_[prop::maximum_qos], max_qos);
@ -173,7 +182,9 @@ BOOST_AUTO_TEST_CASE(test_connack) {
BOOST_CHECK_EQUAL(*cprops_[prop::assigned_client_identifier], assigned_client_id);
BOOST_CHECK_EQUAL(*cprops_[prop::topic_alias_maximum], topic_alias_max);
BOOST_CHECK_EQUAL(*cprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property);
BOOST_ASSERT(cprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][1], user_property_2);
BOOST_CHECK_EQUAL(*cprops_[prop::wildcard_subscription_available], wildcard_sub);
BOOST_CHECK_EQUAL(*cprops_[prop::subscription_identifier_available], sub_id);
BOOST_CHECK_EQUAL(*cprops_[prop::shared_subscription_available], shared_sub);
@ -195,8 +206,8 @@ BOOST_AUTO_TEST_CASE(test_publish) {
int16_t topic_alias = 16;
std::string response_topic = "topic/response";
std::string correlation_data = "correlation data";
std::string publish_prop_1 = "first publish prop";
std::string publish_prop_2 = "second publish prop";
std::string publish_prop_1 = "key";
std::string publish_prop_2 = "val";
uint32_t subscription_identifier = 123456;
std::string content_type = "application/octet-stream";
@ -219,11 +230,11 @@ BOOST_AUTO_TEST_CASE(test_publish) {
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing PUBLISH fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_publish(control_byte, remain_length, it);
BOOST_CHECK_MESSAGE(rv, "Parsing PUBLISH failed.");
BOOST_ASSERT(rv);
const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv;
BOOST_CHECK(packet_id);
@ -231,12 +242,13 @@ BOOST_AUTO_TEST_CASE(test_publish) {
BOOST_CHECK_EQUAL(topic_, topic);
BOOST_CHECK_EQUAL(payload_, payload);
pprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
pprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*pprops_[prop::payload_format_indicator], payload_format_indicator);
BOOST_CHECK_EQUAL(*pprops_[prop::message_expiry_interval], message_expiry_interval);
BOOST_CHECK_EQUAL(*pprops_[prop::topic_alias], topic_alias);
BOOST_CHECK_EQUAL(*pprops_[prop::response_topic], response_topic);
BOOST_CHECK_EQUAL(*pprops_[prop::correlation_data], correlation_data);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], publish_prop_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], publish_prop_2);
BOOST_CHECK_EQUAL(*pprops_[prop::subscription_identifier], subscription_identifier);
@ -257,11 +269,11 @@ BOOST_AUTO_TEST_CASE(test_large_publish) {
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing PUBLISH fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_publish(control_byte, remain_length, it);
BOOST_CHECK_MESSAGE(rv, "Parsing PUBLISH failed.");
BOOST_ASSERT(rv);
const auto& [topic_, packet_id_, flags, pprops, payload_] = *rv;
BOOST_CHECK(packet_id);
@ -276,17 +288,19 @@ BOOST_AUTO_TEST_CASE(test_puback) {
uint8_t reason_code = 0x93;
std::string reason_string = "PUBACK reason string";
std::string user_prop = "PUBACK user prop";
std::string user_property_1 = "PUBACK user prop";
std::string user_property_2 = "PUBACK user prop val";
puback_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_prop);
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
auto msg = encoders::encode_puback(packet_id, reason_code, pprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing PUBACK fixed header failed.");
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
@ -294,13 +308,15 @@ BOOST_AUTO_TEST_CASE(test_puback) {
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_puback(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(rv, "Parsing PUBACK failed.");
BOOST_ASSERT(rv);
const auto& [reason_code_, pprops_] = *rv;
pprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
pprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_prop);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pubrec) {
@ -309,17 +325,19 @@ BOOST_AUTO_TEST_CASE(test_pubrec) {
uint8_t reason_code = 0x92;
std::string reason_string = "PUBREC reason string";
std::string user_prop = "PUBREC user prop";
std::string user_property_1 = "PUBREC user prop";
std::string user_property_2 = "PUBREC user prop val";
pubrec_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_prop);
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
auto msg = encoders::encode_pubrec(packet_id, reason_code, pprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing PUBREC fixed header failed.");
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
@ -327,13 +345,15 @@ BOOST_AUTO_TEST_CASE(test_pubrec) {
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_pubrec(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(rv, "Parsing PUBREC failed.");
BOOST_ASSERT(rv);
const auto& [reason_code_, pprops_] = *rv;
pprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
pprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_prop);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pubrel) {
@ -342,17 +362,19 @@ BOOST_AUTO_TEST_CASE(test_pubrel) {
uint8_t reason_code = 0x00;
std::string reason_string = "PUBREL reason string";
std::string user_prop = "PUBREL user prop";
std::string user_property_1 = "PUBREL user prop";
std::string user_property_2 = "PUBREL user prop val";
pubrel_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_prop);
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
auto msg = encoders::encode_pubrel(packet_id, reason_code, pprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing PUBREL fixed header failed.");
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
@ -360,13 +382,15 @@ BOOST_AUTO_TEST_CASE(test_pubrel) {
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_pubrel(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(rv, "Parsing PUBREL failed.");
BOOST_ASSERT(rv);
const auto& [reason_code_, pprops_] = *rv;
pprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
pprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_prop);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pubcomp) {
@ -375,17 +399,19 @@ BOOST_AUTO_TEST_CASE(test_pubcomp) {
uint8_t reason_code = 0x00;
std::string reason_string = "PUBCOMP reason string";
std::string user_prop = "PUBCOMP user prop";
std::string user_property_1 = "PUBCOMP user prop";
std::string user_property_2 = "PUBCOMP user prop val";
pubcomp_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_prop);
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
auto msg = encoders::encode_pubcomp(packet_id, reason_code, pprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing PUBCOMP fixed header failed.");
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
@ -393,19 +419,22 @@ BOOST_AUTO_TEST_CASE(test_pubcomp) {
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_pubcomp(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(rv, "Parsing PUBCOMP failed.");
BOOST_ASSERT(rv);;
const auto& [reason_code_, pprops_] = *rv;
pprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
pprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_prop);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_subscribe) {
//testing variables
uint32_t sub_id = 1'234'567;
std::string user_prop = "SUBSCRIBE user prop";
std::string user_property_1 = "SUBSCRIBE user prop";
std::string user_property_2 = "SUBSCRIBE user prop val";
// delete using lines when we shorten the enum names
using no_local_e = subscribe_options::no_local_e;
@ -419,7 +448,8 @@ BOOST_AUTO_TEST_CASE(test_subscribe) {
subscribe_props sprops;
sprops[prop::subscription_identifier] = sub_id;
sprops[prop::user_property].push_back(user_prop);
sprops[prop::user_property].push_back(user_property_1);
sprops[prop::user_property].push_back(user_property_2);
std::vector<subscribe_topic> filters {
{
@ -433,14 +463,14 @@ BOOST_AUTO_TEST_CASE(test_subscribe) {
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing SUBSCRIBE fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
BOOST_CHECK_EQUAL(*packet_id_, packet_id);
auto rv = decoders::decode_subscribe(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(rv, "Parsing SUBSCRIBE failed.");
BOOST_ASSERT(rv);
const auto& [sprops_, filters_] = *rv;
const auto& filter_ = filters_[0];
@ -453,9 +483,11 @@ BOOST_AUTO_TEST_CASE(test_subscribe) {
static_cast<uint8_t>(qos);
BOOST_CHECK_EQUAL(options_, mask);
sprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
sprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*sprops_[prop::subscription_identifier], sub_id);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_prop);
BOOST_ASSERT(sprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_suback) {
@ -464,31 +496,35 @@ BOOST_AUTO_TEST_CASE(test_suback) {
std::vector<uint8_t> reason_codes { 48, 28 };
std::string reason_string = "subscription accepted";
std::string user_prop = "SUBACK user prop";
std::string user_property_1 = "SUBACK user prop";
std::string user_property_2 = "SUBACK user prop val";
suback_props sprops;
sprops[prop::reason_string] = reason_string;
sprops[prop::user_property].push_back(user_prop);
sprops[prop::user_property].push_back(user_property_1);
sprops[prop::user_property].push_back(user_property_2);
auto msg = encoders::encode_suback(packet_id, reason_codes, sprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing SUBACK fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
BOOST_CHECK_EQUAL(*packet_id_, packet_id);
auto rv = decoders::decode_suback(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(rv, "Parsing SUBACK failed.");
BOOST_ASSERT(rv);
const auto& [sprops_, reason_codes_] = *rv;
BOOST_CHECK(reason_codes_ == reason_codes);
sprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
sprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*sprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_prop);
BOOST_ASSERT(sprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_unsubscribe) {
@ -496,29 +532,33 @@ BOOST_AUTO_TEST_CASE(test_unsubscribe) {
uint16_t packet_id = 14423;
std::vector<std::string> topics { "first topic", "second/topic" };
std::string user_prop = "UNSUBSCRIBE user prop";
std::string user_property_1 = "UNSUBSCRIBE user prop";
std::string user_property_2 = "UNSUBSCRIBE user prop val";
unsubscribe_props uprops;
uprops[prop::user_property].push_back(user_prop);
uprops[prop::user_property].push_back(user_property_1);
uprops[prop::user_property].push_back(user_property_2);
auto msg = encoders::encode_unsubscribe(packet_id, topics, uprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing UNSUBSCRIBE fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
BOOST_CHECK_EQUAL(*packet_id_, packet_id);
auto rv = decoders::decode_unsubscribe(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(rv, "Parsing UNSUBSCRIBE failed.");
BOOST_ASSERT(rv);
const auto& [uprops_, topics_] = *rv;
BOOST_CHECK(topics_ == topics);
uprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_prop);
uprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_ASSERT(uprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_unsuback) {
@ -527,31 +567,35 @@ BOOST_AUTO_TEST_CASE(test_unsuback) {
std::vector<uint8_t> reason_codes{ 48, 28 };
std::string reason_string = "some unsuback reason string";
std::string user_prop = "UNSUBACK user prop";
std::string user_property_1 = "SUBACK user prop";
std::string user_property_2 = "SUBACK user prop val";
unsuback_props uprops;
uprops[prop::reason_string] = reason_string;
uprops[prop::user_property].push_back(user_prop);
uprops[prop::user_property].push_back(user_property_1);
uprops[prop::user_property].push_back(user_property_2);
auto msg = encoders::encode_unsuback(packet_id, reason_codes, uprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing UNSUBACK fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_CHECK(packet_id);
BOOST_CHECK_EQUAL(*packet_id_, packet_id);
auto rv = decoders::decode_unsuback(remain_length - sizeof(uint16_t), it);
BOOST_CHECK_MESSAGE(header, "Parsing UNSUBACK failed.");
BOOST_ASSERT(rv);
const auto& [uprops_, reason_codes_] = *rv;
BOOST_CHECK(reason_codes_ == reason_codes);
uprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
uprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*uprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_prop);
BOOST_ASSERT(uprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_disconnect) {
@ -560,32 +604,36 @@ BOOST_AUTO_TEST_CASE(test_disconnect) {
int32_t session_expiry_interval = 50;
std::string reason_string = "a reason";
std::string user_property = "DISCONNECT user property";
std::string user_property_1 = "DISCONNECT user prop";
std::string user_property_2 = "DISCONNECT user prop val";
std::string server_reference = "server";
disconnect_props dprops;
dprops[prop::session_expiry_interval] = session_expiry_interval;
dprops[prop::reason_string] = reason_string;
dprops[prop::user_property].emplace_back(user_property);
dprops[prop::user_property].emplace_back(user_property_1);
dprops[prop::user_property].emplace_back(user_property_2);
dprops[prop::server_reference] = server_reference;
auto msg = encoders::encode_disconnect(reason_code, dprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing DISCONNECT fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_disconnect(remain_length, it);
BOOST_CHECK_MESSAGE(header, "Parsing DISCONNECT failed.");
BOOST_ASSERT(rv);
const auto& [reason_code_, dprops_] = *rv;
BOOST_CHECK_EQUAL(reason_code_, reason_code);
dprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
dprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*dprops_[prop::session_expiry_interval], session_expiry_interval);
BOOST_CHECK_EQUAL(*dprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(dprops_[prop::user_property][0], user_property);
BOOST_ASSERT(dprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(dprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(dprops_[prop::user_property][1], user_property_2);
BOOST_CHECK_EQUAL(*dprops_[prop::server_reference], server_reference);
}
@ -597,32 +645,36 @@ BOOST_AUTO_TEST_CASE(test_auth) {
std::string authentication_data = "data";
std::string reason_string = "reason";
std::string user_property = "AUTH user propety";
std::string user_property_1 = "AUTH user propety";
std::string user_property_2 = "AUTH user propety val";
auth_props aprops;
aprops[prop::authentication_method] = authentication_method;
aprops[prop::authentication_data] = authentication_data;
aprops[prop::reason_string] = reason_string;
aprops[prop::user_property].emplace_back(user_property);
aprops[prop::user_property].emplace_back(user_property_1);
aprops[prop::user_property].emplace_back(user_property_2);
auto msg = encoders::encode_auth(reason_code, aprops);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_CHECK_MESSAGE(header, "Parsing AUTH fixed header failed.");
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_auth(remain_length, it);
BOOST_CHECK_MESSAGE(rv, "Parsing AUTH failed.");
BOOST_ASSERT(rv);
const auto& [reason_code_, aprops_] = *rv;
BOOST_CHECK_EQUAL(reason_code_, reason_code);
aprops_.visit([](const auto& prop, const auto&) { BOOST_CHECK(prop); return true; });
aprops_.visit([](const auto& prop, const auto&) { BOOST_ASSERT(prop); return true; });
BOOST_CHECK_EQUAL(*aprops_[prop::authentication_method], authentication_method);
BOOST_CHECK_EQUAL(*aprops_[prop::authentication_data], authentication_data);
BOOST_CHECK_EQUAL(*aprops_[prop::reason_string], reason_string);
BOOST_CHECK_EQUAL(aprops_[prop::user_property][0], user_property);
BOOST_ASSERT(aprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(aprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(aprops_[prop::user_property][1], user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pingreq) {
@ -639,4 +691,171 @@ BOOST_AUTO_TEST_CASE(test_pingresp) {
BOOST_CHECK_EQUAL(msg, encoded_pingresp);
}
BOOST_AUTO_TEST_CASE(empty_user_property) {
publish_props pprops;
pprops[prop::user_property].emplace_back("");
pprops[prop::user_property].emplace_back("");
auto msg = encoders::encode_publish(
1, "topic", "payload",
qos_e::at_least_once, retain_e::yes, dup_e::no,
pprops
);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_publish(control_byte, remain_length, it);
BOOST_ASSERT(rv);
const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], "");
BOOST_CHECK_EQUAL(user_props_[1], "");
}
BOOST_AUTO_TEST_CASE(omit_invalid_user_property) {
// testing variables
std::string_view user_property_1 = "key";
std::string_view user_property_2 = "val";
std::string_view user_property_3 = "lone key";
publish_props pprops;
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
pprops[prop::user_property].emplace_back(user_property_3);
auto msg = encoders::encode_publish(
1, "topic", "payload",
qos_e::at_least_once, retain_e::yes, dup_e::no,
pprops
);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_publish(control_byte, remain_length, it);
BOOST_ASSERT(rv);
const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], user_property_1);
BOOST_CHECK_EQUAL(user_props_[1], user_property_2);
}
BOOST_AUTO_TEST_CASE(omit_all_user_property) {
// testing variables
std::string_view user_property_1 = "key";
publish_props pprops;
pprops[prop::user_property].emplace_back(user_property_1);
auto msg = encoders::encode_publish(
1, "topic", "payload",
qos_e::at_least_once, retain_e::yes, dup_e::no,
pprops
);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_publish(control_byte, remain_length, it);
BOOST_ASSERT(rv);
const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_CHECK(user_props_.size() == 0);
}
BOOST_AUTO_TEST_CASE(deserialize_user_property) {
// testing variables
const char puback[] = {
64, 15, 0, 1, 0, 11, 38, 0, 3, 'k', 'e', 'y', 0, 3, 'v', 'a', 'l'
};
std::string msg { puback, sizeof(puback) / sizeof(char) };
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_ASSERT(packet_id_);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_puback(remain_length - sizeof(uint16_t), it);
BOOST_ASSERT(rv);
const auto& [reason_code_, pprops_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], "key");
BOOST_CHECK_EQUAL(user_props_[1], "val");
}
BOOST_AUTO_TEST_CASE(deserialize_empty_user_property) {
// testing variables
const char puback[] = {
64, 9, 0, 1, 0, 5, 38, 0, 0, 0, 0
};
std::string msg{ puback, sizeof(puback) / sizeof(char) };
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_ASSERT(packet_id_);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_puback(remain_length - sizeof(uint16_t), it);
BOOST_ASSERT(rv);
const auto& [reason_code_, pprops_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], "");
BOOST_CHECK_EQUAL(user_props_[1], "");
}
BOOST_AUTO_TEST_CASE(malformed_user_property) {
// testing variables
const char malformed_puback[] = {
64, 10, 0, 1, 0, 6, 38, 0, 3, 'k', 'e', 'y' // missing value
};
std::string msg { malformed_puback, sizeof(malformed_puback) / sizeof(char) };
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_ASSERT(packet_id_);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_puback(remain_length - sizeof(uint16_t), it);
BOOST_CHECK(!rv);
}
BOOST_AUTO_TEST_CASE(malformed_reason_string) {
// testing variables
const char malformed_puback[] = {
64, 6, 0, 1, 0, 2, 31, 1
};
std::string msg { malformed_puback, sizeof(malformed_puback) / sizeof(char) };
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
auto packet_id_ = decoders::decode_packet_id(it);
BOOST_ASSERT(packet_id_);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_puback(remain_length - sizeof(uint16_t), it);
BOOST_CHECK(!rv);
}
BOOST_AUTO_TEST_SUITE_END()