From 07b7c35002d1ad39379e479b706525c3da979b16 Mon Sep 17 00:00:00 2001 From: vsoulgard Date: Mon, 30 Mar 2026 23:11:39 +0300 Subject: [PATCH 1/2] Add some missing UT cases --- test/unit/async_mutex.cpp | 28 ++++++++++++++++++++++++ test/unit/async_sender.cpp | 42 ++++++++++++++++++++++++++++++++++++ test/unit/connect_op.cpp | 16 ++++++++++++++ test/unit/logger.cpp | 37 +++++++++++++++++++++++++++++++ test/unit/subscribe_op.cpp | 8 ++++--- test/unit/unsubscribe_op.cpp | 8 ++++--- 6 files changed, 133 insertions(+), 6 deletions(-) diff --git a/test/unit/async_mutex.cpp b/test/unit/async_mutex.cpp index b6c0082..6554220 100644 --- a/test/unit/async_mutex.cpp +++ b/test/unit/async_mutex.cpp @@ -127,6 +127,34 @@ BOOST_AUTO_TEST_CASE(per_op_cancellation) { BOOST_TEST(!mutex.is_locked()); } +BOOST_AUTO_TEST_CASE(cancel_ops_by_none_type) { + constexpr int expected_handlers_called = 4; + int handlers_called = 0; + + asio::io_context ioc; + asio::cancellation_signal cs; + + async_mutex mutex(asio::make_strand(ioc.get_executor())); + + for (int i = 0; i < expected_handlers_called; ++i) { + mutex.lock( + [&mutex, &handlers_called](error_code ec) { + ++handlers_called; + BOOST_TEST(!ec); + mutex.unlock(); + } + ); + } + + // Canceling with cancellation_type_t::none shouldn't cause cancellation + cs.emit(asio::cancellation_type_t::none); + cs.slot().clear(); + + ioc.poll(); + BOOST_TEST(handlers_called == expected_handlers_called); + BOOST_TEST(!mutex.is_locked()); +} + BOOST_AUTO_TEST_CASE(cancel_ops_by_destructor) { constexpr int expected_handlers_called = 2; int handlers_called = 0; diff --git a/test/unit/async_sender.cpp b/test/unit/async_sender.cpp index 35161f8..d48ff77 100644 --- a/test/unit/async_sender.cpp +++ b/test/unit/async_sender.cpp @@ -337,6 +337,48 @@ BOOST_FIXTURE_TEST_CASE(throttling_ordering, shared_test_data) { BOOST_TEST(broker.received_all_expected()); } +BOOST_FIXTURE_TEST_CASE(throttling_with_non_throttled_request, shared_test_data) { + constexpr int expected_handlers_called = 1; + int handlers_called = 0; + + auto publish_qos0 = encoders::encode_publish( + 0, topic, payload, qos_e::at_most_once, retain_e::no, dup_e::no, {} + ); + + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(1ms)) + .reply_with(connack_rm, after(2ms)) + .expect(publish_qos0) + .complete_with(success, after(1ms)); + + asio::io_context ioc; + auto executor = ioc.get_executor(); + auto& broker = asio::make_service( + ioc, executor, std::move(broker_side) + ); + + using client_type = mqtt_client; + client_type c(executor); + + c.brokers("127.0.0.1") + .async_run(asio::detached); + + c.async_publish( + topic, payload, retain_e::no, publish_props {}, + [&](error_code ec) { + ++handlers_called; + BOOST_TEST(!ec); + c.cancel(); + } + ); + + broker.run(ioc); + BOOST_TEST(handlers_called == expected_handlers_called); + BOOST_TEST(broker.received_all_expected()); +} + BOOST_FIXTURE_TEST_CASE(prioritize_disconnect, shared_test_data) { constexpr int expected_handlers_called = 3; int handlers_called = 0; diff --git a/test/unit/connect_op.cpp b/test/unit/connect_op.cpp index 76d1c5b..6d6b6b0 100644 --- a/test/unit/connect_op.cpp +++ b/test/unit/connect_op.cpp @@ -204,6 +204,22 @@ BOOST_FIXTURE_TEST_CASE(fail_reading_connack_payload, shared_test_data) { run_unit_test(std::move(broker_side), std::move(handler)); } +BOOST_FIXTURE_TEST_CASE(fail_reading_connack_fixed_header, shared_test_data) { + test::msg_exchange broker_side; + broker_side + .expect(connect) + .complete_with(success, after(2ms)) + // Send only 3 bytes (min packet size is 5) + .send(std::string({0x20, 0x02, 0x00}), after(30ms)) + .send(fail, after(60ms)); + + auto handler = [&](error_code ec) { + BOOST_TEST(ec == fail); + }; + + run_unit_test(std::move(broker_side), std::move(handler)); +} + BOOST_FIXTURE_TEST_CASE(receive_unexpected_auth, shared_test_data) { auth_props aprops; aprops[prop::authentication_method] = "method"; diff --git a/test/unit/logger.cpp b/test/unit/logger.cpp index 82fecfa..e8d4860 100644 --- a/test/unit/logger.cpp +++ b/test/unit/logger.cpp @@ -105,6 +105,18 @@ struct resolve_test_data { std::string(host), std::string(port) ); } + + auto few_endpoints() { + std::vector eps; + eps.emplace_back(asio::ip::make_address("127.0.0.1"), 1883); + eps.emplace_back(asio::ip::make_address("127.0.0.2"), 1883); + eps.emplace_back(asio::ip::make_address("127.0.0.3"), 1883); + + return asio::ip::tcp::resolver::results_type::create( + eps.begin(), eps.end(), + std::string(host), std::string(port) + ); + } }; BOOST_FIXTURE_TEST_CASE(at_resolve_success_warning, resolve_test_data) { @@ -157,6 +169,20 @@ BOOST_FIXTURE_TEST_CASE(at_resolve_success_debug, resolve_test_data) { test_logger_output(std::move(test_fun), expected_output); } +BOOST_FIXTURE_TEST_CASE(at_resolve_success_debug_few_eps, resolve_test_data) { + const auto expected_output = + "[Boost.MQTT5] resolve: localhost:1883 - " + + success_msg() + ". [127.0.0.1,127.0.0.2,127.0.0.3]\n" + ; + + auto test_fun = [this] { + logger l(log_level::debug); + l.at_resolve(error_code {}, host, port, few_endpoints()); + }; + + test_logger_output(std::move(test_fun), expected_output); +} + BOOST_FIXTURE_TEST_CASE(at_resolve_fail_debug, resolve_test_data) { const auto expected_output = "[Boost.MQTT5] resolve: localhost:1883 - " + host_not_found_msg() + ". []\n" @@ -412,6 +438,17 @@ BOOST_AUTO_TEST_CASE(at_transport_error_info) { test_logger_output(std::move(test_fun), expected_output); } +BOOST_AUTO_TEST_CASE(at_transport_error_warning) { + const auto expected_output = ""; + + auto test_fun = [] { + logger l(log_level::warning); + l.at_transport_error(asio::error::eof); + }; + + test_logger_output(std::move(test_fun), expected_output); +} + // Test that the mqtt_client calls logger functions as expected. BOOST_AUTO_TEST_CASE(client_disconnect) { diff --git a/test/unit/subscribe_op.cpp b/test/unit/subscribe_op.cpp index a8001d5..9d61f88 100644 --- a/test/unit/subscribe_op.cpp +++ b/test/unit/subscribe_op.cpp @@ -73,10 +73,12 @@ void run_test( BOOST_TEST(rcs[i] == reason_codes::empty); }; - detail::subscribe_op< + auto sub_op = detail::subscribe_op< client_service_type, decltype(handler) - > { svc_ptr, std::move(handler) } - .perform(topics, sprops); + > { svc_ptr, std::move(handler) }; + + BOOST_TEST(static_cast(sub_op.get_executor() == asio::any_io_executor(ioc.get_executor()))); + sub_op.perform(topics, sprops); ioc.poll(); BOOST_TEST(handlers_called == expected_handlers_called); diff --git a/test/unit/unsubscribe_op.cpp b/test/unit/unsubscribe_op.cpp index f9e2c43..8172678 100644 --- a/test/unit/unsubscribe_op.cpp +++ b/test/unit/unsubscribe_op.cpp @@ -40,10 +40,12 @@ BOOST_AUTO_TEST_CASE(pid_overrun) { BOOST_TEST(rcs[0] == reason_codes::empty); }; - detail::unsubscribe_op< + auto unsub_op = detail::unsubscribe_op< client_service_type, decltype(handler) - > { svc_ptr, std::move(handler) } - .perform({ "topic" }, unsubscribe_props {}); + > { svc_ptr, std::move(handler) }; + + BOOST_TEST(static_cast(unsub_op.get_executor() == asio::any_io_executor(ioc.get_executor()))); + unsub_op.perform({ "topic" }, unsubscribe_props {}); ioc.poll(); BOOST_TEST(handlers_called == expected_handlers_called); From 2faec316b5ea09b6f0c67a1ef3c944cc47704cde Mon Sep 17 00:00:00 2001 From: Bruno Iljazovic Date: Wed, 1 Apr 2026 10:39:47 +0200 Subject: [PATCH 2/2] expand async_sender/throttling_ordering test --- test/unit/async_sender.cpp | 107 +++++++++++++++---------------------- 1 file changed, 43 insertions(+), 64 deletions(-) diff --git a/test/unit/async_sender.cpp b/test/unit/async_sender.cpp index d48ff77..b6781e3 100644 --- a/test/unit/async_sender.cpp +++ b/test/unit/async_sender.cpp @@ -279,22 +279,34 @@ BOOST_FIXTURE_TEST_CASE(throttling, shared_test_data) { } BOOST_FIXTURE_TEST_CASE(throttling_ordering, shared_test_data) { - constexpr int expected_handlers_called = 2; + constexpr int expected_handlers_called = 4; int handlers_called = 0; // packets auto publish_qos0 = encoders::encode_publish( 0, topic, payload, qos_e::at_most_once, retain_e::no, dup_e::no, {} ); + auto publish_1_qos1 = encoders::encode_publish( + 1, topic, payload, qos_e::at_least_once, retain_e::no, dup_e::no, {} + ); + auto publish_2_qos1 = encoders::encode_publish( + 2, topic, payload, qos_e::at_least_once, retain_e::no, dup_e::no, {} + ); + + auto puback_1 = encoders::encode_puback(1, uint8_t(0x00), {}); + auto puback_2 = encoders::encode_puback(2, uint8_t(0x00), {}); test::msg_exchange broker_side; broker_side .expect(connect) .complete_with(success, after(1ms)) - .reply_with(connack, after(2ms)) - .expect(publish_qos1, publish_qos0) + .reply_with(connack_rm, after(2ms)) + .expect(publish_qos0, publish_1_qos1, publish_qos0) .complete_with(success, after(1ms)) - .reply_with(puback, after(2ms)); + .reply_with(puback_1, after(2ms)) + .expect(publish_2_qos1) + .complete_with(success, after(1ms)) + .reply_with(puback_2, after(2ms)); asio::io_context ioc; auto executor = ioc.get_executor(); @@ -307,72 +319,39 @@ BOOST_FIXTURE_TEST_CASE(throttling_ordering, shared_test_data) { c.brokers("127.0.0.1,127.0.0.1") // to avoid reconnect backoff .async_run(asio::detached); - c.async_publish( - topic, payload, retain_e::no, publish_props {}, - [&](error_code ec, reason_code rc, puback_props) { - ++handlers_called; - - BOOST_TEST(!ec); - BOOST_TEST(rc == reason_codes::success); - - if (handlers_called == expected_handlers_called) - c.cancel(); - } - ); - - c.async_publish( - topic, payload, retain_e::no, publish_props{}, - [&](error_code ec) { - ++handlers_called; - - BOOST_TEST(!ec); - - if (handlers_called == expected_handlers_called) - c.cancel(); - } - ); - - broker.run(ioc); - BOOST_TEST(handlers_called == expected_handlers_called); - BOOST_TEST(broker.received_all_expected()); -} - -BOOST_FIXTURE_TEST_CASE(throttling_with_non_throttled_request, shared_test_data) { - constexpr int expected_handlers_called = 1; - int handlers_called = 0; + auto send_qos0 = [&] { + c.async_publish( + topic, payload, retain_e::no, publish_props{}, + [&](error_code ec) { + ++handlers_called; - auto publish_qos0 = encoders::encode_publish( - 0, topic, payload, qos_e::at_most_once, retain_e::no, dup_e::no, {} - ); + BOOST_TEST(!ec); - test::msg_exchange broker_side; - broker_side - .expect(connect) - .complete_with(success, after(1ms)) - .reply_with(connack_rm, after(2ms)) - .expect(publish_qos0) - .complete_with(success, after(1ms)); + if (handlers_called == expected_handlers_called) + c.cancel(); + } + ); + }; - asio::io_context ioc; - auto executor = ioc.get_executor(); - auto& broker = asio::make_service( - ioc, executor, std::move(broker_side) - ); + auto send_qos1 = [&] { + c.async_publish( + topic, payload, retain_e::no, publish_props {}, + [&](error_code ec, reason_code rc, puback_props) { + ++handlers_called; - using client_type = mqtt_client; - client_type c(executor); + BOOST_TEST(!ec); + BOOST_TEST(rc == reason_codes::success); - c.brokers("127.0.0.1") - .async_run(asio::detached); + if (handlers_called == expected_handlers_called) + c.cancel(); + } + ); + }; - c.async_publish( - topic, payload, retain_e::no, publish_props {}, - [&](error_code ec) { - ++handlers_called; - BOOST_TEST(!ec); - c.cancel(); - } - ); + send_qos0(); + send_qos1(); + send_qos1(); + send_qos0(); broker.run(ioc); BOOST_TEST(handlers_called == expected_handlers_called);