diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70e4cf9..37c9006 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,7 +104,7 @@ jobs: - name: Run tests run: | - .\\test\\build\\${{ matrix.build-type }}\\mqtt-test.exe --log_level=test_suite + .\\test\\build\\${{ matrix.build-type }}\\mqtt-test.exe posix: name: "${{ matrix.toolset }} std=c++${{ matrix.cxxstd }} ${{ matrix.build-type }}" @@ -220,4 +220,4 @@ jobs: cmake --build test/build -j 4 - name: Run tests - run: ./test/build/mqtt-test --log_level=test_suite + run: ./test/build/mqtt-test diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 13a3809..3d4ebcd 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -8,7 +8,7 @@ env: jobs: posix: - name: "coverage ${{matrix.compiler}} -std=c++${{matrix.cxxstd}}" + name: "coverage ${{ matrix.compiler }} -std=c++${{ matrix.cxxstd }}" defaults: run: shell: bash @@ -66,7 +66,7 @@ jobs: cmake --build test/build -j 4 - name: Run tests - run: ./test/build/mqtt-test --log_level=test_suite + run: ./test/build/mqtt-test - name: Generate Coverage Report run: | diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..09793f6 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,23 @@ +codecov: + require_ci_to_pass: true + +comment: + behavior: default + layout: reach,diff,flags,tree,reach + show_carryforward_flags: false + +coverage: + precision: 2 + round: down + status: + changes: false + default_rules: + flag_coverage_not_uploaded_behavior: include + patch: + default: + informational: true + project: + default: + informational: true +github_checks: + annotations: true diff --git a/test/integration/read_message.cpp b/test/integration/read_message.cpp index c9df2b0..f87254e 100644 --- a/test/integration/read_message.cpp +++ b/test/integration/read_message.cpp @@ -1,8 +1,5 @@ #include -#include -#include - #include #include "test_common/message_exchange.hpp" @@ -13,12 +10,12 @@ using namespace async_mqtt5; BOOST_AUTO_TEST_SUITE(read_message/*, *boost::unit_test::disabled()*/) +using test::after; +using namespace std::chrono; + void test_receive_malformed_packet( std::string malformed_packet, std::string reason_string ) { - using test::after; - using std::chrono_literals::operator ""ms; - // packets auto connect = encoders::encode_connect( "", std::nullopt, std::nullopt, 10, false, {}, std::nullopt @@ -102,10 +99,16 @@ BOOST_AUTO_TEST_CASE(receive_malformed_publish) { ); } -BOOST_AUTO_TEST_CASE(receive_disconnect) { - using test::after; - using std::chrono_literals::operator ""ms; +struct shared_test_data { + error_code success {}; + const std::string connect = encoders::encode_connect( + "", std::nullopt, std::nullopt, 10, false, {}, std::nullopt + ); + const std::string connack = encoders::encode_connack(false, uint8_t(0x00), {}); +}; + +BOOST_FIXTURE_TEST_CASE(receive_disconnect, shared_test_data) { // packets auto connect = encoders::encode_connect( "", std::nullopt, std::nullopt, 10, false, {}, std::nullopt @@ -147,10 +150,7 @@ BOOST_AUTO_TEST_CASE(receive_disconnect) { } -BOOST_AUTO_TEST_CASE(receive_pingresp) { - using test::after; - using std::chrono_literals::operator ""ms; - +BOOST_FIXTURE_TEST_CASE(receive_pingresp, shared_test_data) { // packets auto connect = encoders::encode_connect( "", std::nullopt, std::nullopt, 10, false, {}, std::nullopt @@ -189,10 +189,7 @@ BOOST_AUTO_TEST_CASE(receive_pingresp) { } -BOOST_AUTO_TEST_CASE(send_byte_by_byte) { - using test::after; - using std::chrono_literals::operator ""ms; - +BOOST_FIXTURE_TEST_CASE(receive_byte_by_byte, shared_test_data) { constexpr int expected_handlers_called = 1; int handlers_called = 0; diff --git a/test/unit/connect_op.cpp b/test/unit/connect_op.cpp index 759a071..4633766 100644 --- a/test/unit/connect_op.cpp +++ b/test/unit/connect_op.cpp @@ -105,7 +105,6 @@ BOOST_FIXTURE_TEST_CASE(connection_refused, shared_test_data) { run_unit_test(std::move(broker_side), std::move(handler)); } - BOOST_FIXTURE_TEST_CASE(access_denied, shared_test_data) { auto denied_connack = encoders::encode_connack( true, reason_codes::bad_username_or_password.value(), {} @@ -137,7 +136,6 @@ BOOST_FIXTURE_TEST_CASE(fail_to_send_connect, shared_test_data) { run_unit_test(std::move(broker_side), std::move(handler)); } - BOOST_FIXTURE_TEST_CASE(receive_wrong_packet, shared_test_data) { // packets auto unexpected_packet = encoders::encode_puback(1, uint8_t(0x00), {}); diff --git a/test/unit/disconnect_op.cpp b/test/unit/disconnect_op.cpp index 3f83afd..15f4d54 100644 --- a/test/unit/disconnect_op.cpp +++ b/test/unit/disconnect_op.cpp @@ -17,49 +17,50 @@ using namespace async_mqtt5; BOOST_AUTO_TEST_SUITE(disconnect_op/*, *boost::unit_test::disabled()*/) - -BOOST_AUTO_TEST_CASE(test_malformed_packet) { - std::string malformed_str = std::string{ 0x01 }; - - disconnect_props invalid_user_props; - invalid_user_props[prop::user_property].push_back(malformed_str); - - disconnect_props invalid_reason_string; - invalid_reason_string[prop::reason_string] = malformed_str; - - std::vector testing_props = { - invalid_user_props, invalid_reason_string - }; - - int expected_handlers_called = static_cast(testing_props.size()); +void run_malformed_props_test(const disconnect_props& dprops) { + constexpr int expected_handlers_called = 1; int handlers_called = 0; asio::io_context ioc; using client_service_type = test::test_service; auto svc_ptr = std::make_shared(ioc.get_executor()); - for (const auto& props : testing_props) { - auto handler = [&handlers_called](error_code ec) { - ++handlers_called; - BOOST_CHECK(ec == client::error::malformed_packet); - }; - detail::disconnect_ctx ctx; - ctx.props = props; + auto handler = [&handlers_called](error_code ec) { + ++handlers_called; + BOOST_CHECK(ec == client::error::malformed_packet); + }; - detail::disconnect_op< - client_service_type, detail::disconnect_ctx - > { svc_ptr, std::move(ctx), std::move(handler) } - .perform(); - } + detail::disconnect_ctx ctx; + ctx.props = dprops; + + detail::disconnect_op< + client_service_type, detail::disconnect_ctx + > { svc_ptr, std::move(ctx), std::move(handler) } + .perform(); ioc.run_for(std::chrono::milliseconds(500)); BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); } -BOOST_AUTO_TEST_CASE(test_omitting_props) { + +BOOST_AUTO_TEST_CASE(malformed_reason_string) { + disconnect_props dprops; + dprops[prop::reason_string] = std::string { 0x01 }; + + run_malformed_props_test(dprops); +} + +BOOST_AUTO_TEST_CASE(malformed_user_property) { + disconnect_props dprops; + dprops[prop::user_property].push_back(std::string { 0x01 }); + + run_malformed_props_test(dprops); +} + +BOOST_AUTO_TEST_CASE(omit_props) { using test::after; - using std::chrono_literals::operator ""ms; + using namespace std::chrono; constexpr int expected_handlers_called = 1; int handlers_called = 0; @@ -106,18 +107,18 @@ BOOST_AUTO_TEST_CASE(test_omitting_props) { .async_run(asio::detached); asio::steady_timer timer(c.get_executor()); - timer.expires_after(std::chrono::milliseconds(200)); + timer.expires_after(std::chrono::milliseconds(50)); timer.async_wait([&](auto) { c.async_disconnect( disconnect_rc_e::normal_disconnection, props, - [&](error_code ec) { + [&handlers_called](error_code ec) { handlers_called++; BOOST_CHECK(!ec); } ); }); - ioc.run_for(std::chrono::seconds(5)); + ioc.run_for(std::chrono::seconds(2)); BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); BOOST_CHECK(broker.received_all_expected()); } diff --git a/test/unit/publish_send_op.cpp b/test/unit/publish_send_op.cpp index bf2c9c2..e65d5ee 100644 --- a/test/unit/publish_send_op.cpp +++ b/test/unit/publish_send_op.cpp @@ -14,8 +14,7 @@ using namespace async_mqtt5; BOOST_AUTO_TEST_SUITE(publish_send_op/*, *boost::unit_test::disabled()*/) - -BOOST_AUTO_TEST_CASE(test_pid_overrun) { +BOOST_AUTO_TEST_CASE(pid_overrun) { constexpr int expected_handlers_called = 1; int handlers_called = 0; @@ -40,228 +39,167 @@ BOOST_AUTO_TEST_CASE(test_pid_overrun) { BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); } -BOOST_AUTO_TEST_CASE(test_invalid_topic_names) { - std::vector invalid_topics = { - "", "+", "#", - "invalid+", "invalid#", "invalid/#", "invalid/+" - }; - int expected_handlers_called = static_cast(invalid_topics.size()); +void run_test( + error_code expected_ec, + const std::string& topic_name, const std::string& payload = {}, + const publish_props& pprops = {}, const connack_props& cprops = {} +) { + constexpr int expected_handlers_called = 1; int handlers_called = 0; asio::io_context ioc; using client_service_type = test::test_service; - auto svc_ptr = std::make_shared(ioc.get_executor()); + auto svc_ptr = std::make_shared(ioc.get_executor(), cprops); - for (const auto& topic: invalid_topics) { - auto handler = [&handlers_called](error_code ec) { - ++handlers_called; - BOOST_CHECK(ec == client::error::invalid_topic); - }; + auto handler = [&handlers_called, expected_ec](error_code ec, reason_code rc, puback_props) { + ++handlers_called; - detail::publish_send_op< - client_service_type, decltype(handler), qos_e::at_most_once - > { svc_ptr, std::move(handler) } - .perform(topic, "payload", retain_e::no, {}); - } + BOOST_CHECK(ec == expected_ec); + BOOST_CHECK_EQUAL(rc, reason_codes::empty); + }; + + detail::publish_send_op< + client_service_type, decltype(handler), qos_e::at_least_once + > { svc_ptr, std::move(handler) } + .perform(topic_name, payload, retain_e::yes, pprops); ioc.run_for(std::chrono::milliseconds(500)); BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); } -BOOST_AUTO_TEST_CASE(test_malformed_packet) { - uint16_t topic_alias_max = 10; - std::string malformed_str = std::string { 0x01 }; +BOOST_AUTO_TEST_CASE(invalid_topic_name_1) { + run_test(client::error::invalid_topic, ""); +} +BOOST_AUTO_TEST_CASE(invalid_topic_name_2) { + run_test(client::error::invalid_topic, "+"); +} + +BOOST_AUTO_TEST_CASE(invalid_topic_name_3) { + run_test(client::error::invalid_topic, "#"); +} + +BOOST_AUTO_TEST_CASE(invalid_topic_name_4) { + run_test(client::error::invalid_topic, "invalid+"); +} + +BOOST_AUTO_TEST_CASE(invalid_topic_name_5) { + run_test(client::error::invalid_topic, "invalid#"); +} + +BOOST_AUTO_TEST_CASE(invalid_topic_name_6) { + run_test(client::error::invalid_topic, "invalid/#"); +} + +BOOST_AUTO_TEST_CASE(invalid_topic_name_7) { + run_test(client::error::invalid_topic, "invalid/+"); +} + +void run_malformed_props_test( + const publish_props& pprops, const connack_props& cprops = {} +) { + return run_test(client::error::malformed_packet, "topic", "payload", pprops, cprops); +} + +BOOST_AUTO_TEST_CASE(malformed_props_1) { + publish_props pprops; + pprops[prop::response_topic] = "response#topic"; + + run_malformed_props_test(pprops); +} + +BOOST_AUTO_TEST_CASE(malformed_props_2) { + publish_props pprops; + pprops[prop::user_property].push_back(std::string { 0x01 }); + + run_malformed_props_test(pprops); +} + +BOOST_AUTO_TEST_CASE(malformed_props_3) { + publish_props pprops; + pprops[prop::content_type] = std::string { 0x01 }; + + run_malformed_props_test(pprops); +} + +BOOST_AUTO_TEST_CASE(malformed_props_4) { connack_props cprops; - cprops[prop::topic_alias_maximum] = topic_alias_max; + cprops[prop::topic_alias_maximum] = uint16_t(10); - publish_props malfored_response_topic_props; - malfored_response_topic_props[prop::response_topic] = "response#topic"; + publish_props pprops; + pprops[prop::topic_alias] = uint16_t(0); - publish_props utf8_payload_props; - utf8_payload_props[prop::payload_format_indicator] = uint8_t(1); - - publish_props invalid_user_props; - invalid_user_props[prop::user_property].push_back(malformed_str); - - publish_props malformed_content_type_props; - malformed_content_type_props[prop::content_type] = malformed_str; - - publish_props zero_topic_alias_props; - zero_topic_alias_props[prop::topic_alias] = uint16_t(0); - - publish_props out_of_range_subid_props; - out_of_range_subid_props[prop::subscription_identifier] = 300'000'000; - - - std::vector testing_props = { - malfored_response_topic_props, utf8_payload_props, - invalid_user_props, malformed_content_type_props, - zero_topic_alias_props, out_of_range_subid_props - }; - - int expected_handlers_called = static_cast(testing_props.size()); - int handlers_called = 0; - - asio::io_context ioc; - using client_service_type = test::test_service; - auto svc_ptr = std::make_shared(ioc.get_executor(), std::move(cprops)); - BOOST_ASSERT(svc_ptr->connack_property(prop::topic_alias_maximum) == topic_alias_max); - - for (const auto& props: testing_props) { - auto handler = [&handlers_called](error_code ec) { - ++handlers_called; - BOOST_CHECK(ec == client::error::malformed_packet); - }; - - detail::publish_send_op< - client_service_type, decltype(handler), qos_e::at_most_once - > { svc_ptr, std::move(handler) } - .perform("topic", malformed_str, retain_e::no, props); - } - - ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); + run_malformed_props_test(pprops, cprops); } -BOOST_AUTO_TEST_CASE(test_packet_too_large) { - int max_packet_sz = 10; +BOOST_AUTO_TEST_CASE(malformed_props_5) { + publish_props pprops; + pprops[prop::subscription_identifier] = 300'000'000; - connack_props props; - props[prop::maximum_packet_size] = max_packet_sz; - - constexpr int expected_handlers_called = 1; - int handlers_called = 0; - - asio::io_context ioc; - using client_service_type = test::test_service; - auto svc_ptr = std::make_shared( - ioc.get_executor(), std::move(props) - ); - BOOST_ASSERT(svc_ptr->connack_property(prop::maximum_packet_size) == max_packet_sz); - - auto handler = [&handlers_called](error_code ec, reason_code rc, puback_props) { - ++handlers_called; - BOOST_CHECK(ec == client::error::packet_too_large); - BOOST_CHECK_EQUAL(rc, reason_codes::empty); - }; - - detail::publish_send_op< - client_service_type, decltype(handler), qos_e::at_least_once - > { svc_ptr, std::move(handler) } - .perform( - "test", "payload", retain_e::no, {} - ); - - ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); + run_malformed_props_test(pprops); } -BOOST_AUTO_TEST_CASE(test_qos_not_supported) { - uint8_t max_qos = 0; +BOOST_AUTO_TEST_CASE(malformed_utf8_payload) { + publish_props pprops; + pprops[prop::payload_format_indicator] = uint8_t(1); - connack_props props; - props[prop::maximum_qos] = max_qos; - - constexpr int expected_handlers_called = 1; - int handlers_called = 0; - - asio::io_context ioc; - using client_service_type = test::test_service; - auto svc_ptr = std::make_shared( - ioc.get_executor(), std::move(props) - ); - BOOST_ASSERT(svc_ptr->connack_property(prop::maximum_qos) == max_qos); - - auto handler = [&handlers_called](error_code ec, reason_code rc, puback_props) { - ++handlers_called; - BOOST_CHECK(ec == client::error::qos_not_supported); - BOOST_CHECK_EQUAL(rc, reason_codes::empty); - }; - - detail::publish_send_op< - client_service_type, decltype(handler), qos_e::at_least_once - > { svc_ptr, std::move(handler) } - .perform( - "test", "payload", retain_e::no, {} - ); - - ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); + run_test(client::error::malformed_packet, "topic", std::string { 0x01 }, pprops); } -BOOST_AUTO_TEST_CASE(test_retain_not_available) { - uint8_t retain = 0; +BOOST_AUTO_TEST_CASE(packet_too_large) { + connack_props cprops; + cprops[prop::maximum_packet_size] = 10; - connack_props props; - props[prop::retain_available] = retain; - - constexpr int expected_handlers_called = 1; - int handlers_called = 0; - - asio::io_context ioc; - using client_service_type = test::test_service; - auto svc_ptr = std::make_shared( - ioc.get_executor(), std::move(props) + run_test( + client::error::packet_too_large, + "very large topic", "very large payload", + publish_props {}, cprops ); - BOOST_ASSERT(svc_ptr->connack_property(prop::retain_available) == retain); - - auto handler = [&handlers_called](error_code ec, reason_code rc, puback_props) { - ++handlers_called; - BOOST_CHECK(ec == client::error::retain_not_available); - BOOST_CHECK_EQUAL(rc, reason_codes::empty); - }; - - detail::publish_send_op< - client_service_type, decltype(handler), qos_e::at_least_once - > { svc_ptr, std::move(handler) } - .perform( - "test", "payload", retain_e::yes, {} - ); - - ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); } -BOOST_AUTO_TEST_CASE(test_topic_alias_maximum) { - uint16_t max_topic_alias = 10; +BOOST_AUTO_TEST_CASE(qos_not_supported) { + connack_props cprops; + cprops[prop::maximum_qos] = uint8_t(0); - connack_props ta_allowed_props; - ta_allowed_props[prop::topic_alias_maximum] = max_topic_alias; + run_test( + client::error::qos_not_supported, + "topic", "payload", publish_props {}, cprops + ); +} - std::vector test_props = { - ta_allowed_props, connack_props {} /* not allowed */ - }; +BOOST_AUTO_TEST_CASE(retain_not_available) { + connack_props cprops; + cprops[prop::retain_available] = uint8_t(0); - int expected_handlers_called = static_cast(test_props.size()); - int handlers_called = 0; + run_test( + client::error::retain_not_available, + "topic", "payload", publish_props {}, cprops + ); +} - asio::io_context ioc; +BOOST_AUTO_TEST_CASE(topic_alias_maximum_reached_1) { + connack_props cprops; + cprops[prop::topic_alias_maximum] = uint16_t(10); - for (const auto& ca_props: test_props) { - using client_service_type = test::test_service; - auto svc_ptr = std::make_shared( - ioc.get_executor(), ca_props - ); + publish_props pprops; + pprops[prop::topic_alias] = uint16_t(12); - auto handler = [&handlers_called](error_code ec) { - ++handlers_called; - BOOST_CHECK(ec == client::error::topic_alias_maximum_reached); - }; + run_test( + client::error::topic_alias_maximum_reached, + "topic", "payload", pprops, cprops + ); +} - publish_props props; - props[prop::topic_alias] = uint16_t(12); +BOOST_AUTO_TEST_CASE(topic_alias_maximum_reached_2) { + connack_props cprops; /* not allowed */ - detail::publish_send_op< - client_service_type, decltype(handler), qos_e::at_most_once - > { svc_ptr, std::move(handler) } - .perform( - "test", "payload", retain_e::yes, props - ); - } + publish_props pprops; + pprops[prop::topic_alias] = uint16_t(12); - ioc.run_for(std::chrono::milliseconds(500)); - BOOST_CHECK_EQUAL(handlers_called, expected_handlers_called); + run_test( + client::error::topic_alias_maximum_reached, + "topic", "payload", pprops, cprops + ); } BOOST_AUTO_TEST_SUITE_END()