blob: d7b35b605c54f7d7f1fec6f22266cb3b0f40f505 [file] [log] [blame]
#include "y2019/jevois/cobs.h"
#include "aos/testing/test_logging.h"
#include "gtest/gtest.h"
#include "third_party/GSL/include/gsl/gsl"
namespace frc971 {
namespace jevois {
// Tests the size conversions for some known, simple values.
TEST(CobsMaxEncodedSizeTest, Simple) {
EXPECT_EQ(0u, CobsMaxEncodedSize(0));
EXPECT_EQ(2u, CobsMaxEncodedSize(1));
EXPECT_EQ(3u, CobsMaxEncodedSize(2));
EXPECT_EQ(254u, CobsMaxEncodedSize(253));
EXPECT_EQ(255u, CobsMaxEncodedSize(254));
EXPECT_EQ(257u, CobsMaxEncodedSize(255));
}
class CobsTest : public ::testing::Test {
public:
CobsTest() { aos::testing::EnableTestLogging(); }
template <size_t min_buffer_size = 0, size_t number_elements>
void EncodeAndDecode(const char (&input_data)[number_elements]) {
static constexpr size_t input_size =
(min_buffer_size > number_elements) ? min_buffer_size : number_elements;
EncodeAndDecode<input_size>(gsl::span<const char>(input_data));
}
template <size_t max_decoded_size>
void EncodeAndDecode(const gsl::span<const char> decoded_input) {
std::array<char, CobsMaxEncodedSize(max_decoded_size)> encoded_buffer;
const auto encoded =
CobsEncode<max_decoded_size>(decoded_input, &encoded_buffer);
ASSERT_LE(encoded.size(), encoded_buffer.size());
ASSERT_EQ(encoded.data(), &encoded_buffer.front());
std::array<char, max_decoded_size> decoded_buffer;
const auto decoded = CobsDecode<max_decoded_size>(encoded, &decoded_buffer);
ASSERT_LE(decoded.size(), decoded_buffer.size());
ASSERT_EQ(decoded.data(), &decoded_buffer.front());
ASSERT_EQ(decoded.size(), decoded_input.size());
for (int i = 0; i < decoded.size(); ++i) {
EXPECT_EQ(decoded[i], decoded_input[i]);
}
}
};
// Tests various small buffers.
TEST_F(CobsTest, Small) {
EncodeAndDecode<1>(std::array<char, 0>{});
EncodeAndDecode<5>(std::array<char, 0>{});
{
const char data[] = {0};
EncodeAndDecode(data);
}
{
const char data[] = {1};
EncodeAndDecode(data);
}
{
const char data[] = {static_cast<char>(254)};
EncodeAndDecode(data);
}
{
const char data[] = {static_cast<char>(255)};
EncodeAndDecode(data);
}
{
const char data[] = {0, 1};
EncodeAndDecode(data);
}
{
const char data[] = {0, static_cast<char>(254)};
EncodeAndDecode(data);
}
{
const char data[] = {0, static_cast<char>(255)};
EncodeAndDecode(data);
}
{
const char data[] = {0, 1, static_cast<char>(254)};
EncodeAndDecode(data);
}
{
const char data[] = {0, 1, static_cast<char>(255)};
EncodeAndDecode(data);
}
{
const char data[] = {static_cast<char>(254), 1};
EncodeAndDecode(data);
}
{
const char data[] = {static_cast<char>(255), 1};
EncodeAndDecode(data);
}
{
const char data[] = {static_cast<char>(254), 0};
EncodeAndDecode(data);
}
{
const char data[] = {static_cast<char>(255), 0};
EncodeAndDecode(data);
}
}
// Tests encoding arrays with approximately one full chunk. This exposes some
// corner cases in the binary format.
TEST_F(CobsTest, AroundOneChunk) {
char data[256];
for (size_t i = 0; i < sizeof(data); ++i) {
data[i] = (i * 7) & 0xFF;
}
const gsl::span<char> data_span = data;
for (int i = 253; i <= 256; ++i) {
EncodeAndDecode<256>(data_span.subspan(0, i));
}
for (int i = 253; i <= 255; ++i) {
EncodeAndDecode<255>(data_span.subspan(0, i));
}
for (int i = 253; i <= 254; ++i) {
EncodeAndDecode<254>(data_span.subspan(0, i));
}
EncodeAndDecode<253>(data_span.subspan(0, 253));
}
// Tests parsing a few packets, one byte at a time.
TEST(CobsPacketizerTest, BasicSingleByte) {
CobsPacketizer<5> packetizer;
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{1}});
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{2}});
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{3}});
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 3>{{1, 2, 3}}),
packetizer.received_packet());
packetizer.clear_received_packet();
ASSERT_TRUE(packetizer.received_packet().empty());
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{5}});
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
ASSERT_FALSE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{9}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
packetizer.ParseData(std::array<char, 1>{{7}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 2>{{9, 7}}),
packetizer.received_packet());
}
// Tests parsing a few packets, one span per packet.
TEST(CobsPacketizerTest, BasicSinglePacket) {
CobsPacketizer<5> packetizer;
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 3>{{1, 2, 3}});
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 3>{{1, 2, 3}}),
packetizer.received_packet());
packetizer.clear_received_packet();
ASSERT_TRUE(packetizer.received_packet().empty());
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{5}});
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
ASSERT_FALSE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 2>{{9, 7}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 2>{{9, 7}}),
packetizer.received_packet());
}
// Tests parsing a few packets, one span per packet including its terminator.
TEST(CobsPacketizerTest, BasicSinglePacketWithTerminator) {
CobsPacketizer<5> packetizer;
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 4>{{1, 2, 3, 0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 3>{{1, 2, 3}}),
packetizer.received_packet());
packetizer.clear_received_packet();
ASSERT_TRUE(packetizer.received_packet().empty());
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 2>{{5, 0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
ASSERT_FALSE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 3>{{9, 7, 0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 2>{{9, 7}}),
packetizer.received_packet());
}
// Tests parsing a packet in the same span as the previous terminator.
TEST(CobsPacketizerTest, OverlappingEnd) {
CobsPacketizer<5> packetizer;
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 3>{{1, 2, 3}});
ASSERT_TRUE(packetizer.received_packet().empty());
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 2>{{0, 5}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 3>{{1, 2, 3}}),
packetizer.received_packet());
packetizer.clear_received_packet();
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
}
// Tests parsing a packet in the same span as the previous terminator, including
// its terminator (so we skip the first packet).
TEST(CobsPacketizerTest, OverlappingTwoEnds) {
CobsPacketizer<5> packetizer;
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 3>{{1, 2, 3}});
ASSERT_TRUE(packetizer.received_packet().empty());
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 3>{{0, 5, 0}});
ASSERT_FALSE(packetizer.received_packet().empty());
// We skip the {{1, 2, 3}} packet (arbitrarily; either that packet or this one
// has to be skipped).
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{5}}),
packetizer.received_packet());
}
// Tests parsing a packet in the same span as the previous terminator, including
// its terminator (so we skip the first packet), and then starting another new
// packet.
TEST(CobsPacketizerTest, OverlappingTwoEndsAndPartial) {
CobsPacketizer<5> packetizer;
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 3>{{1, 2, 3}});
ASSERT_TRUE(packetizer.received_packet().empty());
ASSERT_TRUE(packetizer.received_packet().empty());
packetizer.ParseData(std::array<char, 4>{{0, 5, 0, 8}});
ASSERT_FALSE(packetizer.received_packet().empty());
// We skip the {{5}} packet (arbitrarily; either that packet or this one has
// to be skipped).
EXPECT_EQ(gsl::span<const char>(std::array<char, 3>{{1, 2, 3}}),
packetizer.received_packet());
packetizer.ParseData(std::array<char, 1>{{0}});
ASSERT_FALSE(packetizer.received_packet().empty());
EXPECT_EQ(gsl::span<const char>(std::array<char, 1>{{8}}),
packetizer.received_packet());
}
} // namespace jevois
} // namespace frc971