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

mavlink / MAVSDK / 16699901624

03 Aug 2025 01:51AM UTC coverage: 46.517% (+0.4%) from 46.108%
16699901624

Pull #2637

github

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

140 of 178 new or added lines in 6 files covered. (78.65%)

43 existing lines in 6 files now uncovered.

16300 of 35041 relevant lines covered (46.52%)

368.27 hits per line

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

95.52
/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, MavlinkDirectForwardingUnknownMessage)
4✔
14
{
15
    // Test 3-instance message forwarding where intermediate instance doesn't know the custom
16
    // message Instance 1 (Sender) -> Instance 2 (Forwarder) -> Instance 3 (Receiver) Only Instance
17
    // 1 and 3 have the custom XML loaded, Instance 2 must forward blindly
18

19
    LogInfo() << "Setting up 3-instance forwarding test: Sender -> Forwarder -> Receiver";
1✔
20

21
    // Instance 1: Sender (autopilot that knows custom message)
22
    Mavsdk mavsdk_sender{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
23

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

27
    // Instance 3: Receiver (ground station that knows custom message)
28
    Mavsdk mavsdk_receiver{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
29

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

44
    // Define custom message that only sender and receiver will know about
45
    std::string custom_xml = R"(<?xml version="1.0"?>
1✔
46
<mavlink>
47
    <version>3</version>
48
    <dialect>0</dialect>
49
    <messages>
50
        <message id="421" name="CUSTOM_FORWARD_TEST">
51
            <description>Test message for forwarding unknown messages</description>
52
            <field type="uint32_t" name="test_id">Unique test identifier</field>
53
            <field type="uint16_t" name="sequence">Sequence number</field>
54
            <field type="uint8_t" name="status">Status code</field>
55
            <field type="char[32]" name="message">Status message</field>
56
        </message>
57
    </messages>
58
</mavlink>)";
59

60
    LogInfo() << "Waiting for connections to establish...";
1✔
61
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
1✔
62

63
    // Receiver discovers the sender system (autopilot) through the forwarder
64
    auto receiver_target_system = mavsdk_receiver.first_autopilot(10.0);
1✔
65
    ASSERT_TRUE(receiver_target_system.has_value());
1✔
66
    auto system = receiver_target_system.value();
1✔
67
    ASSERT_TRUE(system->has_autopilot());
1✔
68

69
    // Sender waits to discover ground station systems
70
    while (mavsdk_sender.systems().size() == 0) {
1✔
NEW
71
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
72
    }
73

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

77
    // Create MavlinkDirect instances: sender uses discovered system, receiver uses discovered
78
    // autopilot
79
    auto sender_mavlink_direct = MavlinkDirect{sender_target_system};
1✔
80
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
81

82
    // Load custom XML only on sender and receiver (NOT on forwarder)
83
    LogInfo() << "Loading custom XML on sender and receiver (but NOT forwarder)";
1✔
84
    auto load_result_sender = sender_mavlink_direct.load_custom_xml(custom_xml);
1✔
85
    EXPECT_EQ(load_result_sender, MavlinkDirect::Result::Success);
1✔
86

87
    auto load_result_receiver = receiver_mavlink_direct.load_custom_xml(custom_xml);
1✔
88
    EXPECT_EQ(load_result_receiver, MavlinkDirect::Result::Success);
1✔
89

90
    // Set up receiver subscription - handle potential duplicate messages
91
    std::atomic<bool> message_received{false};
1✔
92
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
93
    auto fut = prom.get_future();
1✔
94

95
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
96
        "CUSTOM_FORWARD_TEST", [&prom, &message_received](MavlinkDirect::MavlinkMessage message) {
3✔
97
            LogInfo() << "Receiver got forwarded custom message: " << message.fields_json;
1✔
98
            if (!message_received.exchange(true)) {
1✔
99
                // Only set the promise once, even if we receive the message multiple times
100
                prom.set_value(message);
1✔
101
            }
102
        });
1✔
103

104
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
1✔
105

106
    // Send custom message from sender
107
    LogInfo() << "Sending custom message through forwarder...";
1✔
108
    MavlinkDirect::MavlinkMessage test_message;
1✔
109
    test_message.message_name = "CUSTOM_FORWARD_TEST";
1✔
110
    test_message.system_id = 1;
1✔
111
    test_message.component_id = 1;
1✔
112
    test_message.target_system = 0;
1✔
113
    test_message.target_component = 0;
1✔
114
    test_message.fields_json =
115
        R"({"test_id":12345,"sequence":1,"status":42,"message":"Hello through forwarder!"})";
1✔
116

117
    auto send_result = sender_mavlink_direct.send_message(test_message);
1✔
118
    EXPECT_EQ(send_result, MavlinkDirect::Result::Success);
1✔
119

120
    // Wait for message to be received through the forwarder
121
    LogInfo() << "Waiting for forwarded message...";
1✔
122
    auto wait_result = fut.wait_for(std::chrono::seconds(10));
1✔
123

124
    if (wait_result == std::future_status::timeout) {
1✔
NEW
125
        LogErr()
×
NEW
126
            << "EXPECTED FAILURE: Message forwarding through unknown message failed - this is what we're implementing!";
×
127
        // This is expected to fail initially since we haven't implemented libmav forwarding yet
NEW
128
        EXPECT_TRUE(false)
×
NEW
129
            << "Message was not forwarded through intermediate instance - libmav forwarding not yet implemented";
×
130
    } else {
131
        ASSERT_EQ(wait_result, std::future_status::ready);
1✔
132
        auto received_message = fut.get();
1✔
133

134
        // Verify the forwarded message
135
        EXPECT_EQ(received_message.message_name, "CUSTOM_FORWARD_TEST");
1✔
136
        EXPECT_EQ(received_message.system_id, 1);
1✔
137
        EXPECT_EQ(received_message.component_id, 1);
1✔
138

139
        // Parse and verify JSON content
140
        Json::Value json;
1✔
141
        Json::Reader reader;
1✔
142
        ASSERT_TRUE(reader.parse(received_message.fields_json, json));
1✔
143

144
        EXPECT_EQ(json["test_id"].asUInt(), 12345u);
1✔
145
        EXPECT_EQ(json["sequence"].asUInt(), 1u);
1✔
146
        EXPECT_EQ(json["status"].asUInt(), 42u);
1✔
147
        EXPECT_EQ(json["message"].asString(), "Hello through forwarder!");
2✔
148

149
        LogInfo()
2✔
150
            << "SUCCESS: Custom message successfully forwarded through unknowing intermediate instance!";
1✔
151
    }
1✔
152

153
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
154
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
155
}
1✔
156

157
TEST(SystemTest, MavlinkDirectForwardingKnownMessage)
4✔
158
{
159
    // Test 3-instance message forwarding with a known standard message
160
    // This verifies that enhanced forwarding doesn't break existing functionality
161
    // Instance 1 (Sender) -> Instance 2 (Forwarder) -> Instance 3 (Receiver)
162

163
    LogInfo()
2✔
164
        << "Setting up 3-instance forwarding test for known message: Sender -> Forwarder -> Receiver";
1✔
165

166
    // Instance 1: Sender (autopilot)
167
    Mavsdk mavsdk_sender{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
168

169
    // Instance 2: Forwarder (router with forwarding enabled)
170
    Mavsdk mavsdk_forwarder{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
171

172
    // Instance 3: Receiver (ground station)
173
    Mavsdk mavsdk_receiver{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
174

175
    // Set up connections: Sender -> Forwarder -> Receiver
176
    ASSERT_EQ(
1✔
177
        mavsdk_sender.add_any_connection("udpout://127.0.0.1:17008"), ConnectionResult::Success);
1✔
178
    ASSERT_EQ(
1✔
179
        mavsdk_forwarder.add_any_connection(
180
            "udpin://0.0.0.0:17008", ForwardingOption::ForwardingOn),
181
        ConnectionResult::Success);
1✔
182
    ASSERT_EQ(
1✔
183
        mavsdk_forwarder.add_any_connection(
184
            "udpout://127.0.0.1:17009", ForwardingOption::ForwardingOn),
185
        ConnectionResult::Success);
1✔
186
    ASSERT_EQ(
1✔
187
        mavsdk_receiver.add_any_connection("udpin://0.0.0.0:17009"), ConnectionResult::Success);
1✔
188

189
    LogInfo() << "Waiting for connections to establish...";
1✔
190
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
1✔
191

192
    // Receiver discovers the sender system (autopilot) through the forwarder
193
    auto receiver_target_system = mavsdk_receiver.first_autopilot(10.0);
1✔
194
    ASSERT_TRUE(receiver_target_system.has_value());
1✔
195
    auto system = receiver_target_system.value();
1✔
196
    ASSERT_TRUE(system->has_autopilot());
1✔
197

198
    // Sender waits to discover ground station systems
199
    while (mavsdk_sender.systems().size() == 0) {
1✔
NEW
200
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
201
    }
202

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

206
    // Create MavlinkDirect instances
207
    auto sender_mavlink_direct = MavlinkDirect{sender_target_system};
1✔
208
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
209

210
    // Set up receiver subscription for a known message (GLOBAL_POSITION_INT)
211
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
212
    auto fut = prom.get_future();
1✔
213

214
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
215
        "GLOBAL_POSITION_INT", [&prom](MavlinkDirect::MavlinkMessage message) {
2✔
216
            LogInfo() << "Receiver got forwarded known message: " << message.fields_json;
1✔
217
            prom.set_value(message);
1✔
218
        });
1✔
219

220
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
1✔
221

222
    // Send known message from sender
223
    LogInfo() << "Sending known GLOBAL_POSITION_INT message through forwarder...";
1✔
224
    MavlinkDirect::MavlinkMessage test_message;
1✔
225
    test_message.message_name = "GLOBAL_POSITION_INT";
1✔
226
    test_message.system_id = 1;
1✔
227
    test_message.component_id = 1;
1✔
228
    test_message.target_system = 0;
1✔
229
    test_message.target_component = 0;
1✔
230
    test_message.fields_json =
231
        R"({"time_boot_ms":12345,"lat":473977418,"lon":-1223974560,"alt":100500,"relative_alt":50250,"vx":100,"vy":-50,"vz":25,"hdg":18000})";
1✔
232

233
    auto send_result = sender_mavlink_direct.send_message(test_message);
1✔
234
    EXPECT_EQ(send_result, MavlinkDirect::Result::Success);
1✔
235

236
    // Wait for message to be received through the forwarder
237
    LogInfo() << "Waiting for forwarded known message...";
1✔
238
    auto wait_result = fut.wait_for(std::chrono::seconds(10));
1✔
239

240
    ASSERT_EQ(wait_result, std::future_status::ready);
1✔
241
    auto received_message = fut.get();
1✔
242

243
    // Verify the forwarded message
244
    EXPECT_EQ(received_message.message_name, "GLOBAL_POSITION_INT");
1✔
245
    EXPECT_EQ(received_message.system_id, 1);
1✔
246
    EXPECT_EQ(received_message.component_id, 1);
1✔
247

248
    // Parse and verify JSON content
249
    Json::Value json;
1✔
250
    Json::Reader reader;
1✔
251
    ASSERT_TRUE(reader.parse(received_message.fields_json, json));
1✔
252

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

258
    LogInfo() << "SUCCESS: Known message successfully forwarded through intermediate instance!";
1✔
259

260
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
261
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
262
}
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