From 30c94deb6a6013b18b9a515d858d6e38a2f2fabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Karakaya?= Date: Sat, 6 Jun 2026 18:33:04 +0300 Subject: [PATCH 1/7] feat(ci): set up GitHub Actions CI workflow and fix python test paths --- .github/workflows/ci.yml | 60 ++++++++++++++++++++++++++++++++ generator/test/test_generator.py | 6 ++-- generator/test/test_parser.py | 6 ++-- 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a9ba03a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build-and-test: + name: Build & Test + runs-on: ubuntu-24.04 + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install System Dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + cmake \ + git \ + python3 \ + python3-pip \ + python3-venv \ + libgrpc++-dev \ + libprotobuf-dev \ + protobuf-compiler \ + protobuf-compiler-grpc + + - name: Set up Python Virtual Environment + run: | + python3 -m venv venv + source venv/bin/activate + pip install -r generator/requirements.txt + + - name: Run Generator Parser Tests + run: | + source venv/bin/activate + python3 generator/test/test_parser.py + + - name: Run Generator Dialect & Protoc Validation Tests + run: | + source venv/bin/activate + python3 generator/test/test_generator.py + + - name: Verify C++ Build (Dialect: common) + run: | + cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DMAVLINK_DIALECT=common + cmake --build build -j$(nproc) + + - name: Verify C++ Build (Dialect: minimal) + run: | + rm -rf build + cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DMAVLINK_DIALECT=minimal + cmake --build build -j$(nproc) diff --git a/generator/test/test_generator.py b/generator/test/test_generator.py index 28e55ed..0339d8c 100644 --- a/generator/test/test_generator.py +++ b/generator/test/test_generator.py @@ -37,7 +37,7 @@ def parse_all_dialects(parser: MAVLinkParser, dialect_names: list) -> dict: for name in dialect_names: try: - dialect = parser.parse_file(f"{name}.xml") + dialect = parser.get_flattened_dialect(name) dialects[name] = dialect progress.update(task, advance=1) except Exception as e: @@ -132,7 +132,9 @@ def main(): console.print(Panel.fit("Core MAVLink Dialects Test (minimal, standard, common)", style="bold blue")) # Paths - xml_dir = Path(__file__).parent.parent.parent / "mavlink" / "message_definitions" / "v1.0" + xml_dir = Path(__file__).parent.parent.parent / "third_party" / "mavlink" / "message_definitions" / "v1.0" + if not xml_dir.exists(): + xml_dir = Path(__file__).parent.parent.parent / "mavlink" / "message_definitions" / "v1.0" template_dir = Path(__file__).parent.parent / "templates" output_dir = Path(__file__).parent.parent.parent / "proto" diff --git a/generator/test/test_parser.py b/generator/test/test_parser.py index 4e97b1a..5fad924 100644 --- a/generator/test/test_parser.py +++ b/generator/test/test_parser.py @@ -18,8 +18,10 @@ def main(): """Test the parser with real MAVLink XML files.""" - # Path to MAVLink definitions (go up to project root, then to mavlink) - xml_dir = Path(__file__).parent.parent.parent / "mavlink" / "message_definitions" / "v1.0" + # Path to MAVLink definitions (go up to project root, then to third_party/mavlink or mavlink) + xml_dir = Path(__file__).parent.parent.parent / "third_party" / "mavlink" / "message_definitions" / "v1.0" + if not xml_dir.exists(): + xml_dir = Path(__file__).parent.parent.parent / "mavlink" / "message_definitions" / "v1.0" if not xml_dir.exists(): console.print(f"[red]Error: MAVLink definitions not found at {xml_dir}[/red]") From 6afbc85d13d7930e05729ef8ad4765b48f69e02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Karakaya?= Date: Sat, 6 Jun 2026 19:00:49 +0300 Subject: [PATCH 2/7] test(cpp): implement C++ unit tests for MessageConverter and Router with ctest verification --- .github/workflows/ci.yml | 6 +- CMakeLists.txt | 3 + bridge/CMakeLists.txt | 52 +++++++++++++- bridge/test/test_converter.cc | 88 +++++++++++++++++++++++ bridge/test/test_router.cc | 131 ++++++++++++++++++++++++++++++++++ bridge/test/test_runner.cc | 19 +++++ 6 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 bridge/test/test_converter.cc create mode 100644 bridge/test/test_router.cc create mode 100644 bridge/test/test_runner.cc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9ba03a..d7e7e97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,13 +48,15 @@ jobs: source venv/bin/activate python3 generator/test/test_generator.py - - name: Verify C++ Build (Dialect: common) + - name: Verify C++ Build & Run Tests (Dialect: common) run: | cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DMAVLINK_DIALECT=common cmake --build build -j$(nproc) + cd build && ctest --output-on-failure - - name: Verify C++ Build (Dialect: minimal) + - name: Verify C++ Build & Run Tests (Dialect: minimal) run: | rm -rf build cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DMAVLINK_DIALECT=minimal cmake --build build -j$(nproc) + cd build && ctest --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f2e24a..953f976 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,5 +17,8 @@ if(NOT GEN_RESULT EQUAL 0) message(FATAL_ERROR "MAVLink to Protobuf generation failed!") endif() +# Enable CTest suite +enable_testing() + # Add bridge module subdirectory add_subdirectory(bridge) diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index 1b4b464..c3a0a0f 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -134,4 +134,54 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_options(${PROJECT_NAME} PRIVATE -O0 -g) else() target_compile_options(${PROJECT_NAME} PRIVATE -O3) -endif() \ No newline at end of file +endif() + +# ============================================================================ +# C++ Testing Target +# ============================================================================ +enable_testing() + +add_executable(test_runner + test/test_runner.cc + test/test_router.cc + test/test_converter.cc + src/service/Router.cc + src/service/Logger.cc + src/mavlink/MessageConverter.cc + $ +) + +target_include_directories(test_runner PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${GENERATED_PROTOBUF_PATH} + ${CMAKE_CURRENT_BINARY_DIR}/generated +) + +target_include_directories(test_runner SYSTEM PRIVATE + ${LIBMAV_INCLUDE_DIR} +) + +add_dependencies(test_runner proto_lib) + +target_link_libraries(test_runner PRIVATE + ${Protobuf_LIBRARIES} + ${GRPC_LIBRARIES} + ${GRPCPP_LIBRARIES} + Threads::Threads +) + +set_target_properties(test_runner PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON +) + +target_compile_definitions(test_runner PRIVATE MAVLINK_DIALECT="${MAVLINK_DIALECT}") +target_compile_options(test_runner PRIVATE -Wall -Wextra -Werror) + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(test_runner PRIVATE -O0 -g) +else() + target_compile_options(test_runner PRIVATE -O3) +endif() + +add_test(NAME bridge_tests COMMAND test_runner) \ No newline at end of file diff --git a/bridge/test/test_converter.cc b/bridge/test/test_converter.cc new file mode 100644 index 0000000..37f04d1 --- /dev/null +++ b/bridge/test/test_converter.cc @@ -0,0 +1,88 @@ +#include "mavlink/MessageConverter.h" +#include +#include +#include +#include + +using namespace mavlink2grpc; + +void test_message_converter_heartbeat() { + std::cout << "Running test_message_converter_heartbeat..." << std::endl; + + // Locate the message definition file + std::string xml_path = ""; + std::vector paths = { + "third_party/mavlink/message_definitions/v1.0/common.xml", + "../third_party/mavlink/message_definitions/v1.0/common.xml", + "mavlink/message_definitions/v1.0/common.xml", + "../mavlink/message_definitions/v1.0/common.xml" + }; + + for (const auto& p : paths) { + if (access(p.c_str(), F_OK) != -1) { + xml_path = p; + break; + } + } + + if (xml_path.empty()) { + std::cerr << "Warning: Could not find common.xml definition. Skipping converter tests." << std::endl; + return; + } + + // Load MessageSet + mav::MessageSet message_set(xml_path); + + // 1. Create a mav::Message for HEARTBEAT + mav::Message mav_msg = message_set.create("HEARTBEAT"); + assert(mav_msg.name() == "HEARTBEAT"); + assert(mav_msg.id() == 0); + + // Set header partner ID (represents system ID and component ID) + // libmav sets source system/component via setFromConnectionPartner + // Let's set some fields on the message + mav_msg.set("type", static_cast(2)); // MAV_TYPE_QUADROTOR + mav_msg.set("autopilot", static_cast(3)); // MAV_AUTOPILOT_ARDUPILOTMEGA + mav_msg.set("base_mode", static_cast(81)); + mav_msg.set("custom_mode", static_cast(12345)); + mav_msg.set("system_status", static_cast(3)); // MAV_STATE_STANDBY + mav_msg.set("mavlink_version", static_cast(3)); + + // 2. Convert to Protobuf + // We can pass system_id = 42 and component_id = 191 by simulating a connection partner if needed, + // but let's check what default to_proto does or if it gets them from the message source. + auto proto_opt = MessageConverter::to_proto(mav_msg, 123456789ULL); + assert(proto_opt.has_value()); + + auto proto_msg = proto_opt.value(); + assert(proto_msg.message_id() == 0); + assert(proto_msg.timestamp_usec() == 123456789ULL); + assert(proto_msg.has_heartbeat()); + + const auto& heartbeat = proto_msg.heartbeat(); + assert(heartbeat.type() == 2); + assert(heartbeat.autopilot() == 3); + assert(heartbeat.base_mode() == 81); + assert(heartbeat.custom_mode() == 12345); + assert(heartbeat.system_status() == 3); + + // 3. Convert back from Protobuf to mav::Message + auto back_opt = MessageConverter::from_proto(proto_msg, message_set); + assert(back_opt.has_value()); + + auto back_msg = back_opt.value(); + assert(back_msg.name() == "HEARTBEAT"); + assert(back_msg.id() == 0); + + assert(back_msg.get("type") == 2); + assert(back_msg.get("autopilot") == 3); + assert(back_msg.get("base_mode") == 81); + assert(back_msg.get("custom_mode") == 12345); + assert(back_msg.get("system_status") == 3); + + std::cout << "test_message_converter_heartbeat passed!" << std::endl; +} + +void run_converter_tests() { + test_message_converter_heartbeat(); +} diff --git a/bridge/test/test_router.cc b/bridge/test/test_router.cc new file mode 100644 index 0000000..848619c --- /dev/null +++ b/bridge/test/test_router.cc @@ -0,0 +1,131 @@ +#include "service/Router.h" +#include +#include + +using namespace mavlink2grpc; + +void test_router_basic() { + std::cout << "Running test_router_basic..." << std::endl; + Router router; + + assert(router.subscription_count() == 0); + + // Subscribe client 1 with no filters (system_id=0, component_id=0) + mavlink::StreamFilter filter1; + filter1.set_system_id(0); + filter1.set_component_id(0); + + size_t client1_received = 0; + uint64_t sub1 = router.subscribe(filter1, [&](const mavlink::MavlinkMessage&) { + client1_received++; + return true; + }); + + assert(sub1 == 1); + assert(router.subscription_count() == 1); + + // Send a message + mavlink::MavlinkMessage msg1; + msg1.set_system_id(1); + msg1.set_component_id(1); + msg1.set_message_id(0); // HEARTBEAT + + size_t routed = router.route_message(msg1); + assert(routed == 1); + assert(client1_received == 1); + + // Unsubscribe client 1 + bool unsub_ok = router.unsubscribe(sub1); + assert(unsub_ok); + assert(router.subscription_count() == 0); + + routed = router.route_message(msg1); + assert(routed == 0); + assert(client1_received == 1); // should remain 1 + + std::cout << "test_router_basic passed!" << std::endl; +} + +void test_router_filters() { + std::cout << "Running test_router_filters..." << std::endl; + Router router; + + // Filter for system 1, component 1, message 30 (ATTITUDE) only + mavlink::StreamFilter filter; + filter.set_system_id(1); + filter.set_component_id(1); + filter.add_message_ids(30); + + size_t received = 0; + router.subscribe(filter, [&](const mavlink::MavlinkMessage&) { + received++; + return true; + }); + + // Message 1: Matches all + mavlink::MavlinkMessage msg1; + msg1.set_system_id(1); + msg1.set_component_id(1); + msg1.set_message_id(30); + router.route_message(msg1); + assert(received == 1); + + // Message 2: Different system ID (2) + mavlink::MavlinkMessage msg2; + msg2.set_system_id(2); + msg2.set_component_id(1); + msg2.set_message_id(30); + router.route_message(msg2); + assert(received == 1); // shouldn't increase + + // Message 3: Different component ID (2) + mavlink::MavlinkMessage msg3; + msg3.set_system_id(1); + msg3.set_component_id(2); + msg3.set_message_id(30); + router.route_message(msg3); + assert(received == 1); // shouldn't increase + + // Message 4: Different message ID (0 - HEARTBEAT) + mavlink::MavlinkMessage msg4; + msg4.set_system_id(1); + msg4.set_component_id(1); + msg4.set_message_id(0); + router.route_message(msg4); + assert(received == 1); // shouldn't increase + + std::cout << "test_router_filters passed!" << std::endl; +} + +void test_router_cleanup() { + std::cout << "Running test_router_cleanup..." << std::endl; + Router router; + + mavlink::StreamFilter filter; + + // Client whose write fails (simulates disconnected client) + router.subscribe(filter, [](const mavlink::MavlinkMessage&) { + return false; // write failed + }); + + assert(router.subscription_count() == 1); + + // Route a message - this will trigger write failure and mark inactive + mavlink::MavlinkMessage msg; + size_t routed = router.route_message(msg); + + assert(routed == 0); + assert(router.subscription_count() == 0); // Active subscription count should drop to 0 + + // Cleanup should remove it from internal list + size_t cleaned = router.cleanup_inactive(); + assert(cleaned == 1); + + std::cout << "test_router_cleanup passed!" << std::endl; +} + +void run_router_tests() { + test_router_basic(); + test_router_filters(); + test_router_cleanup(); +} diff --git a/bridge/test/test_runner.cc b/bridge/test/test_runner.cc new file mode 100644 index 0000000..d007b78 --- /dev/null +++ b/bridge/test/test_runner.cc @@ -0,0 +1,19 @@ +#include + +extern void run_router_tests(); +extern void run_converter_tests(); + +int main() { + std::cout << "========================================" << std::endl; + std::cout << "Starting C++ Unit Tests" << std::endl; + std::cout << "========================================" << std::endl; + + run_router_tests(); + run_converter_tests(); + + std::cout << "========================================" << std::endl; + std::cout << "All C++ Unit Tests Passed Successfully!" << std::endl; + std::cout << "========================================" << std::endl; + + return 0; +} From a20cdb57eb6388c0e42a0b4005897864f7c8fc98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Karakaya?= Date: Sat, 6 Jun 2026 19:03:08 +0300 Subject: [PATCH 3/7] fix(ci): quote workflow step names containing colons to prevent YAML syntax error --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7e7e97..39d5071 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,13 +48,13 @@ jobs: source venv/bin/activate python3 generator/test/test_generator.py - - name: Verify C++ Build & Run Tests (Dialect: common) + - name: "Verify C++ Build & Run Tests (Dialect: common)" run: | cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DMAVLINK_DIALECT=common cmake --build build -j$(nproc) cd build && ctest --output-on-failure - - name: Verify C++ Build & Run Tests (Dialect: minimal) + - name: "Verify C++ Build & Run Tests (Dialect: minimal)" run: | rm -rf build cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DMAVLINK_DIALECT=minimal From f38d6dcadf6249cce535abf890d9af77af66a20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Karakaya?= Date: Sat, 6 Jun 2026 19:08:10 +0300 Subject: [PATCH 4/7] ci: trigger workflow on push to any branch --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39d5071..43f62a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [ main ] + branches: [ '**' ] pull_request: branches: [ main ] From 563e9d1af0b3f77871350f13e09fd505d645b018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Karakaya?= Date: Sat, 6 Jun 2026 19:09:48 +0300 Subject: [PATCH 5/7] ci: add python virtual environment bin path to GITHUB_PATH to resolve pydantic during cmake configuration --- .github/workflows/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 43f62a5..7e77f05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,17 +35,15 @@ jobs: - name: Set up Python Virtual Environment run: | python3 -m venv venv - source venv/bin/activate - pip install -r generator/requirements.txt + ./venv/bin/pip install -r generator/requirements.txt + echo "$GITHUB_WORKSPACE/venv/bin" >> $GITHUB_PATH - name: Run Generator Parser Tests run: | - source venv/bin/activate python3 generator/test/test_parser.py - name: Run Generator Dialect & Protoc Validation Tests run: | - source venv/bin/activate python3 generator/test/test_generator.py - name: "Verify C++ Build & Run Tests (Dialect: common)" From f623cc8c001b3c65372d55144ec23d382968e131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Karakaya?= Date: Sun, 7 Jun 2026 17:51:22 +0300 Subject: [PATCH 6/7] fix(test): use custom TEST_ASSERT macro to prevent unused variable compile errors in Release build mode --- bridge/test/test_converter.cc | 50 ++++++++++++++++++++--------------- bridge/test/test_router.cc | 44 +++++++++++++++++------------- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/bridge/test/test_converter.cc b/bridge/test/test_converter.cc index 37f04d1..12eed80 100644 --- a/bridge/test/test_converter.cc +++ b/bridge/test/test_converter.cc @@ -1,9 +1,17 @@ #include "mavlink/MessageConverter.h" #include #include -#include +#include #include +#define TEST_ASSERT(cond) \ + do { \ + if (!(cond)) { \ + std::cerr << "Assertion failed: " #cond " at " << __FILE__ << ":" << __LINE__ << std::endl; \ + std::abort(); \ + } \ + } while (0) + using namespace mavlink2grpc; void test_message_converter_heartbeat() { @@ -35,8 +43,8 @@ void test_message_converter_heartbeat() { // 1. Create a mav::Message for HEARTBEAT mav::Message mav_msg = message_set.create("HEARTBEAT"); - assert(mav_msg.name() == "HEARTBEAT"); - assert(mav_msg.id() == 0); + TEST_ASSERT(mav_msg.name() == "HEARTBEAT"); + TEST_ASSERT(mav_msg.id() == 0); // Set header partner ID (represents system ID and component ID) // libmav sets source system/component via setFromConnectionPartner @@ -52,33 +60,33 @@ void test_message_converter_heartbeat() { // We can pass system_id = 42 and component_id = 191 by simulating a connection partner if needed, // but let's check what default to_proto does or if it gets them from the message source. auto proto_opt = MessageConverter::to_proto(mav_msg, 123456789ULL); - assert(proto_opt.has_value()); + TEST_ASSERT(proto_opt.has_value()); auto proto_msg = proto_opt.value(); - assert(proto_msg.message_id() == 0); - assert(proto_msg.timestamp_usec() == 123456789ULL); - assert(proto_msg.has_heartbeat()); + TEST_ASSERT(proto_msg.message_id() == 0); + TEST_ASSERT(proto_msg.timestamp_usec() == 123456789ULL); + TEST_ASSERT(proto_msg.has_heartbeat()); const auto& heartbeat = proto_msg.heartbeat(); - assert(heartbeat.type() == 2); - assert(heartbeat.autopilot() == 3); - assert(heartbeat.base_mode() == 81); - assert(heartbeat.custom_mode() == 12345); - assert(heartbeat.system_status() == 3); + TEST_ASSERT(heartbeat.type() == 2); + TEST_ASSERT(heartbeat.autopilot() == 3); + TEST_ASSERT(heartbeat.base_mode() == 81); + TEST_ASSERT(heartbeat.custom_mode() == 12345); + TEST_ASSERT(heartbeat.system_status() == 3); // 3. Convert back from Protobuf to mav::Message auto back_opt = MessageConverter::from_proto(proto_msg, message_set); - assert(back_opt.has_value()); + TEST_ASSERT(back_opt.has_value()); auto back_msg = back_opt.value(); - assert(back_msg.name() == "HEARTBEAT"); - assert(back_msg.id() == 0); - - assert(back_msg.get("type") == 2); - assert(back_msg.get("autopilot") == 3); - assert(back_msg.get("base_mode") == 81); - assert(back_msg.get("custom_mode") == 12345); - assert(back_msg.get("system_status") == 3); + TEST_ASSERT(back_msg.name() == "HEARTBEAT"); + TEST_ASSERT(back_msg.id() == 0); + + TEST_ASSERT(back_msg.get("type") == 2); + TEST_ASSERT(back_msg.get("autopilot") == 3); + TEST_ASSERT(back_msg.get("base_mode") == 81); + TEST_ASSERT(back_msg.get("custom_mode") == 12345); + TEST_ASSERT(back_msg.get("system_status") == 3); std::cout << "test_message_converter_heartbeat passed!" << std::endl; } diff --git a/bridge/test/test_router.cc b/bridge/test/test_router.cc index 848619c..678170c 100644 --- a/bridge/test/test_router.cc +++ b/bridge/test/test_router.cc @@ -1,6 +1,14 @@ #include "service/Router.h" #include -#include +#include + +#define TEST_ASSERT(cond) \ + do { \ + if (!(cond)) { \ + std::cerr << "Assertion failed: " #cond " at " << __FILE__ << ":" << __LINE__ << std::endl; \ + std::abort(); \ + } \ + } while (0) using namespace mavlink2grpc; @@ -8,7 +16,7 @@ void test_router_basic() { std::cout << "Running test_router_basic..." << std::endl; Router router; - assert(router.subscription_count() == 0); + TEST_ASSERT(router.subscription_count() == 0); // Subscribe client 1 with no filters (system_id=0, component_id=0) mavlink::StreamFilter filter1; @@ -21,8 +29,8 @@ void test_router_basic() { return true; }); - assert(sub1 == 1); - assert(router.subscription_count() == 1); + TEST_ASSERT(sub1 == 1); + TEST_ASSERT(router.subscription_count() == 1); // Send a message mavlink::MavlinkMessage msg1; @@ -31,17 +39,17 @@ void test_router_basic() { msg1.set_message_id(0); // HEARTBEAT size_t routed = router.route_message(msg1); - assert(routed == 1); - assert(client1_received == 1); + TEST_ASSERT(routed == 1); + TEST_ASSERT(client1_received == 1); // Unsubscribe client 1 bool unsub_ok = router.unsubscribe(sub1); - assert(unsub_ok); - assert(router.subscription_count() == 0); + TEST_ASSERT(unsub_ok); + TEST_ASSERT(router.subscription_count() == 0); routed = router.route_message(msg1); - assert(routed == 0); - assert(client1_received == 1); // should remain 1 + TEST_ASSERT(routed == 0); + TEST_ASSERT(client1_received == 1); // should remain 1 std::cout << "test_router_basic passed!" << std::endl; } @@ -68,7 +76,7 @@ void test_router_filters() { msg1.set_component_id(1); msg1.set_message_id(30); router.route_message(msg1); - assert(received == 1); + TEST_ASSERT(received == 1); // Message 2: Different system ID (2) mavlink::MavlinkMessage msg2; @@ -76,7 +84,7 @@ void test_router_filters() { msg2.set_component_id(1); msg2.set_message_id(30); router.route_message(msg2); - assert(received == 1); // shouldn't increase + TEST_ASSERT(received == 1); // shouldn't increase // Message 3: Different component ID (2) mavlink::MavlinkMessage msg3; @@ -84,7 +92,7 @@ void test_router_filters() { msg3.set_component_id(2); msg3.set_message_id(30); router.route_message(msg3); - assert(received == 1); // shouldn't increase + TEST_ASSERT(received == 1); // shouldn't increase // Message 4: Different message ID (0 - HEARTBEAT) mavlink::MavlinkMessage msg4; @@ -92,7 +100,7 @@ void test_router_filters() { msg4.set_component_id(1); msg4.set_message_id(0); router.route_message(msg4); - assert(received == 1); // shouldn't increase + TEST_ASSERT(received == 1); // shouldn't increase std::cout << "test_router_filters passed!" << std::endl; } @@ -108,18 +116,18 @@ void test_router_cleanup() { return false; // write failed }); - assert(router.subscription_count() == 1); + TEST_ASSERT(router.subscription_count() == 1); // Route a message - this will trigger write failure and mark inactive mavlink::MavlinkMessage msg; size_t routed = router.route_message(msg); - assert(routed == 0); - assert(router.subscription_count() == 0); // Active subscription count should drop to 0 + TEST_ASSERT(routed == 0); + TEST_ASSERT(router.subscription_count() == 0); // Active subscription count should drop to 0 // Cleanup should remove it from internal list size_t cleaned = router.cleanup_inactive(); - assert(cleaned == 1); + TEST_ASSERT(cleaned == 1); std::cout << "test_router_cleanup passed!" << std::endl; } From 754d7f754c128976914dd619e8e42f8977fc8466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=BCseyin=20Karakaya?= Date: Sun, 7 Jun 2026 18:01:24 +0300 Subject: [PATCH 7/7] ci: add nlohmann-json3-dev dependency to suppress libmav preprocessor JSON warning --- .github/workflows/ci.yml | 3 ++- setup.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e77f05..05265d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,8 @@ jobs: libgrpc++-dev \ libprotobuf-dev \ protobuf-compiler \ - protobuf-compiler-grpc + protobuf-compiler-grpc \ + nlohmann-json3-dev - name: Set up Python Virtual Environment run: | diff --git a/setup.sh b/setup.sh index c47cd3c..9249399 100755 --- a/setup.sh +++ b/setup.sh @@ -17,7 +17,8 @@ sudo apt-get install -y \ python3-pip \ libgrpc++-dev \ libprotobuf-dev \ - protobuf-compiler-grpc + protobuf-compiler-grpc \ + nlohmann-json3-dev # Ensure git submodules are initialized and updated echo "-- Initializing and updating submodules..."