Skip to content

Commit

Permalink
Add tests for subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-kirienko committed Aug 9, 2023
1 parent 7428526 commit 6625b09
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 10 deletions.
24 changes: 14 additions & 10 deletions libudpard/udpard.c
Original file line number Diff line number Diff line change
Expand Up @@ -1472,20 +1472,23 @@ static inline void rxSessionInit(struct UdpardInternalRxSession* const self, con
static inline void rxSessionDestroyTree(struct UdpardInternalRxSession* const self,
const struct UdpardRxMemoryResources memory)
{
for (uint_fast8_t i = 0; i < UDPARD_NETWORK_INTERFACE_COUNT_MAX; i++)
{
rxIfaceFree(&self->ifaces[i], (RxMemory){.fragment = memory.fragment, .payload = memory.payload});
}
for (uint_fast8_t i = 0; i < 2; i++)
if (self != NULL)
{
struct UdpardInternalRxSession* const child = (struct UdpardInternalRxSession*) (void*) self->base.lr[i];
if (child != NULL)
for (uint_fast8_t i = 0; i < UDPARD_NETWORK_INTERFACE_COUNT_MAX; i++)
{
rxIfaceFree(&self->ifaces[i], (RxMemory){.fragment = memory.fragment, .payload = memory.payload});
}
for (uint_fast8_t i = 0; i < 2; i++)
{
UDPARD_ASSERT(child->base.up == &self->base);
rxSessionDestroyTree(child, memory); // NOSONAR recursion
struct UdpardInternalRxSession* const child = (struct UdpardInternalRxSession*) (void*) self->base.lr[i];
if (child != NULL)
{
UDPARD_ASSERT(child->base.up == &self->base);
rxSessionDestroyTree(child, memory); // NOSONAR recursion
}
}
memFree(memory.session, sizeof(struct UdpardInternalRxSession), self);
}
memFree(memory.session, sizeof(struct UdpardInternalRxSession), self);
}

// -------------------------------------------------- RX PORT --------------------------------------------------
Expand Down Expand Up @@ -1632,6 +1635,7 @@ static inline void rxPortInit(struct UdpardRxPort* const self)
static inline void rxPortFree(struct UdpardRxPort* const self, const struct UdpardRxMemoryResources memory)
{
rxSessionDestroyTree(self->sessions, memory);
self->sessions = NULL;
}

// -------------------------------------------------- RX API --------------------------------------------------
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ endfunction()
gen_test_matrix(test_helpers "src/test_helpers.c")
gen_test_matrix(test_cavl "src/test_cavl.cpp")
gen_test_matrix(test_tx "${library_dir}/udpard.c;src/test_tx.cpp")
gen_test_matrix(test_rx "${library_dir}/udpard.c;src/test_rx.cpp")
gen_test_matrix(test_e2e "${library_dir}/udpard.c;src/test_e2e.cpp")
gen_test_matrix(test_misc "${library_dir}/udpard.c;src/test_misc.cpp")
gen_test_matrix(test_intrusive_crc "src/test_intrusive_crc.c")
gen_test_matrix(test_intrusive_tx "src/test_intrusive_tx.c")
Expand Down
22 changes: 22 additions & 0 deletions tests/src/test_e2e.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// This software is distributed under the terms of the MIT License.
/// Copyright (C) OpenCyphal Development Team <opencyphal.org>
/// Copyright Amazon.com Inc. or its affiliates.
/// SPDX-License-Identifier: MIT

#include <unity.h>

namespace
{
//
} // namespace

void setUp() {}

void tearDown() {}

int main()
{
UNITY_BEGIN();
// TODO
return UNITY_END();
}
162 changes: 162 additions & 0 deletions tests/src/test_rx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/// This software is distributed under the terms of the MIT License.
/// Copyright (C) OpenCyphal Development Team <opencyphal.org>
/// Copyright Amazon.com Inc. or its affiliates.
/// SPDX-License-Identifier: MIT

#include <udpard.h>
#include "helpers.h"
#include "hexdump.hpp"
#include <unity.h>
#include <cstdint>
#include <variant>
#include <cstring>
#include <iostream>
#include <array>

namespace
{
void testRxSubscriptionInit()
{
InstrumentedAllocator mem_session{};
InstrumentedAllocator mem_fragment{};
InstrumentedAllocator mem_payload{};
instrumentedAllocatorNew(&mem_session);
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
UdpardRxSubscription sub{};
TEST_ASSERT_EQUAL(0,
udpardRxSubscriptionInit(&sub,
0x1234,
1000,
{
.session = instrumentedAllocatorMakeMemoryResource(&mem_session),
.fragment = instrumentedAllocatorMakeMemoryResource(&mem_fragment),
.payload = instrumentedAllocatorMakeMemoryDeleter(&mem_payload),
}));
TEST_ASSERT_EQUAL(&instrumentedAllocatorAllocate, sub.memory.session.allocate);
TEST_ASSERT_EQUAL(&instrumentedAllocatorDeallocate, sub.memory.session.deallocate);
TEST_ASSERT_EQUAL(1000, sub.port.extent);
TEST_ASSERT_EQUAL(UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC, sub.port.transfer_id_timeout_usec);
TEST_ASSERT_EQUAL(nullptr, sub.port.sessions);
TEST_ASSERT_EQUAL(0xEF001234UL, sub.udp_ip_endpoint.ip_address);
TEST_ASSERT_EQUAL(9382, sub.udp_ip_endpoint.udp_port);
TEST_ASSERT_EQUAL(0, mem_session.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
udpardRxSubscriptionFree(&sub);
TEST_ASSERT_EQUAL(0, mem_session.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
udpardRxSubscriptionFree(nullptr); // No-op.
// Invalid arguments.
TEST_ASSERT_EQUAL(-UDPARD_ERROR_ARGUMENT,
udpardRxSubscriptionInit(nullptr,
0xFFFF,
1000,
{
.session = instrumentedAllocatorMakeMemoryResource(&mem_session),
.fragment = instrumentedAllocatorMakeMemoryResource(&mem_fragment),
.payload = instrumentedAllocatorMakeMemoryDeleter(&mem_payload),
}));
TEST_ASSERT_EQUAL(-UDPARD_ERROR_ARGUMENT,
udpardRxSubscriptionInit(&sub,
0xFFFF,
1000,
{
.session = instrumentedAllocatorMakeMemoryResource(&mem_session),
.fragment = instrumentedAllocatorMakeMemoryResource(&mem_fragment),
.payload = instrumentedAllocatorMakeMemoryDeleter(&mem_payload),
}));
TEST_ASSERT_EQUAL(-UDPARD_ERROR_ARGUMENT, udpardRxSubscriptionInit(&sub, 1234, 1000, {}));
}

void testRxSubscriptionReceive()
{
InstrumentedAllocator mem_session{};
InstrumentedAllocator mem_fragment{};
InstrumentedAllocator mem_payload{};
instrumentedAllocatorNew(&mem_session);
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
UdpardRxSubscription sub{};
TEST_ASSERT_EQUAL(0,
udpardRxSubscriptionInit(&sub,
0x1234,
1000,
{
.session = instrumentedAllocatorMakeMemoryResource(&mem_session),
.fragment = instrumentedAllocatorMakeMemoryResource(&mem_fragment),
.payload = instrumentedAllocatorMakeMemoryDeleter(&mem_payload),
}));
TEST_ASSERT_EQUAL(1000, sub.port.extent);
TEST_ASSERT_EQUAL(UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC, sub.port.transfer_id_timeout_usec);
TEST_ASSERT_EQUAL(nullptr, sub.port.sessions);
TEST_ASSERT_EQUAL(0xEF001234UL, sub.udp_ip_endpoint.ip_address);
TEST_ASSERT_EQUAL(9382, sub.udp_ip_endpoint.udp_port);
TEST_ASSERT_EQUAL(0, mem_session.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
UdpardRxTransfer transfer{};
// Feed a single-frame transfer. Remember that in Cyphal/UDP, the payload CRC is part of the payload itself.
//
//>>> from pycyphal.transport.commons.crc import CRC32C
//>>> CRC32C.new(b"Hello!").value_as_bytes
//
// >>> from pycyphal.transport.udp import UDPFrame
// >>> from pycyphal.transport import Priority, MessageDataSpecifier, ServiceDataSpecifier
// >>> frame = UDPFrame(priority=Priority.FAST, transfer_id=0xbadc0ffee0ddf00d, index=0, end_of_transfer=True,
// payload=memoryview(b'Hello!\xd6\xeb\xfd\t'), source_node_id=2345, destination_node_id=0xFFFF,
// data_specifier=MessageDataSpecifier(0x1234), user_data=0)
// >>> list(frame.compile_header_and_payload()[0])
// >>> list(frame.compile_header_and_payload()[1])
{
const std::array<std::uint_fast8_t, 34> data{{1, 2, 41, 9, 255, 255, 52, 18, 13, 240, 221, 224,
254, 15, 220, 186, 0, 0, 0, 128, 0, 0, 246, 129, //
72, 101, 108, 108, 111, 33, 214, 235, 253, 9}};
const UdpardMutablePayload datagram{
.size = sizeof(data),
.data = instrumentedAllocatorAllocate(&mem_payload, sizeof(data)),
};
TEST_ASSERT_NOT_NULL(datagram.data);
std::memcpy(datagram.data, data.data(), data.size());
TEST_ASSERT_EQUAL(1, udpardRxSubscriptionReceive(&sub, 10'000'000, datagram, 0, &transfer));
}
TEST_ASSERT_EQUAL(1, mem_session.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments); // Head optimization in effect.
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(10'000'000, transfer.timestamp_usec);
TEST_ASSERT_EQUAL(UdpardPriorityFast, transfer.priority);
TEST_ASSERT_EQUAL(2345, transfer.source_node_id);
TEST_ASSERT_EQUAL(0xBADC0FFEE0DDF00DUL, transfer.transfer_id);
TEST_ASSERT_EQUAL(6, transfer.payload_size);
TEST_ASSERT_EQUAL(6, transfer.payload.view.size);
TEST_ASSERT_EQUAL_MEMORY("Hello!", transfer.payload.view.data, 6);
TEST_ASSERT_NULL(transfer.payload.next);
// Free the subscription, ensure the payload is not affected because its ownership has been transferred to us.
udpardRxSubscriptionFree(&sub);
udpardRxSubscriptionFree(&sub); // The API does not guarantee anything but this is for extra safety.
TEST_ASSERT_EQUAL(0, mem_session.allocated_fragments); // Session gone. Bye bye.
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments); // Stayin' alive.
// Free the payload as well.
udpardRxFragmentFree(transfer.payload,
instrumentedAllocatorMakeMemoryResource(&mem_fragment),
instrumentedAllocatorMakeMemoryDeleter(&mem_payload));
TEST_ASSERT_EQUAL(0, mem_session.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments); // Yeah.
}

} // namespace

void setUp() {}

void tearDown() {}

int main()
{
UNITY_BEGIN();
RUN_TEST(testRxSubscriptionInit);
RUN_TEST(testRxSubscriptionReceive);
return UNITY_END();
}

0 comments on commit 6625b09

Please sign in to comment.