mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 19:02:00 +00:00
* DynamicConfigSerializer fixes, and unit tests
This commit is contained in:
@@ -171,6 +171,7 @@ build_src_filter =
|
|||||||
+<../src/Utils.cpp>
|
+<../src/Utils.cpp>
|
||||||
+<../src/Packet.cpp>
|
+<../src/Packet.cpp>
|
||||||
+<../src/helpers/ConfigSerializer.cpp>
|
+<../src/helpers/ConfigSerializer.cpp>
|
||||||
|
+<../src/helpers/DynamicConfigSerializer.cpp>
|
||||||
lib_deps =
|
lib_deps =
|
||||||
google/googletest @ 1.17.0
|
google/googletest @ 1.17.0
|
||||||
|
|
||||||
|
|||||||
@@ -104,13 +104,13 @@ private:
|
|||||||
float getBandwidth() const override { return _parent->bw; }
|
float getBandwidth() const override { return _parent->bw; }
|
||||||
void setBandwidth(float bw) override { _parent->bw = bw; markDirty(); }
|
void setBandwidth(float bw) override { _parent->bw = bw; markDirty(); }
|
||||||
uint8_t getSpreadFactor() const override { return _parent->sf; }
|
uint8_t getSpreadFactor() const override { return _parent->sf; }
|
||||||
void setSpreadFactor(uint8_t sf) override { _parent->sf; markDirty(); }
|
void setSpreadFactor(uint8_t sf) override { _parent->sf = sf; markDirty(); }
|
||||||
uint8_t getCodingRate() const override { return _parent->cr; }
|
uint8_t getCodingRate() const override { return _parent->cr; }
|
||||||
void setCodingRate(uint8_t cr) override { _parent->cr = cr; markDirty(); }
|
void setCodingRate(uint8_t cr) override { _parent->cr = cr; markDirty(); }
|
||||||
float getAirtimeFactor() const override { return _parent->airtime_factor; }
|
float getAirtimeFactor() const override { return _parent->airtime_factor; }
|
||||||
void setAirtimeFactor(float af) override { _parent->airtime_factor = af; markDirty(); }
|
void setAirtimeFactor(float af) override { _parent->airtime_factor = af; markDirty(); }
|
||||||
bool isCadEnabled() const override { return _parent->cad_enabled; }
|
bool isCadEnabled() const override { return _parent->cad_enabled; }
|
||||||
void setCadEnabled(bool en) override { _parent->cad_enabled; markDirty(); }
|
void setCadEnabled(bool en) override { _parent->cad_enabled = en; markDirty(); }
|
||||||
uint8_t getIntThresh() const override { return _parent->interference_threshold; }
|
uint8_t getIntThresh() const override { return _parent->interference_threshold; }
|
||||||
void setIntThresh(uint8_t t) override { _parent->interference_threshold = t; markDirty(); }
|
void setIntThresh(uint8_t t) override { _parent->interference_threshold = t; markDirty(); }
|
||||||
uint8_t getRxGain() const override { return _parent->rx_boosted_gain; }
|
uint8_t getRxGain() const override { return _parent->rx_boosted_gain; }
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class ConfigSerializer {
|
|||||||
int8_t _depth;
|
int8_t _depth;
|
||||||
bool _dirty = false;
|
bool _dirty = false;
|
||||||
|
|
||||||
|
protected:
|
||||||
enum OP { READ, WRITE };
|
enum OP { READ, WRITE };
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
@@ -39,13 +40,14 @@ class ConfigSerializer {
|
|||||||
const char* getToken() const { return rd_buf; }
|
const char* getToken() const { return rd_buf; }
|
||||||
bool keyMatch(int8_t depth, const char* key) { return strcmp(key, _keys[depth]) == 0; }
|
bool keyMatch(int8_t depth, const char* key) { return strcmp(key, _keys[depth]) == 0; }
|
||||||
void setKey(uint8_t depth, const char* key) { strcpy(_keys[depth], key); }
|
void setKey(uint8_t depth, const char* key) { strcpy(_keys[depth], key); }
|
||||||
|
const char* getKey(uint8_t depth) { return _keys[depth]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
Context* _context = NULL;
|
Context* _context = NULL;
|
||||||
|
int8_t getDepth() const { return _depth; }
|
||||||
|
|
||||||
void writeComma();
|
void writeComma();
|
||||||
|
|
||||||
protected:
|
|
||||||
ConfigSerializer() { }
|
ConfigSerializer() { }
|
||||||
|
|
||||||
void def(const char* key, char* value, size_t max_len); // max_len inclusive of null
|
void def(const char* key, char* value, size_t max_len); // max_len inclusive of null
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#define KEY_SEP_CHAR ':'
|
#define KEY_SEP_CHAR ':'
|
||||||
#define KEY_SEP_STR ":"
|
#define KEY_SEP_STR ":"
|
||||||
|
|
||||||
bool DynamicConfigSerializer::setByKey(const char* key, const char* value) {
|
bool DynamicConfigSerializer::setByKeyPrv(const char* key, const char* value) {
|
||||||
if (_fallback && _fallback->setByKey(key, value)) return true;
|
if (_fallback && _fallback->setByKey(key, value)) return true;
|
||||||
|
|
||||||
// TODO: guard for bad chars (':' or '|')
|
// TODO: guard for bad chars (':' or '|')
|
||||||
@@ -34,16 +34,26 @@ bool DynamicConfigSerializer::setByKey(const char* key, const char* value) {
|
|||||||
}
|
}
|
||||||
// now append new key/value (if it fits)
|
// now append new key/value (if it fits)
|
||||||
if (strlen(new_config) + strlen(key) + strlen(value) + 2 < sizeof(_config)-1) {
|
if (strlen(new_config) + strlen(key) + strlen(value) + 2 < sizeof(_config)-1) {
|
||||||
|
if (new_config[0]) {
|
||||||
|
strcat(new_config, PROP_SEP_STR);
|
||||||
|
}
|
||||||
strcat(new_config, key);
|
strcat(new_config, key);
|
||||||
strcat(new_config, KEY_SEP_STR);
|
strcat(new_config, KEY_SEP_STR);
|
||||||
strcat(new_config, value);
|
strcat(new_config, value);
|
||||||
strcpy(_config, new_config); // commit new serialized string
|
strcpy(_config, new_config); // commit new serialized string
|
||||||
markDirty();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false; // didn't fit in _config[]
|
return false; // didn't fit in _config[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DynamicConfigSerializer::setByKey(const char* key, const char* value) {
|
||||||
|
if (setByKeyPrv(key, value)) {
|
||||||
|
markDirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool DynamicConfigSerializer::getByKey(const char* key, char* value, size_t max_len) {
|
bool DynamicConfigSerializer::getByKey(const char* key, char* value, size_t max_len) {
|
||||||
if (_fallback && _fallback->getByKey(key, value, max_len)) return true;
|
if (_fallback && _fallback->getByKey(key, value, max_len)) return true;
|
||||||
|
|
||||||
@@ -66,19 +76,22 @@ bool DynamicConfigSerializer::getByKey(const char* key, char* value, size_t max_
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DynamicConfigSerializer::structure() {
|
void DynamicConfigSerializer::structure() {
|
||||||
|
if (_context->op() == OP::WRITE) {
|
||||||
char tmp[MAX_DYNAMIC_CONFG];
|
char tmp[MAX_DYNAMIC_CONFG];
|
||||||
strcpy(tmp, _config); // make a (modifiable) copy
|
strcpy(tmp, _config); // make a (modifiable) copy
|
||||||
|
|
||||||
const char* parts[8];
|
const char* parts[8];
|
||||||
int n = mesh::Utils::parseTextParts(tmp, parts, 8, PROP_SEP_CHAR);
|
int n = mesh::Utils::parseTextParts(tmp, parts, 8, PROP_SEP_CHAR);
|
||||||
|
|
||||||
// dynamically call def()'s
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
char* item = (char *) parts[i];
|
char* item = (char *) parts[i];
|
||||||
char* eq = strchr(item, KEY_SEP_CHAR);
|
char* eq = strchr(item, KEY_SEP_CHAR);
|
||||||
if (eq) {
|
if (eq) {
|
||||||
*eq = 0; // replace separator with null terminator
|
*eq = 0; // replace separator with null terminator
|
||||||
def(item, eq + 1, MAX_DYNAMIC_CONFG/2); // maximum HALF of total for individual property value
|
def(item, eq + 1, MAX_DYNAMIC_CONFG/2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
setByKeyPrv(_context->getKey(getDepth()), _context->getToken());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
#include "ConfigSerializer.h"
|
#include "ConfigSerializer.h"
|
||||||
#include "KeyValueStore.h"
|
#include "KeyValueStore.h"
|
||||||
|
|
||||||
@@ -9,6 +10,8 @@ class DynamicConfigSerializer : public ConfigSerializer, public KeyValueStore {
|
|||||||
char _config[MAX_DYNAMIC_CONFG];
|
char _config[MAX_DYNAMIC_CONFG];
|
||||||
KeyValueStore* _fallback;
|
KeyValueStore* _fallback;
|
||||||
|
|
||||||
|
bool setByKeyPrv(const char* key, const char* value);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void structure() override;
|
void structure() override;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include "helpers/ConfigSerializer.h"
|
#include "helpers/ConfigSerializer.h"
|
||||||
|
#include "helpers/DynamicConfigSerializer.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_S "56"
|
||||||
#define TEST_INT 56
|
#define TEST_INT 56
|
||||||
@@ -192,28 +185,90 @@ TEST(ConfigSerializer, LoadSerial_IgnoreUnknowns) {
|
|||||||
EXPECT_TRUE(match);
|
EXPECT_TRUE(match);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(NodePrefs, FemGainSettingsRoundTrip) {
|
TEST(DynamicConfigSerializer, GetSet_Basic) {
|
||||||
NodePrefs saved;
|
DynamicConfigSerializer data;
|
||||||
saved.radio_fem_rxgain = 0;
|
|
||||||
saved.radio_fem_txgain = 1;
|
|
||||||
|
|
||||||
MockPrintStream output;
|
bool s1 = data.setByKey("age", "11");
|
||||||
ASSERT_TRUE(saved.saveSerial(output));
|
bool s2 = data.setByKey("name", "Scott");
|
||||||
|
EXPECT_TRUE(s1 && s2);
|
||||||
|
|
||||||
std::string serialised(reinterpret_cast<const char*>(output.getBytes()), output.getLength());
|
char tmp[32];
|
||||||
EXPECT_NE(std::string::npos, serialised.find("fem_rxgain:0"));
|
bool g1 = data.getByKey("age", tmp, 31);
|
||||||
EXPECT_NE(std::string::npos, serialised.find("fem_txgain:1"));
|
EXPECT_TRUE(g1);
|
||||||
|
EXPECT_STREQ("11", tmp);
|
||||||
|
|
||||||
MockInputStream input(serialised.c_str());
|
bool g2 = data.getByKey("name", tmp, 31);
|
||||||
NodePrefs loaded;
|
EXPECT_TRUE(g2);
|
||||||
loaded.radio_fem_rxgain = 1;
|
EXPECT_STREQ("Scott", tmp);
|
||||||
loaded.radio_fem_txgain = 0;
|
|
||||||
|
|
||||||
ASSERT_TRUE(loaded.loadSerial(input));
|
|
||||||
EXPECT_EQ(0, loaded.radio_fem_rxgain);
|
|
||||||
EXPECT_EQ(1, loaded.radio_fem_txgain);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(DynamicConfigSerializer, Set_Replaces) {
|
||||||
|
DynamicConfigSerializer data;
|
||||||
|
|
||||||
|
bool s1 = data.setByKey("age", "11");
|
||||||
|
bool s2 = data.setByKey("name", "Scott");
|
||||||
|
EXPECT_TRUE(s1 && s2);
|
||||||
|
|
||||||
|
bool s3 = data.setByKey("age", "333");
|
||||||
|
EXPECT_TRUE(s3);
|
||||||
|
|
||||||
|
char tmp[32];
|
||||||
|
bool g1 = data.getByKey("age", tmp, 31);
|
||||||
|
EXPECT_TRUE(g1);
|
||||||
|
EXPECT_STREQ("333", tmp);
|
||||||
|
|
||||||
|
bool g2 = data.getByKey("name", tmp, 31);
|
||||||
|
EXPECT_TRUE(g2);
|
||||||
|
EXPECT_STREQ("Scott", tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DynamicConfigSerializer, GetUnknown_Fail) {
|
||||||
|
DynamicConfigSerializer data;
|
||||||
|
|
||||||
|
bool s1 = data.setByKey("age", "11");
|
||||||
|
EXPECT_TRUE(s1);
|
||||||
|
|
||||||
|
char tmp[32];
|
||||||
|
bool g2 = data.getByKey("name", tmp, 31);
|
||||||
|
EXPECT_FALSE(g2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DynamicConfigSerializer, SaveCustom_Basic) {
|
||||||
|
MockPrintStream s;
|
||||||
|
DynamicConfigSerializer data;
|
||||||
|
|
||||||
|
bool s1 = data.setByKey("age", "11");
|
||||||
|
bool s2 = data.setByKey("name", "Scott");
|
||||||
|
EXPECT_TRUE(s1 && s2);
|
||||||
|
|
||||||
|
bool success = data.saveSerial(s);
|
||||||
|
EXPECT_TRUE(success);
|
||||||
|
|
||||||
|
auto l = s.getLength();
|
||||||
|
char tmp[128];
|
||||||
|
memcpy(tmp, s.getBytes(), l);
|
||||||
|
tmp[l] = 0;
|
||||||
|
|
||||||
|
const char* expect = "{age:\"11\",name:\"Scott\"}";
|
||||||
|
EXPECT_STREQ(expect, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DynamicConfigSerializer, LoadCustom_Basic) {
|
||||||
|
MockInputStream s("{age:\"" TEST_INT_S "\",name:\"Scott\"}");
|
||||||
|
DynamicConfigSerializer data;
|
||||||
|
|
||||||
|
bool success = data.loadSerial(s);
|
||||||
|
EXPECT_TRUE(success);
|
||||||
|
|
||||||
|
char tmp[32];
|
||||||
|
bool g1 = data.getByKey("age", tmp, 31);
|
||||||
|
EXPECT_TRUE(g1);
|
||||||
|
EXPECT_STREQ(TEST_INT_S, tmp);
|
||||||
|
|
||||||
|
bool g2 = data.getByKey("name", tmp, 31);
|
||||||
|
EXPECT_TRUE(g2);
|
||||||
|
EXPECT_STREQ("Scott", tmp);
|
||||||
|
}
|
||||||
|
|
||||||
// ── main ───────────────────────────────────────────────────────
|
// ── main ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user