Skip to content

Commit ce43a18

Browse files
add tests
1 parent 4c13edb commit ce43a18

File tree

3 files changed

+101
-3
lines changed

3 files changed

+101
-3
lines changed

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ Please read `README.md` for general information about LibUDPard.
55
Keep the code and comments very brief. Be sure every significant code block is preceded with a brief comment.
66

77
When building the code, don't hesitate to use multiple jobs to use all CPU cores.
8+
To speed things up, it is best to configure CMake with `NO_STATIC_ANALYSIS=1`.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ next-generation intelligent vehicles: manned and unmanned aircraft, spacecraft,
3434
- Detailed time complexity and memory requirement models for the benefit of real-time high-integrity applications.
3535
- Runs anywhere out of the box, including extremely resource-constrained baremetal environments with ~100K ROM/RAM.
3636
No porting required.
37-
- MISRA C compliance (reach out to <https://forum.opencyphal.org>).
37+
- Partial MISRA C compliance (reach out to <https://forum.opencyphal.org>).
3838
- Full implementation in a single C file with only ~2k lines of straightforward C99!
3939
- Extensive test coverage.
4040

tests/src/test_e2e_edge.cpp

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ namespace {
1616
void on_message(udpard_rx_t* rx, udpard_rx_port_t* port, udpard_rx_transfer_t transfer);
1717
void on_collision(udpard_rx_t* rx, udpard_rx_port_t* port, udpard_remote_t remote);
1818
constexpr udpard_rx_port_vtable_t callbacks{ .on_message = &on_message, .on_collision = &on_collision };
19+
void on_message_p2p(udpard_rx_t* rx, udpard_rx_port_p2p_t* port, udpard_rx_transfer_p2p_t transfer);
20+
constexpr udpard_rx_port_p2p_vtable_t p2p_callbacks{ &on_message_p2p };
1921

2022
struct CapturedFrame
2123
{
@@ -47,8 +49,9 @@ constexpr udpard_tx_vtable_t tx_vtable{ .eject = &capture_tx_frame };
4749
struct Context
4850
{
4951
std::vector<uint64_t> ids;
50-
size_t collisions = 0;
51-
uint64_t expected_uid = 0;
52+
size_t collisions = 0;
53+
uint64_t expected_uid = 0;
54+
uint64_t expected_topic = 0;
5255
udpard_udpip_ep_t source{};
5356
};
5457

@@ -164,6 +167,19 @@ void on_collision(udpard_rx_t* const rx, udpard_rx_port_t* const /*port*/, const
164167
ctx->collisions++;
165168
}
166169

170+
void on_message_p2p(udpard_rx_t* const rx, udpard_rx_port_p2p_t* const port, const udpard_rx_transfer_p2p_t transfer)
171+
{
172+
auto* const ctx = static_cast<Context*>(rx->user);
173+
ctx->ids.push_back(transfer.base.transfer_id);
174+
if (ctx->expected_topic != 0) {
175+
TEST_ASSERT_EQUAL_UINT64(ctx->expected_topic, transfer.topic_hash);
176+
}
177+
TEST_ASSERT_EQUAL_UINT64(ctx->expected_uid, transfer.base.remote.uid);
178+
TEST_ASSERT_EQUAL_UINT32(ctx->source.ip, transfer.base.remote.endpoints[0].ip);
179+
TEST_ASSERT_EQUAL_UINT16(ctx->source.port, transfer.base.remote.endpoints[0].port);
180+
udpard_fragment_free_all(transfer.base.payload, port->base.memory.fragment);
181+
}
182+
167183
/// UNORDERED mode should drop duplicates while keeping arrival order.
168184
void test_udpard_rx_unordered_duplicates()
169185
{
@@ -262,6 +278,86 @@ void test_udpard_rx_ordered_head_advanced_late()
262278
TEST_ASSERT_EQUAL_size_t(0, fix.ctx.collisions);
263279
}
264280

281+
/// P2P helper should emit frames with auto transfer-ID and proper addressing.
282+
void test_udpard_tx_push_p2p()
283+
{
284+
instrumented_allocator_t tx_alloc_transfer{};
285+
instrumented_allocator_t tx_alloc_payload{};
286+
instrumented_allocator_t rx_alloc_frag{};
287+
instrumented_allocator_t rx_alloc_session{};
288+
instrumented_allocator_new(&tx_alloc_transfer);
289+
instrumented_allocator_new(&tx_alloc_payload);
290+
instrumented_allocator_new(&rx_alloc_frag);
291+
instrumented_allocator_new(&rx_alloc_session);
292+
udpard_tx_mem_resources_t tx_mem{};
293+
tx_mem.transfer = instrumented_allocator_make_resource(&tx_alloc_transfer);
294+
for (auto& res : tx_mem.payload) {
295+
res = instrumented_allocator_make_resource(&tx_alloc_payload);
296+
}
297+
udpard_tx_t tx{};
298+
TEST_ASSERT_TRUE(udpard_tx_new(&tx, 0x1122334455667788ULL, 5U, 8, tx_mem, &tx_vtable));
299+
std::vector<CapturedFrame> frames;
300+
tx.user = &frames;
301+
302+
const udpard_rx_mem_resources_t rx_mem{ .session = instrumented_allocator_make_resource(&rx_alloc_session),
303+
.fragment = instrumented_allocator_make_resource(&rx_alloc_frag) };
304+
udpard_rx_t rx{};
305+
udpard_rx_port_p2p_t port{};
306+
Context ctx{};
307+
const udpard_udpip_ep_t source{ .ip = 0x0A0000AAU, .port = 7600U };
308+
const udpard_udpip_ep_t dest{ .ip = 0x0A000010U, .port = 7400U };
309+
const uint64_t local_uid = 0xCAFEBABECAFED00DULL;
310+
const uint64_t topic_hash = 0xAABBCCDDEEFF1122ULL;
311+
ctx.expected_uid = tx.local_uid;
312+
ctx.expected_topic = topic_hash;
313+
ctx.source = source;
314+
rx.user = &ctx;
315+
TEST_ASSERT_TRUE(udpard_rx_port_new_p2p(&port, local_uid, 1024, rx_mem, &p2p_callbacks));
316+
317+
udpard_remote_t remote{};
318+
remote.uid = local_uid;
319+
remote.endpoints[0U] = dest;
320+
321+
std::array<uint8_t, UDPARD_P2P_HEADER_BYTES> payload_buf{};
322+
constexpr uint8_t p2p_kind_response = 0U;
323+
payload_buf[0] = p2p_kind_response;
324+
for (size_t i = 0; i < sizeof(topic_hash); i++) {
325+
payload_buf[8U + i] = static_cast<uint8_t>((topic_hash >> (i * 8U)) & 0xFFU);
326+
}
327+
const uint64_t response_transfer_id = 55;
328+
for (size_t i = 0; i < sizeof(response_transfer_id); i++) {
329+
payload_buf[16U + i] = static_cast<uint8_t>((response_transfer_id >> (i * 8U)) & 0xFFU);
330+
}
331+
const udpard_bytes_t payload{ .size = payload_buf.size(), .data = payload_buf.data() };
332+
const udpard_us_t now = 0;
333+
const uint64_t first_id = tx.p2p_transfer_id;
334+
TEST_ASSERT_GREATER_THAN_UINT32(
335+
0U, udpard_tx_push_p2p(&tx, now, now + 1000000, udpard_prio_nominal, remote, payload, nullptr, nullptr));
336+
udpard_tx_poll(&tx, now, UDPARD_IFACE_MASK_ALL);
337+
TEST_ASSERT_FALSE(frames.empty());
338+
339+
const udpard_mem_deleter_t tx_payload_deleter{ .user = nullptr, .free = &tx_refcount_free };
340+
for (const auto& f : frames) {
341+
TEST_ASSERT_TRUE(udpard_rx_port_push(
342+
&rx, reinterpret_cast<udpard_rx_port_t*>(&port), now, source, f.datagram, tx_payload_deleter, f.iface_index));
343+
}
344+
udpard_rx_poll(&rx, now);
345+
TEST_ASSERT_EQUAL_size_t(1, ctx.ids.size());
346+
TEST_ASSERT_EQUAL_UINT64(first_id, ctx.ids[0]);
347+
TEST_ASSERT_EQUAL_size_t(0, ctx.collisions);
348+
349+
udpard_rx_port_free(&rx, reinterpret_cast<udpard_rx_port_t*>(&port));
350+
udpard_tx_free(&tx);
351+
TEST_ASSERT_EQUAL(0, tx_alloc_transfer.allocated_fragments);
352+
TEST_ASSERT_EQUAL(0, tx_alloc_payload.allocated_fragments);
353+
TEST_ASSERT_EQUAL(0, rx_alloc_frag.allocated_fragments);
354+
TEST_ASSERT_EQUAL(0, rx_alloc_session.allocated_fragments);
355+
instrumented_allocator_reset(&tx_alloc_transfer);
356+
instrumented_allocator_reset(&tx_alloc_payload);
357+
instrumented_allocator_reset(&rx_alloc_frag);
358+
instrumented_allocator_reset(&rx_alloc_session);
359+
}
360+
265361
} // namespace
266362

267363
extern "C" void setUp() {}
@@ -274,5 +370,6 @@ int main()
274370
RUN_TEST(test_udpard_rx_unordered_duplicates);
275371
RUN_TEST(test_udpard_rx_ordered_out_of_order);
276372
RUN_TEST(test_udpard_rx_ordered_head_advanced_late);
373+
RUN_TEST(test_udpard_tx_push_p2p);
277374
return UNITY_END();
278375
}

0 commit comments

Comments
 (0)