mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-09-14 20:35:33 +00:00
Add the framed BLE mOTA transport and companion control path, complete the full-profile build and runtime fixes, and document and test the OTA workflow.
252 lines
7.7 KiB
C++
252 lines
7.7 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, AdvertLocationDefaultsToStoredPrefs) {
|
|
NodePrefs prefs;
|
|
EXPECT_EQ(ADVERT_LOC_PREFS, prefs.advert_loc_policy);
|
|
EXPECT_EQ(ADVERT_LOC_PREFS, DEFAULT_ADVERT_LOC_POLICY);
|
|
|
|
// An explicit privacy choice remains persistent and is not replaced by
|
|
// the first-boot default when preferences are loaded.
|
|
prefs.advert_loc_policy = ADVERT_LOC_NONE;
|
|
MockPrintStream output;
|
|
ASSERT_TRUE(prefs.saveSerial(output));
|
|
|
|
std::string serialised(reinterpret_cast<const char*>(output.getBytes()),
|
|
output.getLength());
|
|
MockInputStream input(serialised.c_str());
|
|
NodePrefs loaded;
|
|
ASSERT_EQ(ADVERT_LOC_PREFS, loaded.advert_loc_policy);
|
|
ASSERT_TRUE(loaded.loadSerial(input));
|
|
EXPECT_EQ(ADVERT_LOC_NONE, loaded.advert_loc_policy);
|
|
}
|
|
|
|
TEST(NodePrefs, FemGainSettingsRoundTrip) {
|
|
NodePrefs saved;
|
|
saved.radio_fem_rxgain = 0;
|
|
saved.radio_fem_txgain = 1;
|
|
saved.usb_logging_enabled = 0;
|
|
saved.bridge_uart = 2;
|
|
|
|
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"));
|
|
EXPECT_NE(std::string::npos, serialised.find("usb_log:0"));
|
|
EXPECT_NE(std::string::npos, serialised.find("uart:2"));
|
|
|
|
MockInputStream input(serialised.c_str());
|
|
NodePrefs loaded;
|
|
loaded.radio_fem_rxgain = 1;
|
|
loaded.radio_fem_txgain = 0;
|
|
loaded.usb_logging_enabled = 1;
|
|
loaded.bridge_uart = 1;
|
|
|
|
ASSERT_TRUE(loaded.loadSerial(input));
|
|
EXPECT_EQ(0, loaded.radio_fem_rxgain);
|
|
EXPECT_EQ(1, loaded.radio_fem_txgain);
|
|
EXPECT_EQ(0, loaded.usb_logging_enabled);
|
|
EXPECT_EQ(2, loaded.bridge_uart);
|
|
}
|
|
|
|
|
|
// ── main ───────────────────────────────────────────────────────
|
|
|
|
int main(int argc, char** argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|