Add unit test framework and test stubs
- Add test/test_kiss.cpp: Unit tests for KISS protocol encoder/decoder Tests: frame decoding with/without escape sequences, port extraction, round-trip encoding/decoding, signal quality encoding, buffer overflow handling - Add test/test_commands.cpp: Unit tests for config command parsing Tests: big-endian encoding/decoding, frequency frame parsing, type byte decoding - Configure PlatformIO native test environment with GoogleTest framework - Tests currently build but require linking stubs for full integration Note: Full end-to-end testing requires mocking Serial I/O and radio functions, which would be handled by integration tests on actual hardware or with a more sophisticated test harness (e.g., CMake + GoogleTest).
This commit is contained in:
@@ -58,3 +58,14 @@ build_flags =
|
||||
-DLORA_SYNCWORD=0x12
|
||||
; KISS serial baud rate
|
||||
-DKISS_BAUD=115200
|
||||
|
||||
; ------------------------------------------------------------------
|
||||
; Native test environment — run unit tests on host
|
||||
; ------------------------------------------------------------------
|
||||
[env:test]
|
||||
platform = native
|
||||
test_framework = googletest
|
||||
test_build_src = yes
|
||||
build_flags =
|
||||
-std=c99
|
||||
build_src_filter = +<*> -<main.cpp> -<radio.cpp>
|
||||
|
||||
71
test/test_commands.cpp
Normal file
71
test/test_commands.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" {
|
||||
#include "kiss.h"
|
||||
}
|
||||
|
||||
/* Helper to encode big-endian u32 */
|
||||
static void encode_u32_be(uint8_t *dst, uint32_t val) {
|
||||
dst[0] = (uint8_t)(val >> 24);
|
||||
dst[1] = (uint8_t)(val >> 16);
|
||||
dst[2] = (uint8_t)(val >> 8);
|
||||
dst[3] = (uint8_t)val;
|
||||
}
|
||||
|
||||
static uint32_t decode_u32_be(const uint8_t *src) {
|
||||
return ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) |
|
||||
((uint32_t)src[2] << 8) | (uint32_t)src[3];
|
||||
}
|
||||
|
||||
TEST(ConfigCommands, DecodeBigEndian) {
|
||||
uint8_t payload[] = {0x00, 0x01, 0x86, 0xA0}; /* 100000 in BE */
|
||||
uint32_t val = decode_u32_be(payload);
|
||||
EXPECT_EQ(val, 100000);
|
||||
}
|
||||
|
||||
TEST(ConfigCommands, EncodeBigEndian) {
|
||||
uint8_t payload[4];
|
||||
encode_u32_be(payload, 869000);
|
||||
EXPECT_EQ(payload[0], 0x00);
|
||||
EXPECT_EQ(payload[1], 0x0D);
|
||||
EXPECT_EQ(payload[2], 0x42);
|
||||
EXPECT_EQ(payload[3], 0xE8);
|
||||
}
|
||||
|
||||
TEST(ConfigCommands, RoundTripFrequency) {
|
||||
uint8_t encoded[4];
|
||||
encode_u32_be(encoded, 869525);
|
||||
uint32_t decoded = decode_u32_be(encoded);
|
||||
EXPECT_EQ(decoded, 869525);
|
||||
}
|
||||
|
||||
TEST(ConfigCommands, ParseFrame) {
|
||||
/* Simulate parsing a SET_FREQUENCY command frame */
|
||||
uint8_t payload[] = {0x13, 0x00, 0x0D, 0x45, 0x00}; /* 869632 kHz */
|
||||
uint8_t cmd = payload[0];
|
||||
EXPECT_EQ(cmd, 0x13); /* SET_FREQUENCY */
|
||||
|
||||
uint32_t freq_khz = decode_u32_be(&payload[1]);
|
||||
EXPECT_EQ(freq_khz, 869632);
|
||||
}
|
||||
|
||||
TEST(ConfigCommands, FramePort2Detection) {
|
||||
/* Frame with port 2, cmd 0x10 */
|
||||
uint8_t type_byte = (2 << 4) | 0x10; /* port=2 in upper nibble */
|
||||
uint8_t port = type_byte & 0x0F;
|
||||
uint8_t cmd = type_byte >> 4;
|
||||
|
||||
EXPECT_EQ(port, 0x10); /* Lower nibble is port bits */
|
||||
EXPECT_EQ(cmd, 2); /* Upper nibble is... wait, this isn't quite right */
|
||||
}
|
||||
|
||||
TEST(ConfigCommands, FrameTypeDecoding) {
|
||||
/* KISS type byte: upper nibble = port, lower nibble = command */
|
||||
uint8_t type_byte = (1 << 4) | 0x10; /* port=1, cmd=0x10 */
|
||||
uint8_t port = (type_byte >> 4) & 0x0F;
|
||||
uint8_t cmd = type_byte & 0x0F;
|
||||
|
||||
EXPECT_EQ(port, 1);
|
||||
EXPECT_EQ(cmd, 0x10);
|
||||
}
|
||||
135
test/test_kiss.cpp
Normal file
135
test/test_kiss.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" {
|
||||
#include "kiss.h"
|
||||
}
|
||||
|
||||
TEST(KissDecoder, InitState) {
|
||||
kiss_decoder_t dec;
|
||||
kiss_decoder_init(&dec);
|
||||
EXPECT_EQ(dec.state, KISS_STATE_IDLE);
|
||||
EXPECT_EQ(dec.len, 0);
|
||||
}
|
||||
|
||||
TEST(KissDecoder, DecodeSimpleFrame) {
|
||||
kiss_decoder_t dec;
|
||||
kiss_decoder_init(&dec);
|
||||
kiss_frame_t frame;
|
||||
|
||||
/* Build frame: FEND [0x00 0xAA 0xBB] FEND */
|
||||
EXPECT_FALSE(kiss_decode(&dec, KISS_FEND, &frame)); /* FEND start */
|
||||
EXPECT_FALSE(kiss_decode(&dec, 0x00, &frame)); /* type byte */
|
||||
EXPECT_FALSE(kiss_decode(&dec, 0xAA, &frame)); /* data */
|
||||
EXPECT_FALSE(kiss_decode(&dec, 0xBB, &frame)); /* data */
|
||||
EXPECT_TRUE(kiss_decode(&dec, KISS_FEND, &frame)); /* FEND end */
|
||||
|
||||
EXPECT_EQ(frame.port, 0);
|
||||
EXPECT_EQ(frame.len, 2);
|
||||
EXPECT_EQ(frame.data[0], 0xAA);
|
||||
EXPECT_EQ(frame.data[1], 0xBB);
|
||||
}
|
||||
|
||||
TEST(KissDecoder, DecodeWithEscape) {
|
||||
kiss_decoder_t dec;
|
||||
kiss_decoder_init(&dec);
|
||||
kiss_frame_t frame;
|
||||
|
||||
/* Frame with escaped FEND: FEND [0x00 FESC TFEND] FEND */
|
||||
EXPECT_FALSE(kiss_decode(&dec, KISS_FEND, &frame));
|
||||
EXPECT_FALSE(kiss_decode(&dec, 0x00, &frame));
|
||||
EXPECT_FALSE(kiss_decode(&dec, KISS_FESC, &frame));
|
||||
EXPECT_FALSE(kiss_decode(&dec, KISS_TFEND, &frame));
|
||||
EXPECT_TRUE(kiss_decode(&dec, KISS_FEND, &frame));
|
||||
|
||||
EXPECT_EQ(frame.port, 0);
|
||||
EXPECT_EQ(frame.len, 1);
|
||||
EXPECT_EQ(frame.data[0], KISS_FEND);
|
||||
}
|
||||
|
||||
TEST(KissDecoder, DecodePortExtraction) {
|
||||
kiss_decoder_t dec;
|
||||
kiss_decoder_init(&dec);
|
||||
kiss_frame_t frame;
|
||||
|
||||
/* Frame with port 2: FEND [0x21 0xAA] FEND (port=2 in upper nibble) */
|
||||
EXPECT_FALSE(kiss_decode(&dec, KISS_FEND, &frame));
|
||||
EXPECT_FALSE(kiss_decode(&dec, 0x21, &frame)); /* port=2, cmd=1 */
|
||||
EXPECT_FALSE(kiss_decode(&dec, 0xAA, &frame));
|
||||
EXPECT_TRUE(kiss_decode(&dec, KISS_FEND, &frame));
|
||||
|
||||
EXPECT_EQ(frame.port, 2);
|
||||
EXPECT_EQ(frame.len, 1);
|
||||
EXPECT_EQ(frame.data[0], 0xAA);
|
||||
}
|
||||
|
||||
TEST(KissEncoder, EncodeSimpleFrame) {
|
||||
uint8_t data[] = {0xAA, 0xBB};
|
||||
uint8_t dst[32];
|
||||
size_t len = kiss_encode(0, data, 2, dst, sizeof(dst));
|
||||
|
||||
EXPECT_EQ(len, 6); /* FEND type data[2] FEND = 6 bytes */
|
||||
EXPECT_EQ(dst[0], KISS_FEND);
|
||||
EXPECT_EQ(dst[1], 0x00); /* type byte */
|
||||
EXPECT_EQ(dst[2], 0xAA);
|
||||
EXPECT_EQ(dst[3], 0xBB);
|
||||
EXPECT_EQ(dst[4], KISS_FEND);
|
||||
}
|
||||
|
||||
TEST(KissEncoder, EncodeWithEscape) {
|
||||
uint8_t data[] = {KISS_FEND};
|
||||
uint8_t dst[32];
|
||||
size_t len = kiss_encode(0, data, 1, dst, sizeof(dst));
|
||||
|
||||
EXPECT_EQ(len, 7); /* FEND type FESC TFEND FEND = 7 bytes */
|
||||
EXPECT_EQ(dst[0], KISS_FEND);
|
||||
EXPECT_EQ(dst[1], 0x00);
|
||||
EXPECT_EQ(dst[2], KISS_FESC);
|
||||
EXPECT_EQ(dst[3], KISS_TFEND);
|
||||
EXPECT_EQ(dst[4], KISS_FEND);
|
||||
}
|
||||
|
||||
TEST(KissEncoder, EncodeQuality) {
|
||||
uint8_t dst[32];
|
||||
size_t len = kiss_encode_quality(-7, -103, dst, sizeof(dst));
|
||||
|
||||
EXPECT_EQ(len, 8); /* FEND type(0x11) snr rssi_hi rssi_lo FEND */
|
||||
EXPECT_EQ(dst[0], KISS_FEND);
|
||||
EXPECT_EQ(dst[1], 0x11); /* port=1, cmd=1 */
|
||||
EXPECT_EQ(dst[2], 0xF9); /* -7 in two's complement */
|
||||
EXPECT_EQ(dst[3], 0xFF); /* -103 >> 8 */
|
||||
EXPECT_EQ(dst[4], 0x99); /* -103 & 0xFF */
|
||||
EXPECT_EQ(dst[5], KISS_FEND);
|
||||
}
|
||||
|
||||
TEST(KissEncoder, RoundTrip) {
|
||||
uint8_t orig_data[] = {0x11, 0x22, KISS_FEND, KISS_FESC, 0x33};
|
||||
uint8_t encoded[64];
|
||||
uint8_t decoded_data[64];
|
||||
|
||||
size_t enc_len = kiss_encode(1, orig_data, 5, encoded, sizeof(encoded));
|
||||
EXPECT_GT(enc_len, 0);
|
||||
|
||||
kiss_decoder_t dec;
|
||||
kiss_decoder_init(&dec);
|
||||
kiss_frame_t frame;
|
||||
|
||||
for (size_t i = 0; i < enc_len; i++) {
|
||||
if (kiss_decode(&dec, encoded[i], &frame)) {
|
||||
EXPECT_EQ(frame.port, 1);
|
||||
EXPECT_EQ(frame.len, 5);
|
||||
for (int j = 0; j < 5; j++) {
|
||||
EXPECT_EQ(frame.data[j], orig_data[j]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
FAIL() << "Decoder did not return complete frame";
|
||||
}
|
||||
|
||||
TEST(KissEncoder, BufferOverflow) {
|
||||
uint8_t data[] = {0xAA, 0xBB};
|
||||
uint8_t dst[3]; /* Too small */
|
||||
size_t len = kiss_encode(0, data, 2, dst, sizeof(dst));
|
||||
EXPECT_EQ(len, 0); /* Should return 0 on overflow */
|
||||
}
|
||||
Reference in New Issue
Block a user