• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 16714259187

04 Aug 2025 04:47AM UTC coverage: 46.46% (+0.4%) from 46.108%
16714259187

Pull #2637

github

web-flow
Merge e7ba3100b into 501170eb2
Pull Request #2637: Enable forwarding unknown messages

141 of 175 new or added lines in 6 files covered. (80.57%)

4 existing lines in 3 files now uncovered.

16267 of 35013 relevant lines covered (46.46%)

367.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.31
/src/system_tests/mavlink_direct_forwarding.cpp
1
#include "log.h"
2
#include "mavsdk.h"
3
#include "plugins/mavlink_direct/mavlink_direct.h"
4
#include <chrono>
5
#include <thread>
6
#include <future>
7
#include <atomic>
8
#include <gtest/gtest.h>
9
#include <json/json.h>
10

11
using namespace mavsdk;
12

13
TEST(SystemTest, MavlinkDirectForwardingKnownMessage)
4✔
14
{
15
    // Test 3-instance message forwarding with a known standard message
16
    // This verifies that enhanced forwarding doesn't break existing functionality
17
    // Instance 1 (Sender) -> Instance 2 (Forwarder) -> Instance 3 (Receiver)
18

19
    // Instance 1: Sender (autopilot)
20
    Mavsdk mavsdk_sender{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
21

22
    // Instance 2: Forwarder (router with forwarding enabled)
23
    Mavsdk mavsdk_forwarder{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
24

25
    // Instance 3: Receiver (ground station)
26
    Mavsdk mavsdk_receiver{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
27

28
    // Set up connections: Sender -> Forwarder -> Receiver
29
    ASSERT_EQ(
1✔
30
        mavsdk_sender.add_any_connection("udpout://127.0.0.1:17008"), ConnectionResult::Success);
1✔
31
    ASSERT_EQ(
1✔
32
        mavsdk_forwarder.add_any_connection(
33
            "udpin://0.0.0.0:17008", ForwardingOption::ForwardingOn),
34
        ConnectionResult::Success);
1✔
35
    ASSERT_EQ(
1✔
36
        mavsdk_forwarder.add_any_connection(
37
            "udpout://127.0.0.1:17009", ForwardingOption::ForwardingOn),
38
        ConnectionResult::Success);
1✔
39
    ASSERT_EQ(
1✔
40
        mavsdk_receiver.add_any_connection("udpin://0.0.0.0:17009"), ConnectionResult::Success);
1✔
41

42
    LogInfo() << "Waiting for connections to establish...";
1✔
43
    std::this_thread::sleep_for(std::chrono::seconds(1));
1✔
44

45
    // Receiver discovers the sender system (autopilot) through the forwarder
46
    auto receiver_target_system = mavsdk_receiver.first_autopilot(10.0);
1✔
47
    ASSERT_TRUE(receiver_target_system.has_value());
1✔
48
    auto system = receiver_target_system.value();
1✔
49
    ASSERT_TRUE(system->has_autopilot());
1✔
50

51
    // Sender waits to discover ground station systems
52
    while (mavsdk_sender.systems().size() == 0) {
1✔
NEW
53
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
54
    }
55

56
    // Get sender's view of the receiver systems (for sending)
57
    auto sender_target_system = mavsdk_sender.systems().at(0);
1✔
58

59
    // Create MavlinkDirect instances
60
    auto sender_mavlink_direct = MavlinkDirect{sender_target_system};
1✔
61
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
62

63
    // Set up receiver subscription for a known message (GLOBAL_POSITION_INT)
64
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
65
    auto fut = prom.get_future();
1✔
66

67
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
68
        "GLOBAL_POSITION_INT", [&prom](const MavlinkDirect::MavlinkMessage& message) {
2✔
69
            LogInfo() << "Receiver got forwarded known message: " << message.fields_json;
1✔
70
            prom.set_value(message);
1✔
71
        });
1✔
72

73
    // Send known message from sender
74
    LogInfo() << "Sending known GLOBAL_POSITION_INT message through forwarder...";
1✔
75
    MavlinkDirect::MavlinkMessage test_message;
1✔
76
    test_message.message_name = "GLOBAL_POSITION_INT";
1✔
77
    test_message.system_id = 1;
1✔
78
    test_message.component_id = 1;
1✔
79
    test_message.target_system = 0;
1✔
80
    test_message.target_component = 0;
1✔
81
    test_message.fields_json =
82
        R"({"time_boot_ms":12345,"lat":473977418,"lon":-1223974560,"alt":100500,"relative_alt":50250,"vx":100,"vy":-50,"vz":25,"hdg":18000})";
1✔
83

84
    auto send_result = sender_mavlink_direct.send_message(test_message);
1✔
85
    EXPECT_EQ(send_result, MavlinkDirect::Result::Success);
1✔
86

87
    // Wait for message to be received through the forwarder
88
    LogInfo() << "Waiting for forwarded known message...";
1✔
89
    auto wait_result = fut.wait_for(std::chrono::seconds(3));
1✔
90

91
    ASSERT_EQ(wait_result, std::future_status::ready);
1✔
92
    auto received_message = fut.get();
1✔
93

94
    // Verify the forwarded message
95
    EXPECT_EQ(received_message.message_name, "GLOBAL_POSITION_INT");
1✔
96
    EXPECT_EQ(received_message.system_id, 1);
1✔
97
    EXPECT_EQ(received_message.component_id, 1);
1✔
98

99
    // Parse and verify JSON content
100
    Json::Value json;
1✔
101
    Json::Reader reader;
1✔
102
    ASSERT_TRUE(reader.parse(received_message.fields_json, json));
1✔
103

104
    // The JSON format may vary but should contain the message information
105
    // For now, just verify it's not empty and contains the message name
106
    EXPECT_FALSE(received_message.fields_json.empty());
1✔
107
    EXPECT_TRUE(received_message.fields_json.find("GLOBAL_POSITION_INT") != std::string::npos);
1✔
108

109
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
110
}
1✔
111

112
TEST(SystemTest, MavlinkDirectForwardingUnknownMessage)
4✔
113
{
114
    // Test 3-instance message forwarding where intermediate instance doesn't know the custom
115
    // message Instance 1 (Sender) -> Instance 2 (Forwarder) -> Instance 3 (Receiver) Only Instance
116
    // 1 and 3 have the custom XML loaded, Instance 2 must forward blindly
117

118
    // Instance 1: Sender (autopilot that knows custom message)
119
    Mavsdk mavsdk_sender{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
120

121
    // Instance 2: Forwarder (router that doesn't know custom message but forwards all)
122
    Mavsdk mavsdk_forwarder{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
123

124
    // Instance 3: Receiver (ground station that knows custom message)
125
    Mavsdk mavsdk_receiver{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
126

127
    // Set up connections: Sender -> Forwarder -> Receiver
128
    ASSERT_EQ(
1✔
129
        mavsdk_sender.add_any_connection("udpout://127.0.0.1:17006"), ConnectionResult::Success);
1✔
130
    ASSERT_EQ(
1✔
131
        mavsdk_forwarder.add_any_connection(
132
            "udpin://0.0.0.0:17006", ForwardingOption::ForwardingOn),
133
        ConnectionResult::Success);
1✔
134
    ASSERT_EQ(
1✔
135
        mavsdk_forwarder.add_any_connection(
136
            "udpout://127.0.0.1:17007", ForwardingOption::ForwardingOn),
137
        ConnectionResult::Success);
1✔
138
    ASSERT_EQ(
1✔
139
        mavsdk_receiver.add_any_connection("udpin://0.0.0.0:17007"), ConnectionResult::Success);
1✔
140

141
    // Define custom message that only sender and receiver will know about
142
    std::string custom_xml = R"(<?xml version="1.0"?>
1✔
143
<mavlink>
144
    <version>3</version>
145
    <dialect>0</dialect>
146
    <messages>
147
        <message id="421" name="CUSTOM_FORWARD_TEST">
148
            <description>Test message for forwarding unknown messages</description>
149
            <field type="uint32_t" name="test_id">Unique test identifier</field>
150
            <field type="uint16_t" name="sequence">Sequence number</field>
151
            <field type="uint8_t" name="status">Status code</field>
152
            <field type="char[32]" name="message">Status message</field>
153
        </message>
154
    </messages>
155
</mavlink>)";
156

157
    LogInfo() << "Waiting for connections to establish...";
1✔
158
    std::this_thread::sleep_for(std::chrono::seconds(1));
1✔
159

160
    // Receiver discovers the sender system (autopilot) through the forwarder
161
    auto receiver_target_system = mavsdk_receiver.first_autopilot(10.0);
1✔
162
    ASSERT_TRUE(receiver_target_system.has_value());
1✔
163
    auto system = receiver_target_system.value();
1✔
164
    ASSERT_TRUE(system->has_autopilot());
1✔
165

166
    // Sender waits to discover ground station systems
167
    while (mavsdk_sender.systems().size() == 0) {
1✔
NEW
168
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
169
    }
170

171
    // Get sender's view of the receiver systems (for sending)
172
    auto sender_target_system = mavsdk_sender.systems().at(0);
1✔
173

174
    // Create MavlinkDirect instances: sender uses discovered system, receiver uses discovered
175
    // autopilot
176
    auto sender_mavlink_direct = MavlinkDirect{sender_target_system};
1✔
177
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
178

179
    // Load custom XML only on sender and receiver (NOT on forwarder)
180
    LogInfo() << "Loading custom XML on sender and receiver (but NOT forwarder)";
1✔
181
    auto load_result_sender = sender_mavlink_direct.load_custom_xml(custom_xml);
1✔
182
    EXPECT_EQ(load_result_sender, MavlinkDirect::Result::Success);
1✔
183

184
    auto load_result_receiver = receiver_mavlink_direct.load_custom_xml(custom_xml);
1✔
185
    EXPECT_EQ(load_result_receiver, MavlinkDirect::Result::Success);
1✔
186

187
    // Set up receiver subscription - handle potential duplicate messages
188
    std::atomic<bool> message_received{false};
1✔
189
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
190
    auto fut = prom.get_future();
1✔
191

192
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
193
        "CUSTOM_FORWARD_TEST",
194
        [&prom, &message_received](const MavlinkDirect::MavlinkMessage& message) {
3✔
195
            LogInfo() << "Receiver got forwarded custom message: " << message.fields_json;
1✔
196
            if (!message_received.exchange(true)) {
1✔
197
                // Only set the promise once, even if we receive the message multiple times
198
                prom.set_value(message);
1✔
199
            }
200
        });
1✔
201

202
    // Send custom message from sender
203
    LogInfo() << "Sending custom message through forwarder...";
1✔
204
    MavlinkDirect::MavlinkMessage test_message;
1✔
205
    test_message.message_name = "CUSTOM_FORWARD_TEST";
1✔
206
    test_message.system_id = 1;
1✔
207
    test_message.component_id = 1;
1✔
208
    test_message.target_system = 0;
1✔
209
    test_message.target_component = 0;
1✔
210
    test_message.fields_json =
211
        R"({"test_id":12345,"sequence":1,"status":42,"message":"Hello through forwarder!"})";
1✔
212

213
    auto send_result = sender_mavlink_direct.send_message(test_message);
1✔
214
    EXPECT_EQ(send_result, MavlinkDirect::Result::Success);
1✔
215

216
    // Wait for message to be received through the forwarder
217
    LogInfo() << "Waiting for forwarded message...";
1✔
218
    auto wait_result = fut.wait_for(std::chrono::seconds(3));
1✔
219

220
    ASSERT_EQ(wait_result, std::future_status::ready);
1✔
221
    auto received_message = fut.get();
1✔
222

223
    // Verify the forwarded message
224
    EXPECT_EQ(received_message.message_name, "CUSTOM_FORWARD_TEST");
1✔
225
    EXPECT_EQ(received_message.system_id, 1);
1✔
226
    EXPECT_EQ(received_message.component_id, 1);
1✔
227

228
    // Parse and verify JSON content
229
    Json::Value json;
1✔
230
    Json::Reader reader;
1✔
231
    ASSERT_TRUE(reader.parse(received_message.fields_json, json));
1✔
232

233
    EXPECT_EQ(json["test_id"].asUInt(), 12345u);
1✔
234
    EXPECT_EQ(json["sequence"].asUInt(), 1u);
1✔
235
    EXPECT_EQ(json["status"].asUInt(), 42u);
1✔
236
    EXPECT_EQ(json["message"].asString(), "Hello through forwarder!");
2✔
237

238
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
239
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc