mirror of https://github.com/meshcore-dev/MeshCore
5 changed files with 206 additions and 5 deletions
@ -0,0 +1,3 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <Stream.h> |
||||
@ -1,10 +1,71 @@ |
|||||
#pragma once |
#pragma once |
||||
|
|
||||
|
#include <stddef.h> |
||||
|
#include <stdint.h> |
||||
|
#include <string.h> |
||||
|
|
||||
// Mock Stream class for native testing
|
// Mock Stream class for native testing
|
||||
// Provides minimal interface needed by Utils.h
|
// Provides minimal interface needed by Utils.h
|
||||
|
|
||||
class Stream { |
#define DEC 10 |
||||
|
#define HEX 16 |
||||
|
#define OCT 8 |
||||
|
#define BIN 2 |
||||
|
|
||||
|
class Print |
||||
|
{ |
||||
public: |
public: |
||||
virtual void print(char c) {} |
virtual size_t write(uint8_t b) { return 1; } |
||||
virtual void print(const char* str) {} |
size_t write(const char *str) |
||||
|
{ |
||||
|
if(str == NULL) { |
||||
|
return 0; |
||||
|
} |
||||
|
return write((const uint8_t *) str, strlen(str)); |
||||
|
} |
||||
|
virtual size_t write(const uint8_t *buffer, size_t size) { |
||||
|
size_t t = 0; |
||||
|
for (int i = 0; i < size; i++) { t += write(buffer[i]); } |
||||
|
return t; |
||||
|
} |
||||
|
size_t write(const char *buffer, size_t size) |
||||
|
{ |
||||
|
return write((const uint8_t *) buffer, size); |
||||
|
} |
||||
|
|
||||
|
virtual size_t print(unsigned char b, int r = DEC) { return 0; } |
||||
|
virtual size_t print(int v, int r = DEC) { return 0; } |
||||
|
virtual size_t print(unsigned int v, int r = DEC) { return 0; } |
||||
|
virtual size_t print(long v, int r = DEC) { return 0; } |
||||
|
virtual size_t print(unsigned long v, int r = DEC) { return 0; } |
||||
|
virtual size_t print(long long v, int r = DEC) { return 0; } |
||||
|
virtual size_t print(unsigned long long v, int r = DEC) { return 0; } |
||||
|
virtual size_t print(double v, int p = 2) { return 0; } |
||||
|
|
||||
|
size_t print(char c) { return write(c); } |
||||
|
size_t print(const char* str) { return write(str); } |
||||
|
|
||||
|
//size_t println(void) { return 0; }
|
||||
|
|
||||
|
virtual void flush() { /* Empty implementation for backward compatibility */ } |
||||
}; |
}; |
||||
|
|
||||
|
class Stream: public Print |
||||
|
{ |
||||
|
public: |
||||
|
virtual int available() { return 0; } |
||||
|
virtual int read() { return -1; } |
||||
|
virtual int peek() { return 0; } |
||||
|
|
||||
|
virtual size_t readBytes(char *buffer, size_t length) { |
||||
|
size_t i = 0; |
||||
|
while (i < length && available()) { |
||||
|
buffer[i++] = read(); |
||||
|
} |
||||
|
return i; |
||||
|
} |
||||
|
virtual size_t readBytes(uint8_t *buffer, size_t length) |
||||
|
{ |
||||
|
return readBytes((char *) buffer, length); |
||||
|
} |
||||
|
}; |
||||
@ -0,0 +1,136 @@ |
|||||
|
#include <gtest/gtest.h> |
||||
|
#include "helpers/ConfigSerializer.h" |
||||
|
|
||||
|
#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]; |
||||
|
public: |
||||
|
size_t write(uint8_t b) override { |
||||
|
if (len < sizeof(_buf)) { |
||||
|
_buf[len++] = b; |
||||
|
return 1; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
size_t print(unsigned char b, int r) override { if (b == TEST_INT) return Print::print(TEST_INT_S); return 0; } |
||||
|
size_t print(int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } |
||||
|
size_t print(unsigned int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } |
||||
|
size_t print(long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } |
||||
|
size_t print(unsigned long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } |
||||
|
size_t print(long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } |
||||
|
size_t print(unsigned long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; } |
||||
|
size_t print(double v, int p = 2) override { |
||||
|
if (p == 6) return Print::print(TEST_DOUBLE_S); |
||||
|
if (p == 4) return Print::print(TEST_FLOAT_S); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
// ── 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_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); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// ── main ───────────────────────────────────────────────────────
|
||||
|
|
||||
|
int main(int argc, char** argv) { |
||||
|
::testing::InitGoogleTest(&argc, argv); |
||||
|
return RUN_ALL_TESTS(); |
||||
|
} |
||||
Loading…
Reference in new issue