Messaging: packet implementation

This commit is contained in:
Erki 2021-03-27 18:11:37 +02:00
parent ed1509809e
commit 8d67f3470b
4 changed files with 186 additions and 13 deletions

View File

@ -10,21 +10,40 @@
#include <cstring> #include <cstring>
#include <cstdint> #include <cstdint>
#include <array>
namespace Messaging namespace Messaging
{ {
template<size_t N> template<std::size_t N>
struct Packet struct Packet
{ {
static constexpr std::uint8_t preamble[] = { 'A', 'A' }; 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; std::uint32_t data_length = 0;
const std::uint32_t max_data_length = N; const std::uint32_t max_data_length = N;
const std::uint32_t preamble_length = sizeof(preamble); const std::uint32_t preamble_length = sizeof(preamble);
const std::uint32_t data_length_length = sizeof(data_length); const std::uint32_t data_length_length = sizeof(data_length);
template<std::size_t data_in_length>
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<std::size_t data_in_length>
void copy_data_in(const std::array<std::uint8_t, data_in_length>& 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) 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; const std::uint32_t required_size = preamble_length + data_length_length + data_length;
@ -66,6 +85,9 @@ struct Packet
} }
}; };
template <std::size_t N>
constexpr std::uint8_t Packet<N>::preamble[2];
} }

View File

@ -5,11 +5,13 @@ find_package(Catch2 REQUIRED)
add_executable(tests add_executable(tests
main.cpp main.cpp
ringbuffer.cpp ringbuffer.cpp
packet.cpp
) )
target_link_libraries(tests target_link_libraries(tests
PUBLIC PUBLIC
skullc::utility skullc::utility
skullc::messaging
Catch2::Catch2 Catch2::Catch2
) )

149
Tests/packet.cpp Normal file
View File

@ -0,0 +1,149 @@
//
// Created by erki on 27.03.21.
//
#include <catch2/catch.hpp>
#include <messaging_packet.hpp>
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<std::uint8_t, 2> 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);
}
}

View File

@ -9,7 +9,7 @@
template<size_t N> template<size_t N>
using Ringbuffer = Utility::Ringbuffer<int, N>; using Ringbuffer = Utility::Ringbuffer<int, N>;
TEST_CASE("Ringbuffer iterator", "[ringbuffer],[iterator]") TEST_CASE("Ringbuffer iterator", "[peripherals],[ringbuffer]")
{ {
using iterator = Ringbuffer<10>::iterator; using iterator = Ringbuffer<10>::iterator;
const auto begin = iterator::pointer(10); 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; using iterator = Ringbuffer<10>::iterator;
const auto begin = iterator::pointer(10); 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; using iterator = Ringbuffer<10>::iterator;
const auto begin = iterator::pointer(10); 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; Ringbuffer<10> buffer;
@ -96,7 +96,7 @@ TEST_CASE("Constructed buffer is empty.", "[ringbuffer]")
REQUIRE(buffer.empty()); REQUIRE(buffer.empty());
} }
TEST_CASE("Buffer reports size properly.", "[ringbuffer]") TEST_CASE("Buffer reports size properly.", "[peripherals],[ringbuffer]")
{ {
Ringbuffer<3> buffer; 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; Ringbuffer<10> buffer;
const auto old_end = buffer.end(); 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; Ringbuffer<10> buffer;
const auto old_begin = buffer.begin(); 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; Ringbuffer<10> buffer;
const auto old_begin = buffer.begin(); 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; 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; 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; Ringbuffer<2> buffer;