#pragma once #include #include #include #include #include #include #include // Minimal String class that mimics Arduino's String for native tests struct String { std::string _s; String() {} String(const char* s) : _s(s ? s : "") {} String(const std::string& s) : _s(s) {} String(int v) { _s = std::to_string(v); } String(unsigned long v) { _s = std::to_string(v); } String(long long v) { _s = std::to_string(v); } String(unsigned long long v) { _s = std::to_string(v); } const char* c_str() const { return _s.c_str(); } size_t length() const { return _s.size(); } bool isEmpty() const { return _s.empty(); } bool empty() const { return _s.empty(); } int toInt() const { return _s.empty() ? 0 : std::stoi(_s); } String substring(size_t from) const { return String(_s.substr(from)); } String substring(size_t from, size_t to) const { return String(_s.substr(from, to - from)); } void replace(const char* from, const char* to_str) { std::string result; std::string f(from), t(to_str); size_t pos = 0, found; while ((found = _s.find(f, pos)) != std::string::npos) { result += _s.substr(pos, found - pos); result += t; pos = found + f.size(); } result += _s.substr(pos); _s = result; } void toUpperCase() { for (char& c : _s) c = (char)toupper((unsigned char)c); } bool equalsIgnoreCase(const String& o) const { if (_s.size() != o._s.size()) return false; for (size_t i = 0; i < _s.size(); i++) { if (tolower((unsigned char)_s[i]) != tolower((unsigned char)o._s[i])) return false; } return true; } bool operator==(const String& o) const { return _s == o._s; } bool operator==(const char* o) const { return _s == o; } bool operator!=(const String& o) const { return _s != o._s; } bool operator!=(const char* o) const { return _s != o; } String operator+(const String& o) const { return String(_s + o._s); } String operator+(const char* o) const { return String(_s + o); } String& operator+=(const String& o) { _s += o._s; return *this; } String& operator+=(const char* o) { _s += o; return *this; } // Allow use as map key operator std::string() const { return _s; } // toString() for IP-like objects that have it String toString() const { return *this; } }; inline String operator+(const char* a, const String& b) { return String(std::string(a) + b._s); } inline String operator+(const std::string& a, const String& b) { return String(a + b._s); } // Controllable millis and digitalRead for timeout / button tests extern uint32_t g_millis_value; extern int g_digital_read_value; #ifndef LOW #define LOW 0 #define HIGH 1 #endif inline unsigned long millis() { return g_millis_value += 10; } inline void delay(unsigned long) {} inline void pinMode(int, int) {} inline int digitalRead(int) { return g_digital_read_value; } // Color constants (from config.h) #define COLOR_YELLOW 0x2 #define COLOR_RED 0x3 // Serial mock struct SerialMock { void begin(int) {} void println(const String&) {} void println(const char*) {} void println(int) {} void print(const String&) {} void print(const char*) {} void flush() {} } Serial; // strtoull is available from on native