Files
HaloKeymind/test/test_config_serializer/test_config_serializer.cpp
T
agessaman e2aa7b98f9 feat(companion_radio): add external FEM gain preferences for RX and TX for companions
Introduced consistent preferences for external LoRa FEM RX and TX gain settings in NodePrefs. Updated companion MyMesh to apply these settings during initialization and transmission. Added unit tests to verify the round-trip serialization of these new preferences.
2026-08-10 11:59:26 -07:00

224 lines
6.6 KiB
C++

#include <gtest/gtest.h>
#include "helpers/ConfigSerializer.h"
class NativeFileSystem {
public:
void mkdir(const char*) { }
};
#define FILESYSTEM NativeFileSystem
#include "helpers/CommonCLI.h"
#undef FILESYSTEM
#define TEST_INT_S "56"
#define TEST_INT 56
#define TEST_FLOAT_S "-6.123"
#define TEST_FLOAT -6.1230f
#define TEST_DOUBLE_S "12.123456"
#define TEST_DOUBLE 12.123456
class MockInputStream : public Stream {
const char* _text;
int pos, len;
public:
MockInputStream(const char* text) : _text(text) { pos = 0; len = strlen(text); }
int available() override { return len - pos; }
int read() override { if (pos < len) { return _text[pos++]; } return -1; }
int peek() override { if (pos < len) { return _text[pos]; } return -1; }
};
class MockPrintStream : public Stream {
int len = 0;
uint8_t _buf[1024];
size_t printSigned(long long value) {
char text[24];
snprintf(text, sizeof(text), "%lld", value);
return Print::print(text);
}
size_t printUnsigned(unsigned long long value) {
char text[24];
snprintf(text, sizeof(text), "%llu", value);
return Print::print(text);
}
public:
size_t write(uint8_t b) override {
if (len < sizeof(_buf)) {
_buf[len++] = b;
return 1;
}
return 0;
}
size_t print(unsigned char v, int r) override { return printUnsigned(v); }
size_t print(int v, int r) override { return printSigned(v); }
size_t print(unsigned int v, int r) override { return printUnsigned(v); }
size_t print(long v, int r) override { return printSigned(v); }
size_t print(unsigned long v, int r) override { return printUnsigned(v); }
size_t print(long long v, int r) override { return printSigned(v); }
size_t print(unsigned long long v, int r) override { return printUnsigned(v); }
size_t print(double v, int p = 2) override {
char text[32];
snprintf(text, sizeof(text), "%.*f", p, v);
return Print::print(text);
}
int getLength() const { return len; }
const uint8_t* getBytes() const { return _buf; }
};
class TestStruct : public ConfigSerializer {
protected:
void structure() override {
def("age", age);
def("flags", flags);
def("name", name, sizeof(name));
}
public:
int32_t age;
char name[16];
uint8_t flags;
};
// ── saveSerial: basic ───────────────────────────────────────────────────────
TEST(ConfigSerializer, SaveSerial_Basic) {
MockPrintStream s;
TestStruct data;
data.age = TEST_INT;
data.flags = TEST_INT;
strcpy(data.name, "Scott");
bool success = data.saveSerial(s);
EXPECT_TRUE(success);
auto l = s.getLength();
const char* expect = "{age:" TEST_INT_S ",flags:" TEST_INT_S ",name:\"Scott\"}";
EXPECT_EQ(strlen(expect), l);
bool match = memcmp(s.getBytes(), expect, l) == 0;
EXPECT_TRUE(match);
}
TEST(ConfigSerializer, SaveSerial_EscChars) {
MockPrintStream s;
TestStruct data;
data.age = TEST_INT;
data.flags = TEST_INT;
strcpy(data.name, "\"Scott\"\n");
bool success = data.saveSerial(s);
EXPECT_TRUE(success);
auto l = s.getLength();
const char* expect = "{age:" TEST_INT_S ",flags:" TEST_INT_S ",name:\"\\\"Scott\\\"\\n\"}";
EXPECT_EQ(strlen(expect), l);
bool match = memcmp(s.getBytes(), expect, l) == 0;
EXPECT_TRUE(match);
}
// ── loadSerial: basic ───────────────────────────────────────────────────────
TEST(ConfigSerializer, LoadSerial_Basic) {
MockInputStream s("{age:" TEST_INT_S ",flags:" TEST_INT_S ",name:\"Scott\"}");
TestStruct data;
bool success = data.loadSerial(s);
EXPECT_TRUE(success);
EXPECT_EQ(TEST_INT, data.age);
EXPECT_EQ(TEST_INT, data.flags);
bool match = strcmp("Scott", data.name) == 0;
EXPECT_TRUE(match);
}
TEST(ConfigSerializer, LoadSerial_HandleWhitespace) {
MockInputStream s(" { age: " TEST_INT_S " , flags: " TEST_INT_S " , name: \"Scott\" } ");
TestStruct data;
bool success = data.loadSerial(s);
EXPECT_TRUE(success);
EXPECT_EQ(TEST_INT, data.age);
EXPECT_EQ(TEST_INT, data.flags);
bool match = strcmp("Scott", data.name) == 0;
EXPECT_TRUE(match);
}
TEST(ConfigSerializer, LoadSerial_EscChars) {
MockInputStream s("{age:" TEST_INT_S ",flags:" TEST_INT_S ",name:\"\\\"Scott\\\"\\n\"}");
TestStruct data;
bool success = data.loadSerial(s);
EXPECT_TRUE(success);
bool match = strcmp("\"Scott\"\n", data.name) == 0;
EXPECT_TRUE(match);
}
TEST(ConfigSerializer, LoadSerial_UnmatchedBraces) {
MockInputStream s("{age:" TEST_INT_S ",flags:" TEST_INT_S ",name:\"Scott\"");
TestStruct data;
bool success = data.loadSerial(s);
EXPECT_FALSE(success);
}
TEST(ConfigSerializer, LoadSerial_MissingCommas) {
MockInputStream s("{age:" TEST_INT_S " flags:" TEST_INT_S " name:\"Scott\"}");
TestStruct data;
bool success = data.loadSerial(s);
EXPECT_FALSE(success);
}
TEST(ConfigSerializer, LoadSerial_IgnoreUnknowns) {
MockInputStream s("{age:" TEST_INT_S ",xxx:" TEST_INT_S ",name:\"Scott\"}");
TestStruct data;
data.flags = 1;
// should ignore the 'xxx' property
bool success = data.loadSerial(s);
EXPECT_TRUE(success);
EXPECT_EQ(TEST_INT, data.age);
EXPECT_EQ(1, data.flags); // flags should be unmodified
bool match = strcmp("Scott", data.name) == 0;
EXPECT_TRUE(match);
}
TEST(NodePrefs, FemGainSettingsRoundTrip) {
NodePrefs saved;
saved.radio_fem_rxgain = 0;
saved.radio_fem_txgain = 1;
MockPrintStream output;
ASSERT_TRUE(saved.saveSerial(output));
std::string serialised(reinterpret_cast<const char*>(output.getBytes()), output.getLength());
EXPECT_NE(std::string::npos, serialised.find("fem_rxgain:0"));
EXPECT_NE(std::string::npos, serialised.find("fem_txgain:1"));
MockInputStream input(serialised.c_str());
NodePrefs loaded;
loaded.radio_fem_rxgain = 1;
loaded.radio_fem_txgain = 0;
ASSERT_TRUE(loaded.loadSerial(input));
EXPECT_EQ(0, loaded.radio_fem_rxgain);
EXPECT_EQ(1, loaded.radio_fem_txgain);
}
// ── main ───────────────────────────────────────────────────────
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}