Stato di partenza prima dei fix. Il repo git era inizializzato a meta' (config.lock + objects/ mancante, zero commit): riparato. my_config.h (UID reale, tutti e 3 i sottoprogetti) escluso via .gitignore; aggiunto my_config.h.example. .pio/ escluso. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
155 lines
5.8 KiB
C++
155 lines
5.8 KiB
C++
// ============================================================
|
|
// Test on-device (Unity via USB, nessuna catena RF) per
|
|
// lib/EspNowCrsf: ring buffer, drop-oldest e filtro keep-alive.
|
|
//
|
|
// Usa test_injectRaw()/test_reset() (hook #ifdef UNIT_TEST,
|
|
// zero impatto sul firmware reale) per richiamare il vero
|
|
// onRecv() senza inizializzare WiFi/ESP-NOW: niente radio
|
|
// necessaria, solo la board collegata via USB.
|
|
// ============================================================
|
|
#include <Arduino.h>
|
|
#include <unity.h>
|
|
#include "EspNowCrsf.h"
|
|
|
|
void setUp(void) { EspNowCrsf::test_reset(); }
|
|
void tearDown(void) {}
|
|
|
|
// Costruisce un pacchetto "backpack": 8 byte header (contenuto
|
|
// ignorato dal parser) + payload CRSF grezzo.
|
|
static uint16_t buildPkt(uint8_t* out, const uint8_t* payload, uint16_t payloadLen) {
|
|
memset(out, 0xAA, 8); // header: contenuto qualsiasi, non ispezionato
|
|
memcpy(out + 8, payload, payloadLen);
|
|
return 8 + payloadLen;
|
|
}
|
|
|
|
void test_fifo_order_preserved(void) {
|
|
uint8_t pkt[16];
|
|
for (uint8_t i = 1; i <= 3; ++i) {
|
|
uint8_t payload[2] = {i, (uint8_t)(0xF0 + i)};
|
|
uint16_t n = buildPkt(pkt, payload, sizeof(payload));
|
|
EspNowCrsf::test_injectRaw(pkt, n);
|
|
}
|
|
|
|
uint8_t buf[64];
|
|
uint16_t len;
|
|
for (uint8_t i = 1; i <= 3; ++i) {
|
|
TEST_ASSERT_TRUE(EspNowCrsf::poll(buf, &len, sizeof(buf)));
|
|
TEST_ASSERT_EQUAL_UINT16(2, len);
|
|
TEST_ASSERT_EQUAL_UINT8(i, buf[0]);
|
|
TEST_ASSERT_EQUAL_UINT8(0xF0 + i, buf[1]);
|
|
}
|
|
TEST_ASSERT_FALSE(EspNowCrsf::poll(buf, &len, sizeof(buf))); // vuoto
|
|
}
|
|
|
|
void test_drop_oldest_when_full(void) {
|
|
const uint8_t INJECTED = 20; // ben oltre qualunque capacita' ragionevole del ring
|
|
uint8_t pkt[16];
|
|
for (uint8_t i = 1; i <= INJECTED; ++i) {
|
|
uint8_t payload[2] = {i, 0};
|
|
uint16_t n = buildPkt(pkt, payload, sizeof(payload));
|
|
EspNowCrsf::test_injectRaw(pkt, n);
|
|
}
|
|
|
|
uint8_t buf[64];
|
|
uint16_t len;
|
|
uint8_t polled = 0;
|
|
uint8_t lastMarker = 0;
|
|
while (EspNowCrsf::poll(buf, &len, sizeof(buf))) {
|
|
lastMarker = buf[0];
|
|
polled++;
|
|
}
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(INJECTED, EspNowCrsf::framesReceived());
|
|
TEST_ASSERT_TRUE_MESSAGE(EspNowCrsf::framesDropped() > 0, "con 20 frame iniettati ci si aspetta almeno un drop-oldest");
|
|
TEST_ASSERT_EQUAL_UINT32(INJECTED, polled + EspNowCrsf::framesDropped());
|
|
// drop-oldest: l'ultimo frame iniettato deve essere sempre presente
|
|
TEST_ASSERT_EQUAL_UINT8(INJECTED, lastMarker);
|
|
}
|
|
|
|
// ---- Filtro keep-alive: regressione del bug off-by-one --------
|
|
// Bug originale: `in[8]==0 && in[9]==0 && len>10` leggeva in[9] OOB
|
|
// quando len==9, e non scartava il keep-alive legittimo a len==10.
|
|
|
|
void test_keepalive_len10_is_filtered(void) {
|
|
uint8_t payload[2] = {0, 0}; // header(8) + 2 byte zero = len 10
|
|
uint8_t pkt[16];
|
|
uint16_t n = buildPkt(pkt, payload, sizeof(payload));
|
|
EspNowCrsf::test_injectRaw(pkt, n);
|
|
|
|
uint8_t buf[64];
|
|
uint16_t len;
|
|
TEST_ASSERT_FALSE_MESSAGE(EspNowCrsf::poll(buf, &len, sizeof(buf)),
|
|
"un keep-alive di len==10 deve essere scartato, non accodato");
|
|
TEST_ASSERT_EQUAL_UINT32(0, EspNowCrsf::framesReceived());
|
|
}
|
|
|
|
void test_len9_payload_not_oob_and_not_keepalive(void) {
|
|
uint8_t payload[1] = {0}; // header(8) + 1 byte zero = len 9
|
|
uint8_t pkt[16];
|
|
uint16_t n = buildPkt(pkt, payload, sizeof(payload));
|
|
EspNowCrsf::test_injectRaw(pkt, n); // non deve leggere OOB / non deve crashare
|
|
|
|
uint8_t buf[64];
|
|
uint16_t len;
|
|
TEST_ASSERT_TRUE_MESSAGE(EspNowCrsf::poll(buf, &len, sizeof(buf)),
|
|
"len==9 e' troppo corto per essere un keep-alive valido: va accodato");
|
|
TEST_ASSERT_EQUAL_UINT16(1, len);
|
|
}
|
|
|
|
void test_keepalive_with_extra_trailing_byte_is_filtered(void) {
|
|
uint8_t payload[3] = {0, 0, 0x7E}; // header(8) + 3 byte, primi due zero = len 11
|
|
uint8_t pkt[16];
|
|
uint16_t n = buildPkt(pkt, payload, sizeof(payload));
|
|
EspNowCrsf::test_injectRaw(pkt, n);
|
|
|
|
uint8_t buf[64];
|
|
uint16_t len;
|
|
TEST_ASSERT_FALSE_MESSAGE(EspNowCrsf::poll(buf, &len, sizeof(buf)),
|
|
"primi due byte payload a zero -> keep-alive, va scartato anche con byte extra");
|
|
}
|
|
|
|
void test_normal_frame_with_nonzero_first_byte_is_kept(void) {
|
|
uint8_t payload[3] = {0xC8, 0x05, 0x02}; // non e' un keep-alive (primo byte != 0)
|
|
uint8_t pkt[16];
|
|
uint16_t n = buildPkt(pkt, payload, sizeof(payload));
|
|
EspNowCrsf::test_injectRaw(pkt, n);
|
|
|
|
uint8_t buf[64];
|
|
uint16_t len;
|
|
TEST_ASSERT_TRUE(EspNowCrsf::poll(buf, &len, sizeof(buf)));
|
|
TEST_ASSERT_EQUAL_UINT16(3, len);
|
|
TEST_ASSERT_EQUAL_UINT8(0xC8, buf[0]);
|
|
}
|
|
|
|
// ---- Clamping a SLOT_SIZE (64) ---------------------------------
|
|
void test_oversized_frame_is_clamped(void) {
|
|
uint8_t pkt[8 + 100];
|
|
uint8_t payload[100];
|
|
for (uint16_t i = 0; i < sizeof(payload); ++i) payload[i] = (uint8_t)(i + 1); // mai 0 in [0]/[1]
|
|
uint16_t n = buildPkt(pkt, payload, sizeof(payload));
|
|
EspNowCrsf::test_injectRaw(pkt, n);
|
|
|
|
uint8_t buf[64];
|
|
uint16_t len;
|
|
TEST_ASSERT_TRUE(EspNowCrsf::poll(buf, &len, sizeof(buf)));
|
|
TEST_ASSERT_EQUAL_UINT16(64, len); // clampato a SLOT_SIZE
|
|
TEST_ASSERT_EQUAL_UINT8(1, buf[0]); // primi byte del payload originale preservati
|
|
TEST_ASSERT_EQUAL_UINT8(64, buf[63]);
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
delay(2000);
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_fifo_order_preserved);
|
|
RUN_TEST(test_drop_oldest_when_full);
|
|
RUN_TEST(test_keepalive_len10_is_filtered);
|
|
RUN_TEST(test_len9_payload_not_oob_and_not_keepalive);
|
|
RUN_TEST(test_keepalive_with_extra_trailing_byte_is_filtered);
|
|
RUN_TEST(test_normal_frame_with_nonzero_first_byte_is_kept);
|
|
RUN_TEST(test_oversized_frame_is_clamped);
|
|
UNITY_END();
|
|
}
|
|
|
|
void loop() {}
|