diff --git a/Messaging/Inc/messaging_packet.hpp b/Messaging/Inc/messaging_packet.hpp index 8394977..46b7f80 100644 --- a/Messaging/Inc/messaging_packet.hpp +++ b/Messaging/Inc/messaging_packet.hpp @@ -10,21 +10,40 @@ #include #include +#include namespace Messaging { -template +template struct Packet { static constexpr std::uint8_t preamble[] = { 'A', 'A' }; - std::uint8_t data[N]; + std::uint8_t data[N] = { 0 }; std::uint32_t data_length = 0; const std::uint32_t max_data_length = N; const std::uint32_t preamble_length = sizeof(preamble); const std::uint32_t data_length_length = sizeof(data_length); + template + void copy_data_in(const std::uint8_t (&data_in)[data_in_length]) + { + const std::uint32_t to_copy_length = std::min(std::uint32_t(data_in_length), max_data_length); + + std::memcpy(data, data_in, to_copy_length); + data_length = to_copy_length; + } + + template + void copy_data_in(const std::array& data_in) + { + const std::uint32_t to_copy_length = std::min(std::uint32_t(data_in_length), max_data_length); + + std::memcpy(data, data_in.data(), to_copy_length); + data_length = to_copy_length; + } + bool serialize(std::uint8_t* buffer, const std::uint32_t max_length) { const std::uint32_t required_size = preamble_length + data_length_length + data_length; @@ -66,6 +85,9 @@ struct Packet } }; +template +constexpr std::uint8_t Packet::preamble[2]; + } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 102b9b6..b9a5fb2 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -5,11 +5,13 @@ find_package(Catch2 REQUIRED) add_executable(tests main.cpp ringbuffer.cpp + packet.cpp ) target_link_libraries(tests PUBLIC skullc::utility + skullc::messaging Catch2::Catch2 ) diff --git a/Tests/packet.cpp b/Tests/packet.cpp new file mode 100644 index 0000000..7484a77 --- /dev/null +++ b/Tests/packet.cpp @@ -0,0 +1,149 @@ +// +// Created by erki on 27.03.21. +// + +#include + +#include + +TEST_CASE("Packet copy_data_in copies data in.", "[messaging],[packet]") +{ + Messaging::Packet<2> packet; + + SECTION("Plain C-array data copies properly.") + { + const std::uint8_t data[2] = { 'C', 'D' }; + const std::uint32_t data_size = sizeof(data); + + packet.copy_data_in(data); + + REQUIRE(packet.data_length == data_size); + REQUIRE(packet.data[0] == data[0]); + REQUIRE(packet.data[1] == data[1]); + } + + SECTION("STL data copies properly.") + { + const std::array data = { 'C', 'D' }; + + packet.copy_data_in(data); + + REQUIRE(packet.data_length == data.size()); + REQUIRE(packet.data[0] == data[0]); + REQUIRE(packet.data[1] == data[1]); + } +} + +TEST_CASE("Packet copying data in cuts off excess bytes.", "[messaging],[packet]") +{ + Messaging::Packet<2> packet; + + const std::uint8_t data[4] = { 'C', 'D', 'E', 'F' }; + + packet.copy_data_in(data); + + REQUIRE(packet.data_length == 2); + REQUIRE(packet.data[0] == 'C'); + REQUIRE(packet.data[1] == 'D'); +} + +TEST_CASE("Packet gets serialized properly.", "[messaging],[packet]") +{ + Messaging::Packet<2> packet; + + const std::uint8_t data[2] = { 'C', 'D' }; + const std::uint32_t data_size = sizeof(data); + + packet.copy_data_in(data); + + std::uint8_t output[data_size + 2 + 4]; + REQUIRE(packet.serialize(output, sizeof(output))); + REQUIRE(packet.data_length == 2); + + SECTION("Preamble gets serialized properly.") + { + REQUIRE(output[0] == 'A'); + REQUIRE(output[1] == 'A'); + } + + SECTION("Length gets serialized properly.") + { + std::uint32_t length; + + std::memcpy(&length, output + 2, sizeof(length)); + REQUIRE(length == 2); + } + + SECTION("Data gets serialized properly.") + { + const std::uint32_t offset = sizeof(data_size) + 2; + REQUIRE(output[offset + 0] == 'C'); + REQUIRE(output[offset + 1] == 'D'); + } +} + +TEST_CASE("Packet serialization fails if buffer too small.", "[messaging],[packet]") +{ + Messaging::Packet<2> packet; + + const std::uint8_t data[2] = { 'C', 'D' }; + packet.copy_data_in(data); + + std::uint8_t output[4] = { 0 }; + REQUIRE(packet.serialize(output, sizeof(output)) == false); + + SECTION("Output buffer is left unmodified.") + { + for (const std::uint8_t& u : output) + { + REQUIRE(u == 0); + } + } +} + +TEST_CASE("Packet deserialization works as expected.", "[messaging],[packet]") +{ + std::uint8_t data[] = { + 'A', 'A', 0, 0, 0, 0, 'C', 'D' + }; + + const std::uint32_t data_length = 2; + std::memcpy(data + 2, &data_length, sizeof(data_length)); + + Messaging::Packet<2> packet; + + REQUIRE(packet.deserialize(data, sizeof(data))); + REQUIRE(packet.data_length == data_length); + REQUIRE(packet.data[0] == 'C'); + REQUIRE(packet.data[1] == 'D'); +} + +TEST_CASE("Packet deserialization fails with invalid conditions.", "[messaging],[packet]") +{ + std::uint8_t data[] = { + 'A', 'A', 0, 0, 0, 0, 'C', 'D' + }; + + const std::uint32_t data_length = 2; + std::memcpy(data + 2, &data_length, sizeof(data_length)); + + Messaging::Packet<2> packet; + + SECTION("Invalid preamble causes failure.") + { + data[0] = 'B'; + REQUIRE(packet.deserialize(data, sizeof(data)) == false); + + data[0] = 'A'; + data[1] = 'B'; + REQUIRE(packet.deserialize(data, sizeof(data)) == false); + } + + SECTION("Data length exceeding expected buffer length causes failure.") + { + const std::uint32_t data_length_fake = 4; + std::memcpy(data + 2, &data_length_fake, sizeof(data_length_fake)); + + REQUIRE(packet.deserialize(data, sizeof(data)) == false); + } +} diff --git a/Tests/ringbuffer.cpp b/Tests/ringbuffer.cpp index 6f42905..e02220f 100644 --- a/Tests/ringbuffer.cpp +++ b/Tests/ringbuffer.cpp @@ -9,7 +9,7 @@ template using Ringbuffer = Utility::Ringbuffer; -TEST_CASE("Ringbuffer iterator", "[ringbuffer],[iterator]") +TEST_CASE("Ringbuffer iterator", "[peripherals],[ringbuffer]") { using iterator = Ringbuffer<10>::iterator; const auto begin = iterator::pointer(10); @@ -43,7 +43,7 @@ TEST_CASE("Ringbuffer iterator", "[ringbuffer],[iterator]") } } -TEST_CASE("Ringbuffer iterator at the end", "[ringbuffer],[iterator]") +TEST_CASE("Ringbuffer iterator at the end", "[peripherals],[ringbuffer]") { using iterator = Ringbuffer<10>::iterator; const auto begin = iterator::pointer(10); @@ -71,7 +71,7 @@ TEST_CASE("Ringbuffer iterator at the end", "[ringbuffer],[iterator]") } } -TEST_CASE("Ringbuffer iterator at the beginning", "[ringbuffer],[iterator]") +TEST_CASE("Ringbuffer iterator at the beginning", "[peripherals],[ringbuffer]") { using iterator = Ringbuffer<10>::iterator; const auto begin = iterator::pointer(10); @@ -87,7 +87,7 @@ TEST_CASE("Ringbuffer iterator at the beginning", "[ringbuffer],[iterator]") } } -TEST_CASE("Constructed buffer is empty.", "[ringbuffer]") +TEST_CASE("Constructed buffer is empty.", "[peripherals],[ringbuffer]") { Ringbuffer<10> buffer; @@ -96,7 +96,7 @@ TEST_CASE("Constructed buffer is empty.", "[ringbuffer]") REQUIRE(buffer.empty()); } -TEST_CASE("Buffer reports size properly.", "[ringbuffer]") +TEST_CASE("Buffer reports size properly.", "[peripherals],[ringbuffer]") { Ringbuffer<3> buffer; @@ -140,7 +140,7 @@ TEST_CASE("Buffer reports size properly.", "[ringbuffer]") } } -TEST_CASE("Adding single element.", "[ringbuffer]") +TEST_CASE("Adding single element.", "[peripherals],[ringbuffer]") { Ringbuffer<10> buffer; const auto old_end = buffer.end(); @@ -191,7 +191,7 @@ TEST_CASE("Adding single element.", "[ringbuffer]") } } -TEST_CASE("Adding multiple elements.", "[ringbuffer]") +TEST_CASE("Adding multiple elements.", "[peripherals],[ringbuffer]") { Ringbuffer<10> buffer; const auto old_begin = buffer.begin(); @@ -223,7 +223,7 @@ TEST_CASE("Adding multiple elements.", "[ringbuffer]") } } -TEST_CASE("Removing elements from the ringbuffer.", "[ringbuffer]") +TEST_CASE("Removing elements from the ringbuffer.", "[peripherals],[ringbuffer]") { Ringbuffer<10> buffer; const auto old_begin = buffer.begin(); @@ -271,7 +271,7 @@ TEST_CASE("Removing elements from the ringbuffer.", "[ringbuffer]") } } -TEST_CASE("Clearing a ringbuffer works.", "[ringbuffer]") +TEST_CASE("Clearing a ringbuffer works.", "[peripherals],[ringbuffer]") { Ringbuffer<10> buffer; @@ -293,7 +293,7 @@ TEST_CASE("Clearing a ringbuffer works.", "[ringbuffer]") } } -TEST_CASE("Manually incrementing tail works.", "[ringbuffer]") +TEST_CASE("Manually incrementing tail works.", "[peripherals],[ringbuffer]") { Ringbuffer<10> buffer; @@ -312,7 +312,7 @@ TEST_CASE("Manually incrementing tail works.", "[ringbuffer]") } } -TEST_CASE("Manually incrementing tail when full deletes data.", "[ringbuffer]") +TEST_CASE("Manually incrementing tail when full deletes data.", "[peripherals],[ringbuffer]") { Ringbuffer<2> buffer;