microReticulum/test/test_msgpack/test_main.cpp

333 lines
11 KiB
C++
Raw Permalink Normal View History

2025-08-25 14:42:15 -06:00
#include <unity.h>
#define MSGPACK_DEBUGLOG_ENABLE 0
#define DEBUGLOG_DEFAULT_LOG_LEVEL_TRACE
2025-08-25 14:42:15 -06:00
#include <MsgPack.h>
#include <ArduinoJson.h>
#include "Bytes.h"
2025-08-25 14:42:15 -06:00
#include <string.h>
#include <stdint.h>
// Use MsgPack's native binary type
using BinaryBuffer = MsgPack::bin_t<uint8_t>;
void test_msgpack_array_packing(void) {
// Test data setup
float testFloat = 3.14159f;
// Create binary buffers using MsgPack's bin_t type
BinaryBuffer buffer1;
buffer1.resize(5);
memcpy(buffer1.data(), "Hello", 5);
BinaryBuffer buffer2;
buffer2.resize(5);
memcpy(buffer2.data(), "World", 5);
// Pack the array using MsgPack
MsgPack::Packer packer;
packer.to_array(testFloat, buffer1, buffer2);
// Get the packed data
size_t packed_size = packer.size();
const uint8_t* packed_data = packer.data();
// Basic validation - should have data
TEST_ASSERT_GREATER_THAN(0, packed_size);
TEST_ASSERT_NOT_NULL(packed_data);
// Expected MsgPack format:
// - Array header (fixarray with 3 elements): 0x93
// - Float (float32): 0xca + 4 bytes for 3.14159f
// - Binary 1 (bin8): 0xc4 + length(5) + "Hello"
// - Binary 2 (bin8): 0xc4 + length(5) + "World"
// Verify array header - fixarray with 3 elements
TEST_ASSERT_EQUAL_HEX8(0x93, packed_data[0]);
// Verify float marker (float32)
TEST_ASSERT_EQUAL_HEX8(0xca, packed_data[1]);
// Extract and verify the float value by unpacking
MsgPack::Unpacker unpacker;
unpacker.feed(packed_data, packed_size);
float unpacked_float;
MsgPack::bin_t<uint8_t> unpacked_buffer1;
MsgPack::bin_t<uint8_t> unpacked_buffer2;
// Unpack the array
bool unpack_success = unpacker.from_array(unpacked_float, unpacked_buffer1, unpacked_buffer2);
TEST_ASSERT_TRUE(unpack_success);
// Verify unpacked values match original data
TEST_ASSERT_EQUAL_FLOAT(testFloat, unpacked_float);
// Verify binary buffers
TEST_ASSERT_EQUAL_size_t(buffer1.size(), unpacked_buffer1.size());
TEST_ASSERT_EQUAL_MEMORY(buffer1.data(), unpacked_buffer1.data(), buffer1.size());
TEST_ASSERT_EQUAL_size_t(buffer2.size(), unpacked_buffer2.size());
TEST_ASSERT_EQUAL_MEMORY(buffer2.data(), unpacked_buffer2.data(), buffer2.size());
// Additional validation: verify total packed size makes sense
// Array header(1) + float marker(1) + float data(4) +
// bin1 header(1) + bin1 length(1) + bin1 data(5) +
// bin2 header(1) + bin2 length(1) + bin2 data(5) = 20 bytes
TEST_ASSERT_EQUAL_size_t(20, packed_size);
// Verify specific binary markers for the Hello and World strings
// After array header(1) + float marker(1) + float data(4) = offset 6
TEST_ASSERT_EQUAL_HEX8(0xc4, packed_data[6]); // bin8 marker for first buffer
TEST_ASSERT_EQUAL_HEX8(5, packed_data[7]); // length of "Hello"
TEST_ASSERT_EQUAL_MEMORY("Hello", &packed_data[8], 5);
// After first buffer: offset 6 + 1 + 1 + 5 = 13
TEST_ASSERT_EQUAL_HEX8(0xc4, packed_data[13]); // bin8 marker for second buffer
TEST_ASSERT_EQUAL_HEX8(5, packed_data[14]); // length of "World"
TEST_ASSERT_EQUAL_MEMORY("World", &packed_data[15], 5);
}
void test_msgpack_empty_buffer(void) {
// Test with empty buffer
float testFloat = -42.0f;
BinaryBuffer emptyBuffer; // Empty buffer
BinaryBuffer normalBuffer;
normalBuffer.resize(4);
memcpy(normalBuffer.data(), "Test", 4);
MsgPack::Packer packer;
packer.to_array(testFloat, emptyBuffer, normalBuffer);
size_t packed_size = packer.size();
const uint8_t* packed_data = packer.data();
TEST_ASSERT_GREATER_THAN(0, packed_size);
TEST_ASSERT_NOT_NULL(packed_data);
// Verify by unpacking
MsgPack::Unpacker unpacker;
unpacker.feed(packed_data, packed_size);
float unpacked_float;
MsgPack::bin_t<uint8_t> unpacked_empty;
MsgPack::bin_t<uint8_t> unpacked_normal;
bool unpack_success = unpacker.from_array(unpacked_float, unpacked_empty, unpacked_normal);
TEST_ASSERT_TRUE(unpack_success);
TEST_ASSERT_EQUAL_FLOAT(testFloat, unpacked_float);
TEST_ASSERT_EQUAL_size_t(0, unpacked_empty.size());
TEST_ASSERT_EQUAL_size_t(4, unpacked_normal.size());
TEST_ASSERT_EQUAL_MEMORY("Test", unpacked_normal.data(), 4);
}
void test_msgpack_binary_content(void) {
// Test with binary data (not just strings)
float testFloat = 1.0f;
uint8_t binaryData1[] = {0x00, 0xFF, 0xAA, 0x55};
uint8_t binaryData2[] = {0x01, 0x02, 0x03};
BinaryBuffer buffer1;
buffer1.resize(sizeof(binaryData1));
memcpy(buffer1.data(), binaryData1, sizeof(binaryData1));
BinaryBuffer buffer2;
buffer2.resize(sizeof(binaryData2));
memcpy(buffer2.data(), binaryData2, sizeof(binaryData2));
MsgPack::Packer packer;
packer.to_array(testFloat, buffer1, buffer2);
size_t packed_size = packer.size();
const uint8_t* packed_data = packer.data();
TEST_ASSERT_GREATER_THAN(0, packed_size);
TEST_ASSERT_NOT_NULL(packed_data);
// Verify by unpacking
MsgPack::Unpacker unpacker;
unpacker.feed(packed_data, packed_size);
float unpacked_float;
MsgPack::bin_t<uint8_t> unpacked_buffer1;
MsgPack::bin_t<uint8_t> unpacked_buffer2;
bool unpack_success = unpacker.from_array(unpacked_float, unpacked_buffer1, unpacked_buffer2);
TEST_ASSERT_TRUE(unpack_success);
TEST_ASSERT_EQUAL_FLOAT(testFloat, unpacked_float);
TEST_ASSERT_EQUAL_size_t(4, unpacked_buffer1.size());
TEST_ASSERT_EQUAL_MEMORY(binaryData1, unpacked_buffer1.data(), 4);
TEST_ASSERT_EQUAL_size_t(3, unpacked_buffer2.size());
TEST_ASSERT_EQUAL_MEMORY(binaryData2, unpacked_buffer2.data(), 3);
}
void test_pack_bytes_array_python() {
static const unsigned char binary[] = {
0x93, 0xcb, 0x40, 0x5e, 0xdd, 0x2f, 0x1a, 0x9f,
0xbe, 0x77, 0xc4, 0x06, 0x66, 0x69, 0x72, 0x73,
0x74, 0xab, 0xc4, 0x07, 0x73, 0x65, 0x63, 0x6f,
0x6e, 0x64, 0xcd,
};
double time = 123.456;
// Test that array containing bytes matches python equivalent
const RNS::Bytes one("first\xab");
const RNS::Bytes two("second\xcd");
MsgPack::Packer packer;
// CBA NOTE: MsgPack appears to use Bytes cast operator to std::vector<uint8_t> for serializing Bytes as binary
packer.to_array(time, one, two);
RNS::Bytes data(packer.data(), packer.size());
TEST_ASSERT_EQUAL_size_t(27, packer.size());
TEST_ASSERT_EQUAL_MEMORY(binary, packer.data(), packer.size());
}
void test_unpack_bytes_array_python() {
static const unsigned char binary[] = {
0x93, 0xcb, 0x40, 0x5e, 0xdd, 0x2f, 0x1a, 0x9f,
0xbe, 0x77, 0xc4, 0x06, 0x66, 0x69, 0x72, 0x73,
0x74, 0xab, 0xc4, 0x07, 0x73, 0x65, 0x63, 0x6f,
0x6e, 0x64, 0xcd,
};
MsgPack::Unpacker unpacker;
unpacker.feed(binary, sizeof(binary));
double time;
// CBA NOTE: Can't currently deserialize directly into Bytes
//RNS::Bytes one;
//RNS::Bytes two;
//unpacker.from_array(time, one, two);
MsgPack::bin_t<uint8_t> bin_one;
MsgPack::bin_t<uint8_t> bin_two;
unpacker.from_array(time, bin_one, bin_two);
RNS::Bytes one(bin_one);
RNS::Bytes two(bin_two);
TEST_ASSERT_EQUAL_FLOAT(123.456, time);
TEST_ASSERT_EQUAL_MEMORY("first\xab", one.data(), one.size());
TEST_ASSERT_EQUAL_MEMORY("second\xcd", two.data(), two.size());
}
void test_pack_float_python() {
static const unsigned char binary[] = {
0xcb, 0x40, 0x5e, 0xdd, 0x2f, 0x1a, 0x9f, 0xbe,
0x77
};
double time = 123.456;
MsgPack::Packer packer;
packer.serialize(time);
RNS::Bytes data(packer.data(), packer.size());
TEST_ASSERT_EQUAL_size_t(9, packer.size());
TEST_ASSERT_EQUAL_MEMORY(binary, packer.data(), packer.size());
}
void test_unpack_float_python() {
static const unsigned char binary[] = {
0xcb, 0x40, 0x5e, 0xdd, 0x2f, 0x1a, 0x9f, 0xbe,
0x77
};
MsgPack::Unpacker unpacker;
unpacker.feed(binary, sizeof(binary));
double time;
unpacker.deserialize(time);
TEST_ASSERT_EQUAL_FLOAT(123.456, time);
}
void test_json_pack_bytes_array_python() {
static const unsigned char binary[] = {
0x93, 0xcb, 0x40, 0x5e, 0xdd, 0x2f, 0x1a, 0x9f,
0xbe, 0x77, 0xc4, 0x06, 0x66, 0x69, 0x72, 0x73,
0x74, 0xab, 0xc4, 0x07, 0x73, 0x65, 0x63, 0x6f,
0x6e, 0x64, 0xcd,
};
double time = 123.456;
// Test that array containing bytes matches python equivalent
const RNS::Bytes one("first\xab");
const RNS::Bytes two("second\xcd");
// CBA NOTE: ArduinJson AJ MsgPack appears to also use Bytes cast operator to std::vector<uint8_t> for serializing Bytes
// but serializes it as an array instead of as binary, so can't currently serialize Bytes directly yet.
// CBA NOTE: Actually AJ MsgPack probably uses the same Json (de)serializers defined in Bytes as AJ Json uses, so need a way
// to distinguish MsgPack from JSON in (de)serializers to know whether to store as binary or as string represenation!!!
MsgPackBinary bin_one(one.data(), one.size());
MsgPackBinary bin_two(two.data(), two.size());
JsonDocument doc;
doc.add(time);
doc.add(bin_one);
doc.add(bin_two);
RNS::Bytes packed;
size_t len = serializeMsgPack(doc, packed.writable(256), 256);
packed.resize(len);
TEST_ASSERT_EQUAL_size_t(27, packed.size());
TEST_ASSERT_EQUAL_MEMORY(binary, packed.data(), packed.size());
}
void test_json_unpack_bytes_array_python() {
static const unsigned char binary[] = {
0x93, 0xcb, 0x40, 0x5e, 0xdd, 0x2f, 0x1a, 0x9f,
0xbe, 0x77, 0xc4, 0x06, 0x66, 0x69, 0x72, 0x73,
0x74, 0xab, 0xc4, 0x07, 0x73, 0x65, 0x63, 0x6f,
0x6e, 0x64, 0xcd,
};
JsonDocument doc;
deserializeMsgPack(doc, binary, sizeof(binary));
double time = doc[0];
// CBA NOTE: Can't currently deserialize directly into Bytes
//RNS::Bytes one;
//RNS::Bytes two;
MsgPackBinary bin_one = doc[1];
MsgPackBinary bin_two = doc[2];
RNS::Bytes one(bin_one.data(), bin_one.size());
RNS::Bytes two(bin_two.data(), bin_two.size());
TEST_ASSERT_EQUAL_FLOAT(123.456, time);
TEST_ASSERT_EQUAL_MEMORY("first\xab", one.data(), one.size());
TEST_ASSERT_EQUAL_MEMORY("second\xcd", two.data(), two.size());
}
void setUp(void) {
// set stuff up here before each test
}
void tearDown(void) {
// clean stuff up here after each test
}
2025-08-25 14:42:15 -06:00
int runUnityTests(void) {
UNITY_BEGIN();
RUN_TEST(test_msgpack_array_packing);
RUN_TEST(test_msgpack_empty_buffer);
RUN_TEST(test_msgpack_binary_content);
RUN_TEST(test_pack_bytes_array_python);
RUN_TEST(test_unpack_bytes_array_python);
RUN_TEST(test_pack_float_python);
RUN_TEST(test_unpack_float_python);
RUN_TEST(test_json_pack_bytes_array_python);
RUN_TEST(test_json_unpack_bytes_array_python);
2025-08-25 14:42:15 -06:00
return UNITY_END();
}
// For native dev-platform or for some embedded frameworks
int main(void) {
return runUnityTests();
}
#ifdef ARDUINO
// For Arduino framework
void setup() {
// Wait ~2 seconds before the Unity test runner
// establishes connection with a board Serial interface
delay(2000);
runUnityTests();
}
void loop() {}
#endif
// For ESP-IDF framework
void app_main() {
runUnityTests();
}