diff --git a/include/async_mqtt5/error.hpp b/include/async_mqtt5/error.hpp index 8ee9e85..7afe863 100644 --- a/include/async_mqtt5/error.hpp +++ b/include/async_mqtt5/error.hpp @@ -86,7 +86,7 @@ enum class error : int { subscription_identifier_not_available, /** The Server does not support Shared Subscriptions. */ - shared_subscription_not_available, + shared_subscription_not_available }; @@ -122,6 +122,11 @@ inline std::string client_error_to_string(error err) { } } +inline std::ostream& operator<<(std::ostream& os, const client::error& err) { + os << client_error_to_string(err); + return os; +} + struct client_ec_category : public boost::system::error_category { const char* name() const noexcept override { return "mqtt_client_error"; } std::string message(int ev) const noexcept override { diff --git a/test/integration/cancellation.cpp b/test/integration/cancellation.cpp index 7de8fde..2f245b7 100644 --- a/test/integration/cancellation.cpp +++ b/test/integration/cancellation.cpp @@ -50,7 +50,7 @@ void setup_cancel_op_test_case( signal.slot(), [&handlers_called](error_code ec) { handlers_called++; - BOOST_CHECK_EQUAL(ec, asio::error::operation_aborted); + BOOST_TEST(ec == asio::error::operation_aborted); } ) ); @@ -69,7 +69,7 @@ void setup_cancel_op_test_case( signal.slot(), [&handlers_called](error_code ec) { handlers_called++; - BOOST_CHECK_EQUAL(ec, asio::error::operation_aborted); + BOOST_TEST(ec == asio::error::operation_aborted); } ) ); @@ -90,9 +90,9 @@ void setup_cancel_op_test_case( error_code ec, std::string t, std::string p, publish_props ) { handlers_called++; - BOOST_CHECK_EQUAL(ec, asio::error::operation_aborted); - BOOST_CHECK_EQUAL(t, ""); - BOOST_CHECK_EQUAL(p, ""); + BOOST_TEST(ec == asio::error::operation_aborted); + BOOST_TEST(t == ""); + BOOST_TEST(p == ""); } ) ); @@ -114,9 +114,9 @@ void setup_cancel_op_test_case( error_code ec, std::vector rcs, unsuback_props ) { handlers_called++; - BOOST_CHECK_EQUAL(ec, asio::error::operation_aborted); - BOOST_ASSERT(rcs.size() == 1); - BOOST_CHECK(rcs[0] == reason_codes::empty); + BOOST_TEST(ec == asio::error::operation_aborted); + BOOST_TEST_REQUIRE(rcs.size() == 1u); + BOOST_TEST(rcs[0] == reason_codes::empty); } ) ); @@ -138,9 +138,9 @@ void setup_cancel_op_test_case( error_code ec, std::vector rcs, suback_props ) { handlers_called++; - BOOST_CHECK_EQUAL(ec, asio::error::operation_aborted); - BOOST_ASSERT(rcs.size() == 1); - BOOST_CHECK(rcs[0] == reason_codes::empty); + BOOST_TEST(ec == asio::error::operation_aborted); + BOOST_TEST_REQUIRE(rcs.size() == 1u); + BOOST_TEST(rcs[0] == reason_codes::empty); } ) ); @@ -307,24 +307,24 @@ BOOST_FIXTURE_TEST_CASE(rerunning_the_client, shared_test_data, *boost::unit_tes auto [ec, rc, props] = co_await c.async_publish( topic, payload, retain_e::no, publish_props {}, use_nothrow_awaitable ); - BOOST_CHECK(!ec); - BOOST_CHECK(!rc); + BOOST_TEST(!ec); + BOOST_TEST(!rc); c.cancel(); auto [cec, crc, cprops] = co_await c.async_publish( topic, payload, retain_e::no, publish_props {}, use_nothrow_awaitable ); - BOOST_CHECK_EQUAL(cec, asio::error::operation_aborted); - BOOST_CHECK_EQUAL(crc, reason_codes::empty); + BOOST_TEST(cec == asio::error::operation_aborted); + BOOST_TEST(crc == reason_codes::empty); c.async_run(asio::detached); auto [rec, rrc, rprops] = co_await c.async_publish( topic, payload, retain_e::no, publish_props {}, use_nothrow_awaitable ); - BOOST_CHECK(!rec); - BOOST_CHECK(!rrc); + BOOST_TEST(!rec); + BOOST_TEST(!rrc); co_await c.async_disconnect(use_nothrow_awaitable); co_return; @@ -333,7 +333,7 @@ BOOST_FIXTURE_TEST_CASE(rerunning_the_client, shared_test_data, *boost::unit_tes ); ioc.run(); - BOOST_CHECK(broker.received_all_expected()); + BOOST_TEST(broker.received_all_expected()); } #endif diff --git a/test/integration/re_authentication.cpp b/test/integration/re_authentication.cpp index 435bde0..39f2856 100644 --- a/test/integration/re_authentication.cpp +++ b/test/integration/re_authentication.cpp @@ -133,7 +133,8 @@ BOOST_FIXTURE_TEST_CASE(malformed_auth_rc, shared_test_data) { .expect(auth_challenge) .complete_with(success, after(2ms)) .reply_with(malformed_auth, after(4ms)) - .expect(disconnect); + .expect(disconnect) + .complete_with(success, after(2ms)); run_test(std::move(broker_side), test::test_authenticator()); } @@ -159,7 +160,29 @@ BOOST_FIXTURE_TEST_CASE(mismatched_auth_method, shared_test_data) { .expect(auth_challenge) .complete_with(success, after(2ms)) .reply_with(mismatched_auth_response, after(4ms)) - .expect(disconnect); + .expect(disconnect) + .complete_with(success, after(2ms)); + + run_test(std::move(broker_side), test::test_authenticator()); +} + +BOOST_FIXTURE_TEST_CASE(malformed_auth_received, shared_test_data) { + auto malformed_auth = std::string { -16, 3, 24, 15, 1, 0 }; + auto disconnect = encoders::encode_disconnect( + reason_codes::malformed_packet.value(), + test::dprops_with_reason_string("Malformed AUTH received: cannot decode") + ); + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(0ms)) + .reply_with(connack, after(1ms)) + .expect(auth_challenge) + .complete_with(success, after(2ms)) + .reply_with(malformed_auth, after(4ms)) + .expect(disconnect) + .complete_with(success, after(2ms)); run_test(std::move(broker_side), test::test_authenticator()); } @@ -178,7 +201,8 @@ BOOST_FIXTURE_TEST_CASE(async_auth_fail, shared_test_data) { .expect(auth_challenge) .complete_with(success, after(2ms)) .reply_with(auth_response, after(4ms)) - .expect(disconnect); + .expect(disconnect) + .complete_with(success, after(2ms)); run_test( std::move(broker_side), @@ -201,7 +225,8 @@ BOOST_FIXTURE_TEST_CASE(unexpected_auth, shared_test_data) { .complete_with(success, after(0ms)) .reply_with(connack, after(1ms)) .send(auth_challenge, after(50ms)) - .expect(disconnect); + .expect(disconnect) + .complete_with(success, after(2ms)); run_test( std::move(broker_side) diff --git a/test/integration/read_message.cpp b/test/integration/read_message.cpp index c1eab4a..8059505 100644 --- a/test/integration/read_message.cpp +++ b/test/integration/read_message.cpp @@ -203,7 +203,7 @@ BOOST_FIXTURE_TEST_CASE(receive_byte_by_byte, shared_test_data) { for (size_t i = 0; i < publish.size(); i++) broker_side.send( - std::string { publish[i] }, after(std::chrono::milliseconds(i + 5)) + std::string { publish[i] }, after(std::chrono::milliseconds(i + 7)) ); auto verify_fun = [&]( diff --git a/test/integration/receive_publish.cpp b/test/integration/receive_publish.cpp index 1bfb9a9..a63fd06 100644 --- a/test/integration/receive_publish.cpp +++ b/test/integration/receive_publish.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -19,11 +20,6 @@ struct shared_test_data { error_code success {}; error_code fail = asio::error::not_connected; - connect_props cprops = allow_big_packets_cprops(); - const std::string connect_with_cprops = encoders::encode_connect( - "", std::nullopt, std::nullopt, 60, false, cprops, std::nullopt - ); - const std::string connect = encoders::encode_connect( "", std::nullopt, std::nullopt, 60, false, {}, std::nullopt ); @@ -49,20 +45,14 @@ struct shared_test_data { const std::string pubrec = encoders::encode_pubrec(1, uint8_t(0x00), {}); const std::string pubrel = encoders::encode_pubrel(1, uint8_t(0x00), {}); const std::string pubcomp = encoders::encode_pubcomp(1, uint8_t(0x00), {}); - - -private: - connect_props allow_big_packets_cprops() { - connect_props c_props; - c_props[prop::maximum_packet_size] = 10'000'000; - return c_props; - } }; using test::after; using namespace std::chrono; -void run_test(test::msg_exchange broker_side) { +void run_test( + test::msg_exchange broker_side, connect_props cprops = {} +) { constexpr int expected_handlers_called = 1; int handlers_called = 0; @@ -75,18 +65,17 @@ void run_test(test::msg_exchange broker_side) { using client_type = mqtt_client; client_type c(executor, ""); c.brokers("127.0.0.1,127.0.0.1") // to avoid reconnect backoff + .connect_properties(cprops) .async_run(asio::detached); c.async_receive([&handlers_called, &c]( error_code ec, std::string rec_topic, std::string rec_payload, publish_props ){ ++handlers_called; - auto data = shared_test_data(); BOOST_TEST(!ec); BOOST_TEST(data.topic == rec_topic); BOOST_TEST(data.payload == rec_payload); - c.cancel(); } ); @@ -95,7 +84,6 @@ void run_test(test::msg_exchange broker_side) { BOOST_TEST(handlers_called == expected_handlers_called); BOOST_TEST(broker.received_all_expected()); } - BOOST_FIXTURE_TEST_CASE(receive_publish_qos0, shared_test_data) { test::msg_exchange broker_side; @@ -166,6 +154,36 @@ BOOST_FIXTURE_TEST_CASE(receive_malformed_publish, shared_test_data) { } BOOST_FIXTURE_TEST_CASE(receive_malformed_pubrel, shared_test_data) { + // packets + auto malformed_pubrel = std::string { 98, 6, 0, 1, 0, 2, 31, 1 }; + + auto disconnect = encoders::encode_disconnect( + reason_codes::malformed_packet.value(), + test::dprops_with_reason_string("Malformed PUBREL received: cannot decode") + ); + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(0ms)) + .reply_with(connack, after(0ms)) + .send(publish_qos2, after(10ms)) + .expect(pubrec) + .complete_with(success, after(1ms)) + .reply_with(malformed_pubrel, after(2ms)) + .expect(disconnect) + .complete_with(success, after(1ms)) + .expect(connect) + .complete_with(success, after(0ms)) + .reply_with(connack, after(0ms)) + .send(pubrel, after(100ms)) + .expect(pubcomp) + .complete_with(success, after(1ms)); + + run_test(std::move(broker_side)); +} + +BOOST_FIXTURE_TEST_CASE(receive_invalid_rc_pubrel, shared_test_data) { // packets auto malformed_pubrel = encoders::encode_pubrel(1, uint8_t(0x04), {}); @@ -299,15 +317,18 @@ BOOST_FIXTURE_TEST_CASE(fail_to_send_pubcomp, shared_test_data) { } BOOST_FIXTURE_TEST_CASE(receive_big_publish, shared_test_data) { - const int expected_handlers_called = 1; - int handlers_called = 0; - // data + connect_props cprops; + cprops[prop::maximum_packet_size] = 10'000'000; + publish_props big_props; for (int i = 0; i < 100; i++) big_props[prop::user_property].push_back(std::string(65534, 'u')); // packets + auto connect_big_packets = encoders::encode_connect( + "", std::nullopt, std::nullopt, 60, false, cprops, std::nullopt + ); auto big_publish = encoders::encode_publish( 1, topic, payload, qos_e::at_most_once, retain_e::no, dup_e::no, @@ -316,11 +337,38 @@ BOOST_FIXTURE_TEST_CASE(receive_big_publish, shared_test_data) { test::msg_exchange broker_side; broker_side - .expect(connect_with_cprops) + .expect(connect_big_packets) .complete_with(success, after(1ms)) .reply_with(connack, after(2ms)) .send(big_publish, after(1s)); + run_test(std::move(broker_side), cprops); +} + +BOOST_FIXTURE_TEST_CASE(receive_buffer_overflow, shared_test_data) { + constexpr int expected_handlers_called = 1; + int handlers_called = 0; + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(0ms)) + .reply_with(connack, after(0ms)); + + std::vector buffers; + buffers.reserve(65536); + + for (int i = 0; i < 65536; ++i) + buffers.push_back( + encoders::encode_publish( + 0, "topic_" + std::to_string(i), + "payload", qos_e::at_most_once, + retain_e::no, dup_e::no, {} + ) + ); + + broker_side.send(boost::algorithm::join(buffers, ""), after(20ms)); + asio::io_context ioc; auto executor = ioc.get_executor(); auto& broker = asio::make_service( @@ -330,21 +378,25 @@ BOOST_FIXTURE_TEST_CASE(receive_big_publish, shared_test_data) { using client_type = mqtt_client; client_type c(executor, ""); c.brokers("127.0.0.1") - .connect_properties(cprops) .async_run(asio::detached); - c.async_receive([&]( - error_code ec, std::string topic_, std::string payload_, publish_props pprops - ) { - handlers_called++; - - BOOST_TEST(!ec); - BOOST_TEST(topic == topic_); - BOOST_TEST(payload == payload_); - BOOST_TEST(pprops[prop::user_property].size() == 100u); - - c.cancel(); - }); + asio::steady_timer timer(executor); + timer.expires_after(7s); + timer.async_wait( + [&](error_code) { + c.async_receive([&]( + error_code ec, std::string rec_topic, + std::string rec_payload, publish_props + ){ + ++handlers_called; + BOOST_TEST(!ec); + BOOST_TEST("topic_1" == rec_topic); + BOOST_TEST(payload == rec_payload); + c.cancel(); + } + ); + } + ); ioc.run(); BOOST_TEST(handlers_called == expected_handlers_called); diff --git a/test/integration/send_publish.cpp b/test/integration/send_publish.cpp index 2eab171..ada76b6 100644 --- a/test/integration/send_publish.cpp +++ b/test/integration/send_publish.cpp @@ -88,8 +88,8 @@ void run_test( ); ioc.run_for(2s); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); - BOOST_CHECK(broker.received_all_expected()); + BOOST_TEST(handlers_called == expected_handlers_called); + BOOST_TEST(broker.received_all_expected()); } BOOST_FIXTURE_TEST_CASE(send_publish_qos0, shared_test_data) { @@ -202,6 +202,44 @@ BOOST_FIXTURE_TEST_CASE(fail_to_send_pubrel, shared_test_data) { } BOOST_FIXTURE_TEST_CASE(receive_malformed_puback, shared_test_data) { + // packets + auto publish_qos1_dup = encoders::encode_publish( + 1, topic, payload, qos_e::at_least_once, retain_e::no, dup_e::yes, {} + ); + auto malformed_puback = std::string { 64, 6, 0, 1, 0, 2, 31, 1 }; + + auto disconnect = encoders::encode_disconnect( + reason_codes::malformed_packet.value(), + test::dprops_with_reason_string("Malformed PUBACK: cannot decode") + ); + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(1ms)) + .reply_with(connack, after(2ms)) + .expect(publish_qos1) + .complete_with(success, after(1ms)) + .reply_with(malformed_puback, after(2ms)) + .expect(disconnect) + .complete_with(success, after(1ms)) + .expect(connect) + .complete_with(success, after(1ms)) + .reply_with(connack, after(2ms)) + .expect(publish_qos1_dup) + .complete_with(success, after(1ms)) + .reply_with(puback, after(2ms)); + + run_test( + std::move(broker_side), + [](error_code ec, reason_code rc, puback_props) { + BOOST_TEST(!ec); + BOOST_TEST(rc == reason_codes::success); + } + ); +} + +BOOST_FIXTURE_TEST_CASE(receive_invalid_rc_puback, shared_test_data) { // packets auto publish_qos1_dup = encoders::encode_publish( 1, topic, payload, qos_e::at_least_once, retain_e::no, dup_e::yes, {} @@ -245,6 +283,48 @@ BOOST_FIXTURE_TEST_CASE(receive_malformed_pubrec, shared_test_data) { 1, topic, payload, qos_e::exactly_once, retain_e::no, dup_e::yes, {} ); + auto malformed_pubrec = std::string { 80, 6, 0, 1, 0, 2, 31, 1 }; + + auto disconnect = encoders::encode_disconnect( + reason_codes::malformed_packet.value(), + test::dprops_with_reason_string("Malformed PUBREC: cannot decode") + ); + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(1ms)) + .reply_with(connack, after(2ms)) + .expect(publish_qos2) + .complete_with(success, after(1ms)) + .reply_with(malformed_pubrec, after(2ms)) + .expect(disconnect) + .complete_with(success, after(1ms)) + .expect(connect) + .complete_with(success, after(1ms)) + .reply_with(connack, after(2ms)) + .expect(publish_qos2_dup) + .complete_with(success, after(1ms)) + .reply_with(pubrec, after(2ms)) + .expect(pubrel) + .complete_with(success, after(1ms)) + .reply_with(pubcomp, after(2ms)); + + run_test( + std::move(broker_side), + [](error_code ec, reason_code rc, pubcomp_props) { + BOOST_TEST(!ec); + BOOST_TEST(rc == reason_codes::success); + } + ); +} + +BOOST_FIXTURE_TEST_CASE(receive_invalid_rc_pubrec, shared_test_data) { + // packets + auto publish_qos2_dup = encoders::encode_publish( + 1, topic, payload, qos_e::exactly_once, retain_e::no, dup_e::yes, {} + ); + auto malformed_pubrec = encoders::encode_pubrec(1, uint8_t(0x04), {}); auto disconnect = encoders::encode_disconnect( @@ -282,6 +362,44 @@ BOOST_FIXTURE_TEST_CASE(receive_malformed_pubrec, shared_test_data) { } BOOST_FIXTURE_TEST_CASE(receive_malformed_pubcomp, shared_test_data) { + // packets + auto malformed_pubcomp = std::string { 112, 6, 0, 1, 0, 2, 31, 1 }; + + auto disconnect = encoders::encode_disconnect( + reason_codes::malformed_packet.value(), + test::dprops_with_reason_string("Malformed PUBCOMP: cannot decode") + ); + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(1ms)) + .reply_with(connack, after(2ms)) + .expect(publish_qos2) + .complete_with(success, after(1ms)) + .reply_with(pubrec, after(2ms)) + .expect(pubrel) + .complete_with(success, after(1ms)) + .reply_with(malformed_pubcomp, after(2ms)) + .expect(disconnect) + .complete_with(success, after(1ms)) + .expect(connect) + .complete_with(success, after(1ms)) + .reply_with(connack, after(2ms)) + .expect(pubrel) + .complete_with(success, after(1ms)) + .reply_with(pubcomp, after(2ms)); + + run_test( + std::move(broker_side), + [](error_code ec, reason_code rc, pubcomp_props) { + BOOST_TEST(!ec); + BOOST_TEST(rc == reason_codes::success); + } + ); +} + +BOOST_FIXTURE_TEST_CASE(receive_invalid_rc_pubcomp, shared_test_data) { // packets auto malformed_pubcomp = encoders::encode_pubcomp(1, uint8_t(0x04), {}); diff --git a/test/unit/disconnect_op.cpp b/test/unit/disconnect_op.cpp index 1556f23..30a73e8 100644 --- a/test/unit/disconnect_op.cpp +++ b/test/unit/disconnect_op.cpp @@ -28,7 +28,7 @@ void run_malformed_props_test(const disconnect_props& dprops) { auto handler = [&handlers_called](error_code ec) { ++handlers_called; - BOOST_CHECK(ec == client::error::malformed_packet); + BOOST_TEST(ec == client::error::malformed_packet); }; detail::disconnect_ctx ctx; @@ -40,7 +40,7 @@ void run_malformed_props_test(const disconnect_props& dprops) { .perform(); ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); + BOOST_TEST(handlers_called == expected_handlers_called); } @@ -85,9 +85,9 @@ BOOST_AUTO_TEST_CASE(omit_props) { reason_codes::normal_disconnection.value(), disconnect_props {} ); - test::msg_exchange broker_side; error_code success {}; + test::msg_exchange broker_side; broker_side .expect(connect) .complete_with(success, after(0ms)) @@ -113,14 +113,14 @@ BOOST_AUTO_TEST_CASE(omit_props) { disconnect_rc_e::normal_disconnection, props, [&](error_code ec) { handlers_called++; - BOOST_CHECK(!ec); + BOOST_TEST(!ec); } ); }); ioc.run_for(2s); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); - BOOST_CHECK(broker.received_all_expected()); + BOOST_TEST(handlers_called == expected_handlers_called); + BOOST_TEST(broker.received_all_expected()); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/unit/publish_send_op.cpp b/test/unit/publish_send_op.cpp index e65d5ee..048353d 100644 --- a/test/unit/publish_send_op.cpp +++ b/test/unit/publish_send_op.cpp @@ -24,8 +24,8 @@ BOOST_AUTO_TEST_CASE(pid_overrun) { auto handler = [&handlers_called](error_code ec, reason_code rc, puback_props) { ++handlers_called; - BOOST_CHECK(ec == client::error::pid_overrun); - BOOST_CHECK_EQUAL(rc, reason_codes::empty); + BOOST_TEST(ec == client::error::pid_overrun); + BOOST_TEST(rc == reason_codes::empty); }; detail::publish_send_op< @@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(pid_overrun) { ); ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); + BOOST_TEST(handlers_called == expected_handlers_called); } void run_test( @@ -54,8 +54,8 @@ void run_test( auto handler = [&handlers_called, expected_ec](error_code ec, reason_code rc, puback_props) { ++handlers_called; - BOOST_CHECK(ec == expected_ec); - BOOST_CHECK_EQUAL(rc, reason_codes::empty); + BOOST_TEST(ec == expected_ec); + BOOST_TEST(rc == reason_codes::empty); }; detail::publish_send_op< @@ -64,7 +64,7 @@ void run_test( .perform(topic_name, payload, retain_e::yes, pprops); ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); + BOOST_TEST(handlers_called == expected_handlers_called); } BOOST_AUTO_TEST_CASE(invalid_topic_name_1) {