diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 080e70d08..000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} diff --git a/Makefile b/Makefile index 842452e02..e311dab9d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ # # GNUMakefile for EMS-ESP +# This is mainly used to generate the .o files for SonarQube analysis # NUMJOBS=${NUMJOBS:-" -j2 "} @@ -38,7 +39,7 @@ CXX_STANDARD := -std=gnu++14 # Defined Symbols #---------------------------------------------------------------------- DEFINES += -DARDUINOJSON_ENABLE -DARDUINOJSON_ENABLE_ARDUINO_STRING -DARDUINOJSON_USE_DOUBLE=0 -DEFINES += -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST -D__linux__ -DEMC_RX_BUFFER_SIZE=1500 +DEFINES += -DEMSESP_STANDALONE -DEMSESP_TEST -DEMC_RX_BUFFER_SIZE=1500 DEFINES += $(ARGS) DEFAULTS = -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.0-dev\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" diff --git a/lib/ESPAsyncWebServer/src/AsyncJson.h b/lib/ESPAsyncWebServer/src/AsyncJson.h index 1db57c991..4c12df572 100644 --- a/lib/ESPAsyncWebServer/src/AsyncJson.h +++ b/lib/ESPAsyncWebServer/src/AsyncJson.h @@ -89,24 +89,24 @@ class AsyncJsonResponse : public AsyncAbstractResponse { } }; -class PrettyAsyncJsonResponse : public AsyncJsonResponse { - public: - PrettyAsyncJsonResponse(bool isArray = false) - : AsyncJsonResponse{isArray} { - } - size_t setLength() { - _contentLength = measureJsonPretty(_root); - if (_contentLength) { - _isValid = true; - } - return _contentLength; - } - size_t _fillBuffer(uint8_t * data, size_t len) { - ChunkPrint dest(data, _sentLength, len); - serializeJsonPretty(_root, dest); - return len; - } -}; +// class PrettyAsyncJsonResponse : public AsyncJsonResponse { +// public: +// PrettyAsyncJsonResponse(bool isArray = false) +// : AsyncJsonResponse{isArray} { +// } +// size_t setLength() { +// _contentLength = measureJsonPretty(_root); +// if (_contentLength) { +// _isValid = true; +// } +// return _contentLength; +// } +// size_t _fillBuffer(uint8_t * data, size_t len) { +// ChunkPrint dest(data, _sentLength, len); +// serializeJsonPretty(_root, dest); +// return len; +// } +// }; typedef std::function ArJsonRequestHandlerFunction; diff --git a/lib/espMqttClient/src/Helpers.h b/lib/espMqttClient/src/Helpers.h index 05ab136d9..fcda6f3e2 100644 --- a/lib/espMqttClient/src/Helpers.h +++ b/lib/espMqttClient/src/Helpers.h @@ -9,41 +9,51 @@ the LICENSE file. #pragma once #if defined(ARDUINO_ARCH_ESP32) - #include // millis(), ESP.getFreeHeap(); - #include "freertos/FreeRTOS.h" - #include "freertos/task.h" - #include "esp_task_wdt.h" - #define EMC_SEMAPHORE_TAKE() xSemaphoreTake(_xSemaphore, portMAX_DELAY) - #define EMC_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore) - #define EMC_GET_FREE_MEMORY() std::max(ESP.getMaxAllocHeap(), ESP.getMaxAllocPsram()) - #define EMC_YIELD() vTaskDelay(1) - #define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "esp32%06llx", ESP.getEfuseMac()); +#include // millis(), ESP.getFreeHeap(); +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_task_wdt.h" +#define EMC_SEMAPHORE_TAKE() xSemaphoreTake(_xSemaphore, portMAX_DELAY) +#define EMC_SEMAPHORE_GIVE() xSemaphoreGive(_xSemaphore) +#define EMC_GET_FREE_MEMORY() std::max(ESP.getMaxAllocHeap(), ESP.getMaxAllocPsram()) +#define EMC_YIELD() vTaskDelay(1) +#define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "esp32%06llx", ESP.getEfuseMac()); #elif defined(ARDUINO_ARCH_ESP8266) - #include // millis(), ESP.getFreeHeap(); - #if EMC_ESP8266_MULTITHREADING - // This lib doesn't run use multithreading on ESP8266 - // _xSemaphore defined as std::atomic - #define EMC_SEMAPHORE_TAKE() while (_xSemaphore) { /*ESP.wdtFeed();*/ } _xSemaphore = true - #define EMC_SEMAPHORE_GIVE() _xSemaphore = false - #else - #define EMC_SEMAPHORE_TAKE() - #define EMC_SEMAPHORE_GIVE() - #endif - #define EMC_GET_FREE_MEMORY() ESP.getMaxFreeBlockSize() - // no need to yield for ESP8266, the Arduino framework does this internally - // yielding in async is forbidden (will crash) - #define EMC_YIELD() - #define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "esp8266%06x", ESP.getChipId()); -#elif defined(__linux__) - #include // NOLINT [build/c++11] - #include // NOLINT [build/c++11] for yield() - #define millis() std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() - #define EMC_GET_FREE_MEMORY() 1000000000 - #define EMC_YIELD() std::this_thread::yield() - #define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "Client%04d%04d%04d", rand()%10000, rand()%10000, rand()%10000) - #include // NOLINT [build/c++11] - #define EMC_SEMAPHORE_TAKE() mtx.lock(); - #define EMC_SEMAPHORE_GIVE() mtx.unlock(); +#include // millis(), ESP.getFreeHeap(); +#if EMC_ESP8266_MULTITHREADING +// This lib doesn't run use multithreading on ESP8266 +// _xSemaphore defined as std::atomic +#define EMC_SEMAPHORE_TAKE() \ + while (_xSemaphore) { /*ESP.wdtFeed();*/ \ + } \ + _xSemaphore = true +#define EMC_SEMAPHORE_GIVE() _xSemaphore = false #else - #error Target platform not supported +#define EMC_SEMAPHORE_TAKE() +#define EMC_SEMAPHORE_GIVE() +#endif +#define EMC_GET_FREE_MEMORY() ESP.getMaxFreeBlockSize() +// no need to yield for ESP8266, the Arduino framework does this internally +// yielding in async is forbidden (will crash) +#define EMC_YIELD() +#define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "esp8266%06x", ESP.getChipId()); +#elif defined(__linux__) +#include // NOLINT [build/c++11] +#include // NOLINT [build/c++11] for yield() +#define millis() std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() +#define EMC_GET_FREE_MEMORY() 1000000000 +#define EMC_YIELD() std::this_thread::yield() +#define EMC_GENERATE_CLIENTID(x) snprintf(x, EMC_CLIENTID_LENGTH, "Client%04d%04d%04d", rand() % 10000, rand() % 10000, rand() % 10000) +#include // NOLINT [build/c++11] +#define EMC_SEMAPHORE_TAKE() mtx.lock(); +#define EMC_SEMAPHORE_GIVE() mtx.unlock(); +#elif defined(_WIN32) || defined(__APPLE__) +#include +#define EMC_SEMAPHORE_TAKE() +#define EMC_SEMAPHORE_GIVE() +#define EMC_YIELD() +#define EMC_GET_FREE_MEMORY() 1000 +#define EMC_GENERATE_CLIENTID(x) +#else +#error Target platform not supported #endif diff --git a/lib/espMqttClient/src/Transport/ClientPosixIPAddress.cpp b/lib/espMqttClient/src/Transport/ClientPosixIPAddress.cpp index 3386dec85..620653ae7 100644 --- a/lib/espMqttClient/src/Transport/ClientPosixIPAddress.cpp +++ b/lib/espMqttClient/src/Transport/ClientPosixIPAddress.cpp @@ -6,27 +6,27 @@ For a copy, see or the LICENSE file. */ -#if defined(__linux__) +#if defined(__linux__) || defined(_WIN32) || defined(__APPLE__) #include "ClientPosixIPAddress.h" IPAddress::IPAddress() -: _address(0) { - // empty + : _address(0) { + // empty } IPAddress::IPAddress(uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3) -: _address(0) { - _address = (uint32_t)p0 << 24 | (uint32_t)p1 << 16 | (uint32_t)p2 << 8 | p3; + : _address(0) { + _address = (uint32_t)p0 << 24 | (uint32_t)p1 << 16 | (uint32_t)p2 << 8 | p3; } IPAddress::IPAddress(uint32_t address) -: _address(address) { - // empty + : _address(address) { + // empty } IPAddress::operator uint32_t() { - return _address; + return _address; } #endif diff --git a/lib/espMqttClient/src/espMqttClient.cpp b/lib/espMqttClient/src/espMqttClient.cpp index bbbfd693e..ab1a3649b 100644 --- a/lib/espMqttClient/src/espMqttClient.cpp +++ b/lib/espMqttClient/src/espMqttClient.cpp @@ -120,4 +120,9 @@ espMqttClient::espMqttClient() , _client() { _transport = &_client; } +#elif defined(_WIN32) || defined(__APPLE__) +// Windows +espMqttClient::espMqttClient() + : MqttClientSetup(espMqttClientTypes::UseInternalTask::NO) { +} #endif diff --git a/lib/espMqttClient/src/espMqttClient.h b/lib/espMqttClient/src/espMqttClient.h index 4e448011d..531dd7ba5 100644 --- a/lib/espMqttClient/src/espMqttClient.h +++ b/lib/espMqttClient/src/espMqttClient.h @@ -22,59 +22,64 @@ the LICENSE file. #if defined(ARDUINO_ARCH_ESP8266) class espMqttClient : public MqttClientSetup { - public: - espMqttClient(); + public: + espMqttClient(); - protected: - espMqttClientInternals::ClientSync _client; + protected: + espMqttClientInternals::ClientSync _client; }; class espMqttClientSecure : public MqttClientSetup { - public: - espMqttClientSecure(); - espMqttClientSecure& setInsecure(); - espMqttClientSecure& setFingerprint(const uint8_t fingerprint[20]); - espMqttClientSecure& setTrustAnchors(const X509List *ta); - espMqttClientSecure& setClientRSACert(const X509List *cert, const PrivateKey *sk); - espMqttClientSecure& setClientECCert(const X509List *cert, const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type); - espMqttClientSecure& setCertStore(CertStoreBase *certStore); + public: + espMqttClientSecure(); + espMqttClientSecure & setInsecure(); + espMqttClientSecure & setFingerprint(const uint8_t fingerprint[20]); + espMqttClientSecure & setTrustAnchors(const X509List * ta); + espMqttClientSecure & setClientRSACert(const X509List * cert, const PrivateKey * sk); + espMqttClientSecure & setClientECCert(const X509List * cert, const PrivateKey * sk, unsigned allowed_usages, unsigned cert_issuer_key_type); + espMqttClientSecure & setCertStore(CertStoreBase * certStore); - protected: - espMqttClientInternals::ClientSecureSync _client; + protected: + espMqttClientInternals::ClientSecureSync _client; }; #endif #if defined(ARDUINO_ARCH_ESP32) class espMqttClient : public MqttClientSetup { - public: - explicit espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask); - explicit espMqttClient(uint8_t priority = 1, uint8_t core = 1); + public: + explicit espMqttClient(espMqttClientTypes::UseInternalTask useInternalTask); + explicit espMqttClient(uint8_t priority = 1, uint8_t core = 1); - protected: - espMqttClientInternals::ClientSync _client; + protected: + espMqttClientInternals::ClientSync _client; }; class espMqttClientSecure : public MqttClientSetup { - public: - explicit espMqttClientSecure(espMqttClientTypes::UseInternalTask useInternalTask); - explicit espMqttClientSecure(uint8_t priority = 1, uint8_t core = 1); - espMqttClientSecure& setInsecure(); - espMqttClientSecure& setCACert(const char* rootCA); - espMqttClientSecure& setCertificate(const char* clientCa); - espMqttClientSecure& setPrivateKey(const char* privateKey); - espMqttClientSecure& setPreSharedKey(const char* pskIdent, const char* psKey); + public: + explicit espMqttClientSecure(espMqttClientTypes::UseInternalTask useInternalTask); + explicit espMqttClientSecure(uint8_t priority = 1, uint8_t core = 1); + espMqttClientSecure & setInsecure(); + espMqttClientSecure & setCACert(const char * rootCA); + espMqttClientSecure & setCertificate(const char * clientCa); + espMqttClientSecure & setPrivateKey(const char * privateKey); + espMqttClientSecure & setPreSharedKey(const char * pskIdent, const char * psKey); - protected: - espMqttClientInternals::ClientSecureSync _client; + protected: + espMqttClientInternals::ClientSecureSync _client; }; -#endif -#if defined(__linux__) +#elif defined(__linux__) class espMqttClient : public MqttClientSetup { - public: - espMqttClient(); + public: + espMqttClient(); - protected: - espMqttClientInternals::ClientPosix _client; + protected: + espMqttClientInternals::ClientPosix _client; }; +#elif defined(_WIN32) || defined(__APPLE__) +class espMqttClient : public MqttClientSetup { + public: + espMqttClient(); +}; + #endif diff --git a/lib/framework/NetworkSettingsService.cpp b/lib/framework/NetworkSettingsService.cpp index 7a4ca513c..99314f3f0 100644 --- a/lib/framework/NetworkSettingsService.cpp +++ b/lib/framework/NetworkSettingsService.cpp @@ -183,9 +183,6 @@ void NetworkSettingsService::setWiFiPowerOnRSSI() { #ifdef EMSESP_DEBUG uint8_t set_power = min_tx_pwr / 10; // this is the recommended power setting to use emsesp::EMSESP::logger().debug("Recommended set WiFi Tx Power (set_power %d, new power %d, rssi %d, threshold %d)", set_power, p, rssi, threshold); -#else - char result[10]; - emsesp::EMSESP::logger().info("Setting WiFi Tx Power to %s dBm", emsesp::Helpers::render_value(result, ((double)(p) / 4), 1)); #endif if (!WiFi.setTxPower(p)) { diff --git a/lib/uuid-console/src/shell.cpp b/lib/uuid-console/src/shell.cpp index 9571821a9..8005913c1 100644 --- a/lib/uuid-console/src/shell.cpp +++ b/lib/uuid-console/src/shell.cpp @@ -60,6 +60,7 @@ bool Shell::running() const { } void Shell::start() { + // added for EMS-ESP #if defined(EMSESP_DEBUG) uuid::log::Logger::register_handler(this, uuid::log::Level::DEBUG); // added for EMS-ESP #else diff --git a/lib_standalone/Arduino.cpp b/lib_standalone/Arduino.cpp index 314f01601..d2975fdcf 100644 --- a/lib_standalone/Arduino.cpp +++ b/lib_standalone/Arduino.cpp @@ -17,7 +17,6 @@ #ifdef EMSESP_STANDALONE - #include #include #include @@ -57,8 +56,11 @@ void ClientLoop(void * arg) { } } +#ifndef UNITY_INCLUDE_CONFIG_H +// we have another main that overrides this when using Unity in test_api.cpp int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused))) { setup(); + std::thread t = std::thread(ClientLoop, nullptr); // while (millis() <= 10 * 1000) { while (1) { @@ -69,10 +71,7 @@ int main(int argc __attribute__((unused)), char * argv[] __attribute__((unused)) t.join(); return EXIT_SUCCESS; } - -// unsigned long millis() { -// return __millis; -// } +#endif int64_t esp_timer_get_time() { return __millis; @@ -139,13 +138,13 @@ uint32_t analogReadMilliVolts(uint8_t pin) { return 0; } -void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation){}; -void analogSetAttenuation(adc_attenuation_t attenuation){}; -void dacWrite(uint8_t pin, uint8_t value){}; +void analogSetPinAttenuation(uint8_t pin, adc_attenuation_t attenuation) {}; +void analogSetAttenuation(adc_attenuation_t attenuation) {}; +void dacWrite(uint8_t pin, uint8_t value) {}; double ledcSetup(uint8_t chan, double freq, uint8_t bit_num) { return 0; }; -void ledcAttachPin(uint8_t pin, uint8_t chan){}; -void ledcWrite(uint8_t chan, uint32_t duty){}; +void ledcAttachPin(uint8_t pin, uint8_t chan) {}; +void ledcWrite(uint8_t chan, uint32_t duty) {}; #endif \ No newline at end of file diff --git a/lib_standalone/Arduino.h b/lib_standalone/Arduino.h index b500609ab..12a294793 100644 --- a/lib_standalone/Arduino.h +++ b/lib_standalone/Arduino.h @@ -32,6 +32,10 @@ #include #include "WString.h" +#include "Network.h" + +extern ETHClass ETH; +extern WiFiClass WiFi; typedef double double_t; @@ -86,6 +90,21 @@ int vsnprintf_P(char * str, size_t size, const char * format, va_list ap); #define pgm_read_float(addr) (*(const float *)(addr)) #define pgm_read_ptr(addr) (*(const void **)(addr)) +// #if defined(__linux__) +#include // NOLINT [build/c++11] +#include // NOLINT [build/c++11] for yield() +#define millis() std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() +// #endif + +int64_t esp_timer_get_time(); + +void delay(unsigned long millis); + +void yield(void); + +void setup(void); +void loop(void); + class Print; class NativeConsole : public Stream { @@ -94,6 +113,11 @@ class NativeConsole : public Stream { } int available() override { +#if defined(_WIN32) + // added for EMS-ESP + return 1; +#else + if (peek_ != -1) return 1; @@ -107,6 +131,7 @@ class NativeConsole : public Stream { timeout.tv_usec = 1000; return ::select(STDIN_FILENO + 1, &rfds, NULL, NULL, &timeout) > 0 ? 1 : 0; +#endif } int read() override { @@ -163,27 +188,6 @@ class NativeConsole : public Stream { int peek_ = -1; }; -#include "Network.h" - extern NativeConsole Serial; -extern ETHClass ETH; -extern WiFiClass WiFi; - -// unsigned long millis(); - -#if defined(__linux__) -#include // NOLINT [build/c++11] -#include // NOLINT [build/c++11] for yield() -#define millis() std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count() -#endif - -int64_t esp_timer_get_time(); - -void delay(unsigned long millis); - -void yield(void); - -void setup(void); -void loop(void); #endif diff --git a/lib_standalone/AsyncJson.h b/lib_standalone/AsyncJson.h index e3af4f7a8..21d1db552 100644 --- a/lib_standalone/AsyncJson.h +++ b/lib_standalone/AsyncJson.h @@ -42,64 +42,66 @@ class ChunkPrint : public Print { } }; -class PrettyAsyncJsonResponse { - protected: - JsonDocument _jsonBuffer; +// class PrettyAsyncJsonResponse { +// protected: +// JsonDocument _jsonBuffer; - JsonVariant _root; - bool _isValid; +// JsonVariant _root; +// bool _isValid; - public: - PrettyAsyncJsonResponse(bool isArray = false) - : _isValid{false} { - if (isArray) - _root = _jsonBuffer.to(); - else - _root = _jsonBuffer.add(); - } +// public: +// PrettyAsyncJsonResponse(bool isArray = false) +// : _isValid{false} { +// if (isArray) +// _root = _jsonBuffer.to(); +// else +// _root = _jsonBuffer.add(); +// } - ~PrettyAsyncJsonResponse() { - } +// ~PrettyAsyncJsonResponse() { +// } - JsonVariant getRoot() { - return _root; - } +// JsonVariant getRoot() { +// return _root; +// } - bool _sourceValid() const { - return _isValid; - } +// bool _sourceValid() const { +// return _isValid; +// } - size_t setLength() { - return 0; - } +// size_t setLength() { +// return 0; +// } - void setContentType(const char * s) { - } +// void setContentType(const char * s) { +// } - size_t getSize() { - return _jsonBuffer.size(); - } +// size_t getSize() { +// return _jsonBuffer.size(); +// } - size_t _fillBuffer(uint8_t * data, size_t len) { - return len; - } +// size_t _fillBuffer(uint8_t * data, size_t len) { +// return len; +// } - void setCode(uint16_t) { - } -}; +// void setCode(uint16_t) { +// } +// }; class AsyncJsonResponse { protected: JsonDocument _jsonBuffer; - - JsonVariant _root; - bool _isValid; - bool _isMsgPack; + JsonVariant _root; + bool _isValid; + bool _isMsgPack; + int _code; + size_t _contentLength; public: AsyncJsonResponse(bool isArray = false, bool isMsgPack = false) : _isValid{false} , _isMsgPack{isMsgPack} { + _code = 200; if (isArray) _root = _jsonBuffer.to(); else @@ -118,7 +120,12 @@ class AsyncJsonResponse { } size_t setLength() { - return 0; + _contentLength = _isMsgPack ? measureMsgPack(_root) : measureJson(_root); + + if (_contentLength) { + _isValid = true; + } + return _contentLength; } size_t getSize() { @@ -126,10 +133,12 @@ class AsyncJsonResponse { } size_t _fillBuffer(uint8_t * data, size_t len) { + // _isMsgPack ? serializeMsgPack(_root, data) : serializeJson(_root, data); return len; } - void setCode(uint16_t) { + void setCode(uint16_t c) { + _code = c; } void setContentType(const char * s) { diff --git a/lib_standalone/ESPAsyncWebServer.h b/lib_standalone/ESPAsyncWebServer.h index 57e0d7c08..69f25b43e 100644 --- a/lib_standalone/ESPAsyncWebServer.h +++ b/lib_standalone/ESPAsyncWebServer.h @@ -11,8 +11,8 @@ class AsyncWebServer; class AsyncWebServerRequest; class AsyncWebServerResponse; class AsyncJsonResponse; -class PrettyAsyncJsonResponse; -class MsgpackAsyncJsonResponse; +// class PrettyAsyncJsonResponse; +// class MsgpackAsyncJsonResponse; class AsyncEventSource; class AsyncWebParameter { @@ -76,9 +76,9 @@ class AsyncWebServerRequest { public: void * _tempObject; - AsyncWebServerRequest(AsyncWebServer *, AsyncClient *){}; - AsyncWebServerRequest(){}; - ~AsyncWebServerRequest(){}; + AsyncWebServerRequest(AsyncWebServer *, AsyncClient *) {}; + AsyncWebServerRequest() {}; + ~AsyncWebServerRequest() {}; AsyncClient * client() { return _client; @@ -99,9 +99,11 @@ class AsyncWebServerRequest { } void send(AsyncWebServerResponse * response) {}; + void send(AsyncJsonResponse * response) {}; - void send(PrettyAsyncJsonResponse * response) {}; - void send(MsgpackAsyncJsonResponse * response) {}; + + // void send(PrettyAsyncJsonResponse * response) {}; + // void send(MsgpackAsyncJsonResponse * response) {}; void send(int code, const String & contentType = String(), const String & content = String()) {}; void send(int code, const String & contentType, const __FlashStringHelper *) {}; @@ -205,6 +207,17 @@ class AsyncWebHandler { }; class AsyncWebServerResponse { + protected: + int _code; + String _contentType; + size_t _contentLength; + bool _sendContentLength; + bool _chunked; + size_t _headLength; + size_t _sentLength; + size_t _ackedLength; + size_t _writtenLength; + public: AsyncWebServerResponse(); virtual ~AsyncWebServerResponse(); @@ -221,9 +234,9 @@ class AsyncWebServer { public: AsyncWebServer(uint16_t port) - : _server(port){}; + : _server(port) {}; - ~AsyncWebServer(){}; + ~AsyncWebServer() {}; void begin() {}; void end(); @@ -239,8 +252,8 @@ class AsyncWebServer { class AsyncEventSource : public AsyncWebHandler { public: - AsyncEventSource(const String & url){}; - ~AsyncEventSource(){}; + AsyncEventSource(const String & url) {}; + ~AsyncEventSource() {}; size_t count() const { return 1; diff --git a/lib_standalone/Print.h b/lib_standalone/Print.h index d8f40e25e..7247f045e 100644 --- a/lib_standalone/Print.h +++ b/lib_standalone/Print.h @@ -86,7 +86,12 @@ class Print { return print(str); } size_t println() { + // added for EMS-ESP +#if defined(_WIN32) + return print("\n"); +#else return print("\r\n"); +#endif } size_t println(const char * data) { return print(data) + println(); @@ -106,7 +111,7 @@ class Print { size_t println(unsigned long value) { return print(std::to_string(value).c_str()) + println(); } - virtual void flush(){}; + virtual void flush() {}; private: int err_{0}; diff --git a/test/api_test.http b/mock-api/api_test.http similarity index 100% rename from test/api_test.http rename to mock-api/api_test.http diff --git a/test/api_test.sh b/mock-api/api_test.sh similarity index 100% rename from test/api_test.sh rename to mock-api/api_test.sh diff --git a/platformio.ini b/platformio.ini index a742abab0..ebb6b4feb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -38,7 +38,6 @@ unbuild_flags = ${common.core_unbuild_flags} [espressi32_base] -; 6.7.0 = Arduino v2.0.16 (based on IDF v4.4.7). See https://github.com/platformio/platform-espressif32/releases/tag/v6.7.0 platform = espressif32@6.8.1 framework = arduino board_build.filesystem = littlefs @@ -85,7 +84,7 @@ lib_deps = ; build for GitHub Actions CI ; the Web interface is built seperately, so is skipped in extra_scripts [env:ci] -; 4MB using Tasmota (no SSL) +; 4MB using Tasmota (no SSL) - like BBQKees older S32 extends = espressi32_base_tasmota extra_scripts = scripts/rename_fw.py board = esp32dev @@ -93,7 +92,7 @@ board_build.partitions = esp32_partition_4M.csv board_build.extra_flags = -DBOARD_HAS_PSRAM [env:ci_s3] -; 16MB ESP32-S3 +; 16MB ESP32-S3 - like BBQKees S3 extends = espressi32_base extra_scripts = scripts/rename_fw.py board = lolin_s3 @@ -106,7 +105,7 @@ build_flags = '-DEMSESP_DEFAULT_BOARD_PROFILE="S32S3"' [env:ci_16M] -; 16MB ESP32 with PSRAM +; 16MB ESP32 with PSRAM - like BBQKees E32V2 extends = espressi32_base extra_scripts = scripts/rename_fw.py board = esp32dev @@ -210,15 +209,28 @@ build_flags = ${common.build_flags} build_unflags = ${common.unbuild_flags} -; to build and run: pio run -e native -t exec +; +; Building and testing natively, standalone without an ESP32. +; See https://docs.platformio.org/en/latest/platforms/native.html +; +; It will generate an executbale which when run will show the EMS-ESP Console where you can run tests using the `test` command. +; +; to build and run directly on linux: pio run -e native -t exec +; +; to build and run on Windows, it needs winsock for the console input so: +; - For the first time, install Msys2 (https://www.msys2.org/) and the GCC compiler with `run pacman -S mingw-w64-ucrt-x86_64-gcc` +; - Then, build with `pio run -e native` to create the program.exe file +; - run by calling the executable from the Mysys shell e.g. `C:/msys64/msys2_shell.cmd -defterm -here -no-start -ucrt64 -c /d/dev/proddy/EMS-ESP32/.pio/build/native/program.exe` +; - or integrate into Windows Terminal https://www.msys2.org/docs/terminals/ +; [env:native] platform = native -extra_scripts = pre:scripts/rename_prog.py build_flags = -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 - -DEMSESP_DEBUG -DEMSESP_STANDALONE -DEMSESP_TEST + -DEMSESP_STANDALONE -DEMSESP_TEST -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" - -std=gnu++14 -Og -ggdb -D__linux__ + -std=gnu++14 -Og -ggdb +build_type = debug build_src_flags = -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces @@ -245,3 +257,50 @@ build_src_filter = lib_compat_mode = off lib_ldf_mode = off lib_ignore = Module EMS-ESP-Modules + +; unit tests +; pio run -e native-test -t exec +; works on Linux, Windows, and MacOS +; to auto generate the API exepected test results, compile with -DUNITY_CREATE and capture the output, and paste into the test_api.cpp file +[env:native-test] +platform = native +test_build_src = true +build_flags = + -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 + -DEMSESP_STANDALONE -DEMSESP_TEST + -DUNITY_INCLUDE_CONFIG_H + ; -DUNITY_CREATE + -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_TX_MODE=8 -DEMSESP_DEFAULT_VERSION=\"3.7.0-dev.0\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" + -std=gnu++14 -Os -ggdb -Wall -Wextra -Werror + -lgcov --coverage -fprofile-arcs -ftest-coverage +build_type = debug +build_src_flags = + -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces + -I./lib_standalone + -I./lib/uuid-common/src + -I./lib/uuid-console/src + -I./lib/uuid-log/src + -I./lib/semver + -I./lib/PButton + -I./lib/ArduinoJson + -I./lib/espMqttClient/src + -I./lib/espMqttClient/src/Transport + -I./test/api + -I./test +build_src_filter = + +<*> + - + +<../test> + +<../lib_standalone> + +<../lib/uuid-common> + +<../lib/uuid-console> + +<../lib/uuid-log> + +<../lib/semver> + +<../lib/PButton> + +<../lib/espMqttClient/src> + +<../lib/espMqttClient/src/Transport> +lib_ldf_mode = off +lib_deps = Unity +lib_ignore = Module EMS-ESP-Modules +test_testing_command = + ${platformio.build_dir}/${this.__env__}/program diff --git a/src/console.cpp b/src/console.cpp index 3d4dfb0c6..3a3642820 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -532,7 +532,8 @@ static void setup_commands(std::shared_ptr & commands) { JsonDocument doc; int8_t id = -1; - const char * cmd = Helpers::toLower(arguments[1]).c_str(); + auto arg = Helpers::toLower(arguments[1]); + const char * cmd = arg.c_str(); // prevent loosing pointer after object destruction uint8_t return_code = CommandRet::OK; JsonObject json = doc.to(); bool has_data = false; diff --git a/src/emsesp.cpp b/src/emsesp.cpp index 37849ed60..7a144dc81 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -1538,11 +1538,16 @@ EMSESP::EMSESP() // start all the core services // the services must be loaded in the correct order void EMSESP::start() { +// don't need shell if running unit tests +#if !defined(UNITY_INCLUDE_CONFIG_H) + serial_console_.begin(SERIAL_CONSOLE_BAUD_RATE); shell_ = std::make_shared(*this, serial_console_, true); shell_->maximum_log_messages(100); + shell_->start(); + #if defined(EMSESP_DEBUG) shell_->log_level(uuid::log::Level::DEBUG); #else @@ -1553,6 +1558,8 @@ void EMSESP::start() { shell_->add_flags(CommandFlags::ADMIN); // always start in su/admin mode when running tests #endif +#endif + // start the file system #ifndef EMSESP_STANDALONE if (!LittleFS.begin(true)) { diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 8f044bd14..493c06723 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -855,7 +855,7 @@ bool Mqtt::publish_ha_sensor_config(uint8_t type, // EMSdev // base name + shortname snprintf(uniq_id, sizeof(uniq_id), "%s_%s_%s", mqtt_basename_.c_str(), device_name, entity_with_tag); } else if (Mqtt::entity_format() == entityFormat::SINGLE_SHORT) { - // shortname (default) + // shortname only (=default) snprintf(uniq_id, sizeof(uniq_id), "%s_%s", device_name, entity_with_tag); } else if (Mqtt::entity_format() == entityFormat::SINGLE_OLD) { // shortname, remap to 3.6 diff --git a/src/mqtt.h b/src/mqtt.h index 7a1688b78..3d987de90 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -35,8 +35,12 @@ using mqtt_sub_function_p = std::function; class Mqtt { public: - enum discoveryType : uint8_t { HOMEASSISTANT, DOMOTICZ, DOMOTICZ_LATEST }; - enum entityFormat : uint8_t { SINGLE_LONG, SINGLE_SHORT, MULTI_SHORT, SINGLE_OLD, MULTI_OLD }; + enum discoveryType : uint8_t { HOMEASSISTANT = 0, DOMOTICZ, DOMOTICZ_LATEST }; + + // SINGLE_SHORT (1) and MULTI_SHORT (2) are the latest. Default is SINGLE_SHORT. + // SINGLE_LONG (0) is v3.4 only + // SINGLE_OLD (3) and MULTI_OLD (4) are for backwards compatibility with the older v3.6 style. https://github.com/emsesp/EMS-ESP32/issues/1714 + enum entityFormat : uint8_t { SINGLE_LONG = 0, SINGLE_SHORT, MULTI_SHORT, SINGLE_OLD, MULTI_OLD }; void loop(); void start(); diff --git a/src/system.cpp b/src/system.cpp index f6349cd80..2f97793a8 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -1207,19 +1207,19 @@ bool System::check_upgrade(bool factory_settings) { // if we're coming from 3.4.4 or 3.5.0b14 which had no version stored then we need to apply new settings if (missing_version) { - LOG_INFO("Upgrade: Setting MQTT Entity ID format to v3.4 format"); + LOG_INFO("Upgrade: Setting MQTT Entity ID format to older v3.4 format"); EMSESP::esp8266React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) { - mqttSettings.entity_format = 0; // use old Entity ID format from v3.4 + mqttSettings.entity_format = Mqtt::entityFormat::SINGLE_LONG; // use old Entity ID format from v3.4 return StateUpdateResult::CHANGED; }); } else if (settings_version.major() == 3 && settings_version.minor() <= 6) { LOG_INFO("Upgrade: Setting MQTT Entity ID format to v3.6 format"); EMSESP::esp8266React.getMqttSettingsService()->update([&](MqttSettings & mqttSettings) { if (mqttSettings.entity_format == 1) { - mqttSettings.entity_format = 3; // use old Entity ID format from v3.6 + mqttSettings.entity_format = Mqtt::entityFormat::SINGLE_OLD; // use old Entity ID format from v3.6 return StateUpdateResult::CHANGED; } else if (mqttSettings.entity_format == 2) { - mqttSettings.entity_format = 4; // use old Entity ID format from v3.6 + mqttSettings.entity_format = Mqtt::entityFormat::MULTI_OLD; // use old Entity ID format from v3.6 return StateUpdateResult::CHANGED; } return StateUpdateResult::UNCHANGED; @@ -1250,6 +1250,7 @@ bool System::check_upgrade(bool factory_settings) { settings.version = EMSESP_APP_VERSION; return StateUpdateResult::CHANGED; }); + LOG_INFO("Upgrade: Setting version to %s", EMSESP_APP_VERSION); return true; // need reboot } @@ -1540,9 +1541,16 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output } // API Status - node = output["api"].to(); + node = output["api"].to(); + +// if we're generating test data for Unit Tests we dont want to count these API calls as it will pollute the data response +#if defined(UNITY_INCLUDE_CONFIG_H) + node["APICalls"] = 0; + node["APIFails"] = 0; +#else node["APICalls"] = WebAPIService::api_count(); node["APIFails"] = WebAPIService::api_fails(); +#endif // EMS Bus Status node = output["bus"].to(); @@ -1610,8 +1618,12 @@ bool System::command_info(const char * value, const int8_t id, JsonObject output node["analogEnabled"] = settings.analog_enabled; node["telnetEnabled"] = settings.telnet_enabled; node["maxWebLogBuffer"] = settings.weblog_buffer; - node["webLogBuffer"] = EMSESP::webLogService.num_log_messages(); - node["modbusEnabled"] = settings.modbus_enabled; +#if defined(UNITY_INCLUDE_CONFIG_H) + node["webLogBuffer"] = 0; +#else + node["webLogBuffer"] = EMSESP::webLogService.num_log_messages(); +#endif + node["modbusEnabled"] = settings.modbus_enabled; }); // Devices - show EMS devices if we have any @@ -1798,9 +1810,15 @@ String System::getBBQKeesGatewayDetails() { if (!EMSESP::nvs_.isKey("mfg")) { return ""; } - if (EMSESP::nvs_.getString("mfg") != "BBQKees") { - return ""; + + // mfg can be either "BBQKees" or "BBQKees Electronics" + auto mfg = EMSESP::nvs_.getString("mfg"); + if (mfg) { + if (!mfg.startsWith("BBQKees")) { + return ""; + } } + return "BBQKees Gateway Model " + EMSESP::nvs_.getString("model") + " v" + EMSESP::nvs_.getString("hwrevision") + "/" + EMSESP::nvs_.getString("batch"); #else return ""; diff --git a/src/test/test.cpp b/src/test/test.cpp index 5cf7bf92c..d3ac94cd8 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -265,6 +265,8 @@ bool Test::test(const std::string & cmd, int8_t id1, int8_t id2) { // These next tests are run from the Consol via the test command, so inherit the Shell void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const std::string & id1_s, const std::string & id2_s) { + bool ok = false; // default tests fail + shell.add_flags(CommandFlags::ADMIN); // switch to su // init stuff @@ -297,8 +299,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const id2 = Helpers::atoint(id2_s.c_str()); } - bool ok = false; - // e.g. "test add 0x10 172" if (command == "add") { if (id1 == -1 || id2 == -1) { @@ -638,11 +638,11 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const Serial.print(COLOR_BRIGHT_MAGENTA); serializeJson(doc, Serial); Serial.print(COLOR_RESET); - Serial.println(); - Serial.print(" measureMsgPack="); - Serial.print(measureMsgPack(doc)); - Serial.print(" measureJson="); - Serial.print(measureJson(doc)); + // Serial.println(); + // Serial.print(" measureMsgPack="); + // Serial.print(measureMsgPack(doc)); + // Serial.print(" measureJson="); + // Serial.print(measureJson(doc)); Serial.println(" **"); } } @@ -964,7 +964,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // load devices test("boiler"); test("thermostat"); - // test("2thermostats"); if (single) { // run dedicated tests only @@ -973,28 +972,14 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const EMSESP::temperaturesensor_.test(); // add temperature sensors EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions - shell.invoke_command("call system fetch"); - request.url("/api/system/fetch"); + // shell.invoke_command("call system fetch"); + // request.url("/api/system/fetch"); + // EMSESP::webAPIService.webAPIService(&request); + + request.url("/api/thermostat"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/thermostat/hc1"); EMSESP::webAPIService.webAPIService(&request); - - // request.url("/api/system"); - // EMSESP::webAPIService.webAPIService(&request); - // request.url("/api/system/system/version"); - // EMSESP::webAPIService.webAPIService(&request); - // request.url("/api/system/bad"); - // EMSESP::webAPIService.webAPIService(&request); - - // request.url("/api/boiler"); - // EMSESP::webAPIService.webAPIService(&request); - // request.url("/api/boiler/bad"); - // EMSESP::webAPIService.webAPIService(&request); - - // request.url("/api/custom"); - // EMSESP::webAPIService.webAPIService(&request); - // request.url("/api/custom/seltemp"); - // EMSESP::webAPIService.webAPIService(&request); - // request.url("/api/custom/bad"); - // EMSESP::webAPIService.webAPIService(&request); } else { EMSESP::webCustomEntityService.test(); // custom entities @@ -1023,11 +1008,23 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const EMSESP::webAPIService.webAPIService(&request); request.url("/api/boiler/outdoortemp"); EMSESP::webAPIService.webAPIService(&request); - request.url("/api/boiler/dhw/chargetype/writeable"); + request.url("/api/boiler/dhw/chargetype"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/boiler/dhw.chargetype/writeable"); EMSESP::webAPIService.webAPIService(&request); request.url("/api/boiler/flamecurr/value"); EMSESP::webAPIService.webAPIService(&request); + // thermostat + request.url("/api/thermostat"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/thermostat/hc1/values"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/thermostat/hc1/seltemp"); + EMSESP::webAPIService.webAPIService(&request); + request.url("/api/thermostat/hc2/seltemp"); + EMSESP::webAPIService.webAPIService(&request); + // custom request.url("/api/custom"); EMSESP::webAPIService.webAPIService(&request); @@ -1045,6 +1042,8 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const EMSESP::webAPIService.webAPIService(&request); request.url("/api/system/fetch"); EMSESP::webAPIService.webAPIService(&request); + request.url("api/system/network/values"); + EMSESP::webAPIService.webAPIService(&request); // scheduler request.url("/api/scheduler"); @@ -1210,117 +1209,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const test("boiler"); test("thermostat"); - AsyncWebServerRequest requestX; - JsonDocument docX; - JsonVariant jsonX; - - requestX.method(HTTP_GET); - - /* - requestX.url("/api"); // should fail - EMSESP::webAPIService.webAPIService(&requestX); - return; - */ - - /* - requestX.url("/api/thermostat/seltemp"); - EMSESP::webAPIService.webAPIService(&requestX); - return; - */ - - /* - requestX.url("/api/thermostat/mode/auto"); - EMSESP::webAPIService.webAPIService(&requestX); - return; - */ - - /* - requestX.url("/api/thermostat"); // check if defaults to info - EMSESP::webAPIService.webAPIService(&requestX); - requestX.url("/api/thermostat/info"); - EMSESP::webAPIService.webAPIService(&requestX); - requestX.url("/api/thermostat/values"); - EMSESP::webAPIService.webAPIService(&requestX); - return; - - requestX.url("/api/thermostat/mode"); - EMSESP::webAPIService.webAPIService(&requestX); - return; - */ - - /* - requestX.url("/api/system"); // check if defaults to info - EMSESP::webAPIService.webAPIService(&requestX); - emsesp::EMSESP::logger().notice("*"); - - requestX.url("/api/system/info"); - EMSESP::webAPIService.webAPIService(&requestX); - emsesp::EMSESP::logger().notice("*"); - - requestX.url("/api/thermostat"); // check if defaults to values - EMSESP::webAPIService.webAPIService(&requestX); - emsesp::EMSESP::logger().notice("*"); - - requestX.url("/api/thermostat/info"); - EMSESP::webAPIService.webAPIService(&requestX); - emsesp::EMSESP::logger().notice("*"); - - requestX.url("/api/thermostat/seltemp"); - EMSESP::webAPIService.webAPIService(&requestX); - return; - */ - - /* - requestX.url("/api/system/restart"); - EMSESP::webAPIService.webAPIService(&requestX); - return; - */ - - /* - requestX.url("/api/temperaturesensor/xxxx"); - EMSESP::webAPIService.webAPIService(&requestX); - emsesp::EMSESP::logger().notice("****"); - requestX.url("/api/temperaturesensor/info"); - EMSESP::webAPIService.webAPIService(&requestX); - return; - */ - - /* - requestX.url("/api"); // should fail - EMSESP::webAPIService.webAPIService(&requestX); - */ - - requestX.method(HTTP_POST); - - /* - char dataX[] = "{\"device\":\"system\", \"entity\":\"settings\"}"; - deserializeJson(docX, dataX); - jsonX = docX.as(); - requestX.url("/api"); - EMSESP::webAPIService.webAPIService(&requestX, jsonX); - return; - */ - - /* - // char dataX[] = "{\"value\":\"0B 88 19 19 02\"}"; - char dataX[] = "{\"name\":\"temp\",\"value\":11}"; - deserializeJson(docX, dataX); - jsonX = docX.as(); - // requestX.url("/api/system/send"); - requestX.url("/api/thermostat"); - EMSESP::webAPIService.webAPIService(&requestX, jsonX); - return; - */ - - /* - char dataX[] = "{}"; - deserializeJson(docX, dataX); - jsonX = docX.as(); - requestX.url("/api/thermostat/mode/auto"); // should fail - EMSESP::webAPIService.webAPIService(&requestX, jsonX); - return; - */ - // test command parse int8_t id_n; const char * ncmd; @@ -1380,7 +1268,6 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // Web API TESTS AsyncWebServerRequest request; - request.method(HTTP_GET); request.url("/api/thermostat"); // check if defaults to info diff --git a/src/web/WebAPIService.cpp b/src/web/WebAPIService.cpp index 5880f4c98..1b12020df 100644 --- a/src/web/WebAPIService.cpp +++ b/src/web/WebAPIService.cpp @@ -64,7 +64,7 @@ void WebAPIService::webAPIService(AsyncWebServerRequest * request, JsonVariant j } #ifdef EMSESP_TEST -// for test.cpp so we can invoke GETs to test the API +// for test.cpp and unit tests so we can invoke GETs to test the API void WebAPIService::webAPIService(AsyncWebServerRequest * request) { JsonDocument input_doc; parse(request, input_doc.to()); @@ -134,7 +134,11 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { const char * api_data = output["api_data"]; if (api_data) { request->send(200, "text/plain; charset=utf-8", api_data); -#if defined(EMSESP_STANDALONE) +#if defined(EMSESP_TEST) + // store the result so we can test with Unity later + storeResponse(output); +#endif +#if defined(EMSESP_STANDALONE) && !defined(UNITY_INCLUDE_CONFIG_H) Serial.printf("%sweb output: %s[%s] %s(200)%s ", COLOR_WHITE, COLOR_BRIGHT_CYAN, request->url().c_str(), COLOR_BRIGHT_GREEN, COLOR_MAGENTA); serializeJson(output, Serial); Serial.println(COLOR_RESET); @@ -157,7 +161,11 @@ void WebAPIService::parse(AsyncWebServerRequest * request, JsonObject input) { request->send(response); api_count_++; -#if defined(EMSESP_STANDALONE) +#if defined(EMSESP_TEST) + // store the result so we can test with Unity later + storeResponse(output); +#endif +#if defined(EMSESP_STANDALONE) && !defined(UNITY_INCLUDE_CONFIG_H) Serial.printf("%sweb output: %s[%s]", COLOR_WHITE, COLOR_BRIGHT_CYAN, request->url().c_str()); Serial.printf(" %s(%d)%s ", ret_codes[return_code] == 200 ? COLOR_BRIGHT_GREEN : COLOR_BRIGHT_RED, ret_codes[return_code], COLOR_YELLOW); serializeJson(output, Serial); @@ -221,4 +229,19 @@ void WebAPIService::getEntities(AsyncWebServerRequest * request) { request->send(response); } +#if defined(EMSESP_TEST) +// store the result so we can test with Unity later +static JsonDocument storeResponseDoc_; + +void WebAPIService::storeResponse(JsonObject response) { + storeResponseDoc_.clear(); // clear it, so can only recall once + storeResponseDoc_.add(response); // add the object to our doc +} +const char * WebAPIService::getResponse() { + static std::string buffer; + serializeJson(storeResponseDoc_, buffer); + return buffer.c_str(); +} +#endif + } // namespace emsesp diff --git a/src/web/WebAPIService.h b/src/web/WebAPIService.h index 2c0c3ef7c..e47e19232 100644 --- a/src/web/WebAPIService.h +++ b/src/web/WebAPIService.h @@ -34,9 +34,11 @@ class WebAPIService { void webAPIService(AsyncWebServerRequest * request, JsonVariant json); -#ifdef EMSESP_TEST - // for test.cpp - void webAPIService(AsyncWebServerRequest * request); +#if defined(EMSESP_TEST) + // for test.cpp and running unit tests + void webAPIService(AsyncWebServerRequest * request); + void storeResponse(JsonObject response); + const char * getResponse(); #endif static uint32_t api_count() { diff --git a/test/README.txt b/test/README.txt deleted file mode 100644 index 292cb501d..000000000 --- a/test/README.txt +++ /dev/null @@ -1,6 +0,0 @@ -This folder contains the default data used when testing in standalone mode, purely for reference purposes. - -It is used for simulation and testing and can be invoked by compiling with -DEMSESP_TEST and from the Console using the command `test general` or via an API call like http://ems-esp.local/api?device=system&cmd=test&data=general - -To run in standalone mode without an ESP32 microcontroller use `make run` or `pio run -e standalone -t exec` - diff --git a/test/standalone_file_export/emsesp_customizations.json b/test/standalone_file_export/emsesp_customizations.json deleted file mode 100644 index 34523dfc7..000000000 --- a/test/standalone_file_export/emsesp_customizations.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "type": "customizations", - "Customizations": { - "ts": [ - { - "id": "01_0203_0405_0607", - "name": "test_sensor1", - "offset": 0 - }, - { - "id": "0B_0C0D_0E0F_1011", - "name": "test_sensor2", - "offset": 4 - } - ], - "as": [], - "masked_entities": [ - { - "product_id": 123, - "device_id": 8, - "custom_name": "Trendline", - "entity_ids": [ - "08heatingactive|is my heating on?" - ] - } - ] - } -} \ No newline at end of file diff --git a/test/standalone_file_export/emsesp_entities.json b/test/standalone_file_export/emsesp_entities.json deleted file mode 100644 index d31abfe07..000000000 --- a/test/standalone_file_export/emsesp_entities.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "type": "entities", - "Entities": { - "entities": [ - { - "id": 0, - "ram": 0, - "device_id": 8, - "type_id": 24, - "offset": 0, - "factor": 1, - "name": "test_custom", - "uom": 1, - "value_type": 1, - "writeable": true - }, - { - "id": 1, - "ram": 0, - "device_id": 24, - "type_id": 677, - "offset": 3, - "factor": 1, - "name": "test_read_only", - "uom": 0, - "value_type": 2, - "writeable": false - }, - { - "id": 2, - "ram": 1, - "device_id": 0, - "type_id": 0, - "offset": 0, - "factor": 1, - "name": "test_ram", - "uom": 0, - "value_type": 8, - "writeable": true, - "value": "14" - } - ] - } -} \ No newline at end of file diff --git a/test/standalone_file_export/emsesp_schedule.json b/test/standalone_file_export/emsesp_schedule.json deleted file mode 100644 index 4292ab6a8..000000000 --- a/test/standalone_file_export/emsesp_schedule.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "schedule", - "Schedule": { - "schedule": [ - { - "id": 0, - "active": true, - "flags": 1, - "time": "12:00", - "cmd": "system/fetch", - "value": "10", - "name": "test_scheduler" - } - ] - } -} \ No newline at end of file diff --git a/test/standalone_file_export/emsesp_settings.json b/test/standalone_file_export/emsesp_settings.json deleted file mode 100644 index 8d8d69f63..000000000 --- a/test/standalone_file_export/emsesp_settings.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "type": "settings", - "System": { - "version": "3.7.0" - }, - "Network": { - "ssid": "fake", - "bssid": "", - "password": "fake", - "hostname": "ems-esp", - "static_ip_config": false, - "bandwidth20": false, - "tx_power": 0, - "nosleep": false, - "enableMDNS": true, - "enableCORS": false, - "CORSOrigin": "*" - }, - "AP": { - "provision_mode": 1, - "ssid": "ems-esp", - "password": "ems-esp-neo", - "channel": 1, - "ssid_hidden": false, - "max_clients": 4, - "local_ip": "192.168.4.1", - "gateway_ip": "192.168.4.1", - "subnet_mask": "255.255.255.0" - }, - "MQTT": { - "enabled": true, - "host": "192.168.1.200", - "port": 1883, - "base": "ems-espS", - "username": "fake", - "password": "fake", - "client_id": "ems-esp", - "keep_alive": 60, - "clean_session": false, - "entity_format": 1, - "publish_time_boiler": 10, - "publish_time_thermostat": 10, - "publish_time_solar": 10, - "publish_time_mixer": 10, - "publish_time_other": 10, - "publish_time_sensor": 10, - "publish_time_heartbeat": 60, - "mqtt_qos": 0, - "mqtt_retain": false, - "ha_enabled": true, - "nested_format": 1, - "discovery_prefix": "homeassistant", - "discovery_type": 0, - "publish_single": false, - "publish_single2cmd": false, - "send_response": false - }, - "NTP": { - "enabled": false, - "server": "time.google.com", - "tz_label": "Europe/Amsterdam", - "tz_format": "CET-1CEST,M3.5.0,M10.5.0/3" - }, - "Security": { - "jwt_secret": "ems-esp-neo", - "users": [ - { - "username": "admin", - "password": "admin", - "admin": true - }, - { - "username": "guest", - "password": "guest", - "admin": false - } - ] - }, - "Settings": { - "version": "3.7.0", - "locale": "en", - "tx_mode": 1, - "ems_bus_id": 11, - "syslog_enabled": false, - "syslog_level": 3, - "trace_raw": false, - "syslog_mark_interval": 0, - "syslog_host": "", - "syslog_port": 514, - "boiler_heatingoff": false, - "shower_timer": false, - "shower_alert": false, - "shower_alert_coldshot": 10, - "shower_alert_trigger": 7, - "rx_gpio": 23, - "tx_gpio": 5, - "dallas_gpio": 18, - "dallas_parasite": false, - "led_gpio": 2, - "hide_led": false, - "low_clock": false, - "telnet_enabled": true, - "notoken_api": false, - "readonly_mode": false, - "analog_enabled": true, - "pbutton_gpio": 0, - "solar_maxflow": 30, - "board_profile": "S32", - "fahrenheit": false, - "bool_format": 1, - "bool_dashboard": 1, - "enum_format": 1, - "weblog_level": 6, - "weblog_buffer": 50, - "weblog_compact": true, - "phy_type": 0, - "eth_power": 0, - "eth_phy_addr": 0, - "eth_clock_mode": 0, - "platform": "ESP32" - } -} diff --git a/test/test_api/test_api.cpp b/test/test_api/test_api.cpp new file mode 100644 index 000000000..d701fca88 --- /dev/null +++ b/test/test_api/test_api.cpp @@ -0,0 +1,361 @@ +/* + * EMS-ESP - https://github.com/emsesp/EMS-ESP + * Copyright 2020-2024 Paul Derbyshire + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include +#include "ESPAsyncWebServer.h" +#include "ESP8266React.h" +#include "web/WebAPIService.h" + +using namespace emsesp; + +AsyncWebServer * webServer; +ESP8266React * esp8266React; +WebAPIService * webAPIService; +EMSESP application; +FS dummyFS; + +// forward declarations +void run_tests(); +const char * call_url(const char * url); + +// load the tests +// this is generated from this file when compiled with -DUNITY_CREATE +// copy the output to the test_api.h file +#include "test_api.h" // generated test functions + +// Unity's setup call - is called before each test +void setUp() { +} + +// Unity's teardown call - is called after each test +void tearDown() { +} + +// simulates a telegram straight from UART, but without the CRC which is added automatically +void uart_telegram(const std::vector & rx_data) { + uint8_t len = rx_data.size(); + uint8_t data[50]; + uint8_t i = 0; + while (len--) { + data[i] = rx_data[i]; + i++; + } + data[i] = EMSESP::rxservice_.calculate_crc(data, i); + EMSESP::incoming_telegram(data, i + 1); +} + +// simulates a telegram from a "string" of hex values +void uart_telegram(const char * rx_data) { + // since the telegram data is a const, make a copy. add 1 to grab the \0 EOS + char telegram[(EMS_MAX_TELEGRAM_LENGTH * 3) + 1]; + for (uint8_t i = 0; i < strlen(rx_data); i++) { + telegram[i] = rx_data[i]; + } + telegram[strlen(rx_data)] = '\0'; // make sure its terminated + + uint8_t count = 0; + char * p; + char value[10] = {0}; + + uint8_t data[EMS_MAX_TELEGRAM_LENGTH]; + + // get first value, which should be the src + if ((p = strtok(telegram, " ,"))) { // delimiter + strlcpy(value, p, 10); + data[0] = (uint8_t)strtol(value, 0, 16); + } + + // and iterate until end + while (p != 0) { + if ((p = strtok(nullptr, " ,"))) { + strlcpy(value, p, 10); + uint8_t val = (uint8_t)strtol(value, 0, 16); + data[++count] = val; + } + } + + if (count == 0) { + return; // nothing to send + } + + data[count + 1] = EMSESP::rxservice_.calculate_crc(data, count + 1); // add CRC + + EMSESP::incoming_telegram(data, count + 2); +} + +// add an EMS device and regiser it +void add_device(uint8_t device_id, uint8_t product_id) { + uart_telegram({device_id, 0x0B, EMSdevice::EMS_TYPE_VERSION, 0, product_id, 1, 0}); +} + +// add our EMS test devices +void add_devices() { + // + // boiler + // + add_device(0x08, 123); // Nefit Trendline + + // UBAuptime + uart_telegram({0x08, 0x0B, 0x14, 00, 0x3C, 0x1F, 0xAC, 0x70}); + + // Boiler -> Me, UBAMonitorFast(0x18), telegram: 08 00 18 00 00 02 5A 73 3D 0A 10 65 40 02 1A 80 00 01 E1 01 76 0E 3D 48 00 C9 44 02 00 (#data=25) + uart_telegram({0x08, 0x00, 0x18, 0x00, 0x00, 0x02, 0x5A, 0x73, 0x3D, 0x0A, 0x10, 0x65, 0x40, 0x02, 0x1A, + 0x80, 0x00, 0x01, 0xE1, 0x01, 0x76, 0x0E, 0x3D, 0x48, 0x00, 0xC9, 0x44, 0x02, 0x00}); + + // Boiler -> Me, UBAParameterWW(0x33), telegram: 08 0B 33 00 08 FF 34 FB 00 28 00 00 46 00 FF FF 00 (#data=13) + uart_telegram({0x08, 0x0B, 0x33, 0x00, 0x08, 0xFF, 0x34, 0xFB, 0x00, 0x28, 0x00, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0x00}); + + // + // thermostat + // + add_device(0x10, 192); // FW120 + + // HC1 - 3 + uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x6F, 0x03, 0x02, 0x00, 0xCD, 0x00, 0xE4}); + uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x70, 0x02, 0x01, 0x00, 0xCE, 0x00, 0xE5}); + uart_telegram({0x90, 0x00, 0xFF, 0x00, 0x00, 0x71, 0x01, 0x02, 0x00, 0xCF, 0x00, 0xE6}); + + // send the telegrams + EMSESP::rxservice_.loop(); +} + +// call the endpoint and get the response, GET +const char * call_url(const char * url) { + AsyncWebServerRequest request; + request.method(HTTP_GET); + request.url(url); + webAPIService->webAPIService(&request); + + return webAPIService->getResponse(); +} + +// call the endpoint and get the response, using a POST +const char * call_url(const char * url, const char * data) { + JsonDocument doc; + JsonVariant json; + + deserializeJson(doc, data); + json = doc.as(); + + AsyncWebServerRequest request; + request.method(HTTP_POST); + request.url(url); + webAPIService->webAPIService(&request, json); + + return webAPIService->getResponse(); +} + +// capture the response and print it out as a test, auto-generates the test functions +// use with -DUNITY_CREATE in the platformio build flags +// only needs to be done once +void capture(const char * url = nullptr) { + static uint8_t count = 1; + + if (count == 1) { + Serial.println(); + Serial.println("// ---------- START - CUT HERE ----------"); + Serial.println(); + } + + if (url) { + // call API, find and replace all double quotes with escaped quotes + std::string escaped_response = call_url(url); + + size_t pos = 0; + while ((pos = escaped_response.find("\"", pos)) != std::string::npos) { + escaped_response.replace(pos, 1, "\\\""); + pos += 2; + } + + Serial.printf("void test_%d() {\n", count++); + Serial.printf(" auto expected_response = \"%s\";\n", escaped_response.c_str()); + Serial.printf(" TEST_ASSERT_EQUAL_STRING(expected_response, call_url(\"%s\"));\n", url); + Serial.println("}"); + Serial.println(); + } else { + // no args means last call, create the run_tests function + Serial.println("void run_tests() {"); + for (uint8_t i = 1; i < count; i++) { + Serial.printf(" RUN_TEST(test_%d);\n", i); + } + Serial.println("}"); + Serial.println(); + Serial.println("// ---------- END - CUT HERE ----------"); + Serial.println(); + Serial.println(); + } +} + +// Functions for backup, just in case we don't have generated functions yet +// void test1() { +// TEST_ASSERT(expected_response != nullptr); +// } +// void run_tests() { +// RUN_TEST(test1); +// } + +void manual_test1() { + auto expected_response = "[{}]"; // empty is good + char data[] = "{\"cmd\":\"send\",\"data\":\"0B 90 FF 13 01 01 B9 01\"}"; + + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system", data)); +} + +void manual_test2() { + auto expected_response = "[{}]"; // empty is good + char data[] = "{\"value\":12}"; + + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/seltemp", data)); +} + +void manual_test3() { + auto expected_response = "[{}]"; // empty is good + char data[] = "{\"device\":\"thermostat\", \"cmd\":\"hc2.seltemp\",\"value\":14}"; + + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api", data)); +} + +void manual_test4() { + auto expected_response = "[{}]"; // empty is good + char data[] = "{\"entity\":\"seltemp\",\"value\":11}"; + + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat", data)); +} + +void run_manual_tests() { + RUN_TEST(manual_test1); + RUN_TEST(manual_test2); + RUN_TEST(manual_test3); + RUN_TEST(manual_test4); +} + +// Main entry point +int main() { + webServer = new AsyncWebServer(80); + esp8266React = new ESP8266React(webServer, &dummyFS); + webAPIService = new WebAPIService(webServer, esp8266React->getSecurityManager()); + + application.start(); // calls begin() + + EMSESP::webCustomEntityService.test(); // custom entities + EMSESP::webCustomizationService.test(); // set customizations - this will overwrite any settings in the FS + EMSESP::temperaturesensor_.test(); // add temperature sensors + EMSESP::webSchedulerService.test(); // run scheduler tests, and conditions + + add_devices(); // add devices + +#if defined(UNITY_CREATE) + + // These tests should all pass.... + + capture("/api/boiler"); + capture("/api/boiler/commands"); + capture("/api/boiler/values"); + capture("/api/boiler/info"); + // capture("/api/boiler/entities"); // skipping since payload is too large + capture("/api/boiler/comfort"); + capture("/api/boiler/comfort/value"); + capture("/api/boiler/comfort/fullname"); + capture("/api/boiler/outdoortemp"); + capture("/api/boiler/dhw/chargetype"); + capture("/api/boiler/dhw.chargetype/writeable"); + capture("/api/boiler/flamecurr/value"); + + // thermostat + capture("/api/thermostat"); + capture("/api/thermostat/hc1/values"); + capture("/api/thermostat/hc1/seltemp"); + capture("/api/thermostat/hc2/seltemp"); + + // custom + capture("/api/custom"); + capture("/api/custom/info"); + capture("/api/custom/seltemp"); + + // system + capture("/api/system"); + capture("/api/system/info"); + capture("/api/system/settings/locale"); + capture("/api/system/fetch"); + capture("api/system/network/values"); + + // scheduler + capture("/api/scheduler"); + capture("/api/scheduler/info"); + capture("/api/scheduler/test_scheduler"); + + // temperaturesensor + capture("/api/temperaturesensor"); + capture("/api/temperaturesensor/info"); + capture("/api/temperaturesensor/test_sensor2"); + capture("/api/temperaturesensor/0B_0C0D_0E0F_1011"); + capture("/api/temperaturesensor/test_sensor2/value"); + + // analogsensor + capture("/api/analogsensor"); + capture("/api/analogsensor/info"); + capture("/api/analogsensor/test_analog1"); + capture("/api/analogsensor/test_analog1/offset"); + + // these tests should all fail... + capture("/api/boiler2"); + capture("/api/boiler/bad/value"); + capture("/api/boiler/comfort/valu"); + + // system + capture("/api/system/settings/locale2"); + capture("/api/system/settings2"); + capture("/api/system/settings2/locale2"); + + + // scheduler + capture("/api/scheduler/test_scheduler2"); + capture("/api/scheduler/test_scheduler/val"); + capture("/api/scheduler/test_scheduler2/val2"); + + // custom + capture("/api/custom/seltemp2"); + capture("/api/custom/seltemp/val"); + + // temperaturesensor + capture("/api/temperaturesensor/test_sensor20"); + capture("/api/temperaturesensor/0B_0C0D_0E0F_XXXX"); + capture("/api/temperaturesensor/test_sensor2/bad"); + + // analogsensor + capture("/api/analogsensor/test_analog1/bad"); + capture("/api/analogsensor/test_analog10"); + capture("/api/analogsensor/test_analog10/bad2"); + + // ************************************************************************************************** + // Finish + capture(); // always end with this, this will create the run_test() function +#endif + + // always run the tests + UNITY_BEGIN(); + + run_tests(); // execute the generated tests + run_manual_tests(); // execute some other manual tests from this file + + return UNITY_END(); +} diff --git a/test/test_api/test_api.h b/test/test_api/test_api.h new file mode 100644 index 000000000..3452fe9b8 --- /dev/null +++ b/test/test_api/test_api.h @@ -0,0 +1,422 @@ +// ************************************************************************************************** +// +// Compile with -DUNITY_CREATE to generate the test functions +// and copy the output and paste below. +// +// TODO: convert output to JSON and compare, showing differences +// +// You can also manually compare the differences using https://www.diffchecker.com/text-compare/ +// +// ************************************************************************************************** + +// ---------- START - CUT HERE ---------- + +void test_1() { + auto expected_response = + "[{\"reset\":\"\",\"heatingoff\":\"off\",\"heatingactive\":\"off\",\"tapwateractive\":\"on\",\"selflowtemp\":0,\"curflowtemp\":60.2,\"rettemp\":48.1," + "\"syspress\":1.4,\"burngas\":\"on\",\"burngas2\":\"off\",\"flamecurr\":37.4,\"fanwork\":\"on\",\"ignwork\":\"off\",\"oilpreheat\":\"off\"," + "\"heatingpump\":\"on\",\"selburnpow\":115,\"curburnpow\":61,\"ubauptime\":3940268,\"servicecode\":\"=H\",\"servicecodenumber\":201,\"nompower\":0," + "\"nrgtotal\":0.0,\"nrgheat\":0.0,\"dhw\":{\"seltemp\":52,\"comfort\":\"hot\",\"flowtempoffset\":40,\"circpump\":\"off\",\"chargetype\":\"3-way " + "valve\",\"hyston\":-5,\"hystoff\":0,\"disinfectiontemp\":70,\"circmode\":\"off\",\"circ\":\"off\",\"storagetemp1\":53.8,\"activated\":\"on\"," + "\"3wayvalve\":\"on\",\"nrg\":0.0}}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler")); +} + +void test_2() { + auto expected_response = + "[{\"info\":\"list all values (verbose)\",\"values\":\"list all values\",\"commands\":\"list all commands\",\"entities\":\"list all " + "entities\",\"boil2hystoff\":\"hysteresis stage 2 off temperature\",\"boil2hyston\":\"hysteresis stage 2 on temperature\",\"boilhystoff\":\"hysteresis " + "off temperature\",\"boilhyston\":\"hysteresis on temperature\",\"burnmaxpower\":\"burner max power\",\"burnminperiod\":\"burner min " + "period\",\"burnminpower\":\"burner min power\",\"coldshot\":\"send a cold shot of water\",\"curvebase\":\"heatingcurve " + "base\",\"curveend\":\"heatingcurve end\",\"curveon\":\"heatingcurve on\",\"dhw[n].activated\":\"activated\",\"dhw[n].chargeoptimization\":\"charge " + "optimization\",\"dhw[n].circ\":\"circulation active\",\"dhw[n].circmode\":\"circulation pump mode\",\"dhw[n].circpump\":\"circulation pump " + "available\",\"dhw[n].comfort\":\"comfort\",\"dhw[n].comfort1\":\"comfort " + "mode\",\"dhw[n].disinfecting\":\"disinfecting\",\"dhw[n].disinfectiontemp\":\"disinfection temperature\",\"dhw[n].flowtempoffset\":\"flow temperature " + "offset\",\"dhw[n].hystoff\":\"hysteresis off temperature\",\"dhw[n].hyston\":\"hysteresis on temperature\",\"dhw[n].maxpower\":\"max " + "power\",\"dhw[n].maxtemp\":\"maximum temperature\",\"dhw[n].nrg\":\"energy\",\"dhw[n].onetime\":\"one time charging\",\"dhw[n].seltemp\":\"selected " + "temperature\",\"dhw[n].seltemplow\":\"selected lower temperature\",\"dhw[n].seltempsingle\":\"single charge " + "temperature\",\"dhw[n].tapactivated\":\"turn on/off\",\"dhw[n].tempecoplus\":\"selected eco+ temperature\",\"emergencyops\":\"emergency " + "operation\",\"emergencytemp\":\"emergency temperature\",\"heatingactivated\":\"heating activated\",\"heatingoff\":\"force heating " + "off\",\"heatingtemp\":\"heating temperature\",\"maintenance\":\"maintenance scheduled\",\"maintenancedate\":\"next maintenance " + "date\",\"maintenancetime\":\"time to next maintenance\",\"nofrostmode\":\"nofrost mode\",\"nofrosttemp\":\"nofrost " + "temperature\",\"nompower\":\"nominal Power\",\"nrgheat\":\"energy heating\",\"pumpcharacter\":\"boiler pump characteristic\",\"pumpdelay\":\"pump " + "delay\",\"pumpmode\":\"boiler pump mode\",\"pumpmodmax\":\"boiler pump max power\",\"pumpmodmin\":\"boiler pump min " + "power\",\"reset\":\"reset\",\"selburnpow\":\"burner selected max power\",\"selflowtemp\":\"selected flow temperature\",\"summertemp\":\"summer " + "temperature\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/commands")); +} + +void test_3() { + auto expected_response = + "[{\"reset\":\"\",\"heatingoff\":\"off\",\"heatingactive\":\"off\",\"tapwateractive\":\"on\",\"selflowtemp\":0,\"curflowtemp\":60.2,\"rettemp\":48.1," + "\"syspress\":1.4,\"burngas\":\"on\",\"burngas2\":\"off\",\"flamecurr\":37.4,\"fanwork\":\"on\",\"ignwork\":\"off\",\"oilpreheat\":\"off\"," + "\"heatingpump\":\"on\",\"selburnpow\":115,\"curburnpow\":61,\"ubauptime\":3940268,\"servicecode\":\"=H\",\"servicecodenumber\":201,\"nompower\":0," + "\"nrgtotal\":0.0,\"nrgheat\":0.0,\"dhw\":{\"seltemp\":52,\"comfort\":\"hot\",\"flowtempoffset\":40,\"circpump\":\"off\",\"chargetype\":\"3-way " + "valve\",\"hyston\":-5,\"hystoff\":0,\"disinfectiontemp\":70,\"circmode\":\"off\",\"circ\":\"off\",\"storagetemp1\":53.8,\"activated\":\"on\"," + "\"3wayvalve\":\"on\",\"nrg\":0.0}}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/values")); +} + +void test_4() { + auto expected_response = + "[{\"reset (reset)\":\"\",\"force heating off (heatingoff)\":\"off\",\"is my heating on? (heatingactive)\":\"off\",\"tapwater active " + "(tapwateractive)\":\"on\",\"selected flow temperature (selflowtemp)\":0,\"current flow temperature (curflowtemp)\":60.2,\"return temperature " + "(rettemp)\":48.1,\"system pressure (syspress)\":1.4,\"gas (burngas)\":\"on\",\"gas stage 2 (burngas2)\":\"off\",\"flame current " + "(flamecurr)\":37.4,\"fan (fanwork)\":\"on\",\"ignition (ignwork)\":\"off\",\"oil preheating (oilpreheat)\":\"off\",\"heating pump " + "(heatingpump)\":\"on\",\"burner selected max power (selburnpow)\":115,\"burner current power (curburnpow)\":61,\"total UBA operating time " + "(ubauptime)\":\"2736 days 7 hours 8 minutes\",\"service code (servicecode)\":\"=H\",\"service code number (servicecodenumber)\":201,\"dhw selected " + "temperature (seltemp)\":52,\"dhw comfort (comfort)\":\"hot\",\"dhw flow temperature offset (flowtempoffset)\":40,\"dhw circulation pump available " + "(circpump)\":\"off\",\"dhw charging type (chargetype)\":\"3-way valve\",\"dhw hysteresis on temperature (hyston)\":-5,\"dhw hysteresis off " + "temperature (hystoff)\":0,\"dhw disinfection temperature (disinfectiontemp)\":70,\"dhw circulation pump mode (circmode)\":\"off\",\"dhw circulation " + "active (circ)\":\"off\",\"dhw storage intern temperature (storagetemp1)\":53.8,\"dhw activated (activated)\":\"on\",\"dhw 3-way valve active " + "(3wayvalve)\":\"on\",\"nominal Power (nompower)\":0,\"total energy (nrgtotal)\":0.0,\"energy heating (nrgheat)\":0.0,\"dhw energy (nrg)\":0.0}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/info")); +} + +void test_5() { + auto expected_response = "[{\"name\":\"comfort\",\"fullname\":\"dhw " + "comfort\",\"circuit\":\"dhw\",\"index\":0,\"enum\":[\"hot\",\"eco\",\"intelligent\"],\"value\":\"hot\",\"type\":\"enum\"," + "\"readable\":true,\"writeable\":true,\"visible\":true}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort")); +} + +void test_6() { + auto expected_response = "[{\"api_data\":\"hot\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/value")); +} + +void test_7() { + auto expected_response = "[{\"api_data\":\"dhw comfort\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/fullname")); +} + +void test_8() { + auto expected_response = "[{\"name\":\"outdoortemp\",\"fullname\":\"outside " + "temperature\",\"circuit\":\"\",\"type\":\"number\",\"uom\":\"°C\",\"readable\":true,\"writeable\":false,\"visible\":true}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/outdoortemp")); +} + +void test_9() { + auto expected_response = "[{\"name\":\"chargetype\",\"fullname\":\"dhw charging type\",\"circuit\":\"dhw\",\"index\":1,\"enum\":[\"chargepump\",\"3-way " + "valve\"],\"value\":\"3-way valve\",\"type\":\"enum\",\"readable\":true,\"writeable\":false,\"visible\":true}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/dhw/chargetype")); +} + +void test_10() { + auto expected_response = "[{\"api_data\":\"false\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/dhw.chargetype/writeable")); +} + +void test_11() { + auto expected_response = "[{\"api_data\":\"37.4\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/flamecurr/value")); +} + +void test_12() { + auto expected_response = "[{\"hc1\":{\"seltemp\":20.5,\"currtemp\":22.8,\"haclimate\":\"roomTemp\",\"modetype\":\"heat\",\"remotetemp\":null},\"hc2\":{" + "\"seltemp\":20.6,\"currtemp\":22.9,\"haclimate\":\"roomTemp\",\"modetype\":\"eco\",\"remotetemp\":null},\"hc3\":{\"seltemp\":20." + "7,\"currtemp\":23.0,\"haclimate\":\"roomTemp\",\"modetype\":\"nofrost\",\"remotetemp\":null},\"dhw\":{}}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat")); +} + +void test_13() { + auto expected_response = "[{\"seltemp\":20.5,\"currtemp\":22.8,\"haclimate\":\"roomTemp\",\"modetype\":\"heat\",\"remotetemp\":null}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/hc1/values")); +} + +void test_14() { + auto expected_response = "[{\"name\":\"seltemp\",\"fullname\":\"hc1 selected room " + "temperature\",\"circuit\":\"hc1\",\"value\":20.5,\"type\":\"number\",\"min\":0,\"max\":30,\"uom\":\"°C\",\"readable\":true," + "\"writeable\":true,\"visible\":true}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/hc1/seltemp")); +} + +void test_15() { + auto expected_response = "[{\"name\":\"seltemp\",\"fullname\":\"hc2 selected room " + "temperature\",\"circuit\":\"hc2\",\"value\":20.6,\"type\":\"number\",\"min\":0,\"max\":30,\"uom\":\"°C\",\"readable\":true," + "\"writeable\":true,\"visible\":true}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/thermostat/hc2/seltemp")); +} + +void test_16() { + auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":0.00,\"test_ram\":\"14\",\"seltemp\":\"14\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom")); +} + +void test_17() { + auto expected_response = "[{\"test_custom\":0.00,\"test_read_only\":0.00,\"test_ram\":\"14\",\"seltemp\":\"14\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/info")); +} + +void test_18() { + auto expected_response = + "[{\"name\":\"seltemp\",\"storage\":\"ram\",\"type\":\"number\",\"readable\":true,\"writeable\":true,\"visible\":true,\"value\":\"14\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp")); +} + +void test_19() { + auto expected_response = + "[{\"system\":{\"version\":\"3.7.0-dev.29\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " + "Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," + "\"disableSleep\":false,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{},\"mqtt\":{\"MQTTStatus\":\"disconnected\",\"MQTTPublishes\":0," + "\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTConnects\":1,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,\"cleanSession\":false," + "\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,\"haEnabled\":true,\"mqttQos\":0," + "\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,\"publishTimeSolar\":10,\"publishTimeMixer\":" + "10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,\"publish2command\":false,\"sendResponse\":false}," + "\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,\"temperatureSensorFails\":0,\"analogSensors\":2," + "\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":" + "\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0," + "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11," + "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":" + "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true," + "\"maxWebLogBuffer\":50,\"webLogBuffer\":0,\"modbusEnabled\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"Custom " + "Name!!\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":37,\"handlersReceived\":\"0x18\"," + "\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x2E " + "0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15," + "\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 0x0168\"}]}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system")); +} + +void test_20() { + auto expected_response = + "[{\"system\":{\"version\":\"3.7.0-dev.29\",\"uptime\":\"000+00:00:00.000\",\"uptimeSec\":0,\"resetReason\":\"Unknown / " + "Unknown\"},\"network\":{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":-23,\"TxPowerSetting\":0,\"staticIP\":false,\"lowBandwidth\":false," + "\"disableSleep\":false,\"enableMDNS\":true,\"enableCORS\":false},\"ntp\":{},\"mqtt\":{\"MQTTStatus\":\"disconnected\",\"MQTTPublishes\":0," + "\"MQTTQueued\":0,\"MQTTPublishFails\":0,\"MQTTConnects\":1,\"enabled\":true,\"clientID\":\"ems-esp\",\"keepAlive\":60,\"cleanSession\":false," + "\"entityFormat\":1,\"base\":\"ems-esp\",\"discoveryPrefix\":\"homeassistant\",\"discoveryType\":0,\"nestedFormat\":1,\"haEnabled\":true,\"mqttQos\":0," + "\"mqttRetain\":false,\"publishTimeHeartbeat\":60,\"publishTimeBoiler\":10,\"publishTimeThermostat\":10,\"publishTimeSolar\":10,\"publishTimeMixer\":" + "10,\"publishTimeWater\":0,\"publishTimeOther\":10,\"publishTimeSensor\":10,\"publishSingle\":false,\"publish2command\":false,\"sendResponse\":false}," + "\"syslog\":{\"enabled\":false},\"sensor\":{\"temperatureSensors\":2,\"temperatureSensorReads\":0,\"temperatureSensorFails\":0,\"analogSensors\":2," + "\"analogSensorReads\":0,\"analogSensorFails\":0},\"api\":{\"APICalls\":0,\"APIFails\":0},\"bus\":{\"busStatus\":\"connected\",\"busProtocol\":" + "\"Buderus\",\"busTelegramsReceived\":8,\"busReads\":0,\"busWrites\":0,\"busIncompleteTelegrams\":0,\"busReadsFailed\":0,\"busWritesFailed\":0," + "\"busRxLineQuality\":100,\"busTxLineQuality\":100},\"settings\":{\"boardProfile\":\"S32\",\"locale\":\"en\",\"txMode\":8,\"emsBusID\":11," + "\"showerTimer\":false,\"showerMinDuration\":180,\"showerAlert\":false,\"hideLed\":false,\"noTokenApi\":false,\"readonlyMode\":false,\"fahrenheit\":" + "false,\"dallasParasite\":false,\"boolFormat\":1,\"boolDashboard\":1,\"enumFormat\":1,\"analogEnabled\":true,\"telnetEnabled\":true," + "\"maxWebLogBuffer\":50,\"webLogBuffer\":0,\"modbusEnabled\":false},\"devices\":[{\"type\":\"boiler\",\"name\":\"Custom " + "Name!!\",\"deviceID\":\"0x08\",\"productID\":123,\"brand\":\"\",\"version\":\"01.00\",\"entities\":37,\"handlersReceived\":\"0x18\"," + "\"handlersFetched\":\"0x14 0x33\",\"handlersPending\":\"0xBF 0x10 0x11 0xC2 0x15 0x1C 0x19 0x1A 0x35 0x34 0x2A 0xD1 0xE3 0xE4 0xE5 0xE9 0x2E " + "0x3B\"},{\"type\":\"thermostat\",\"name\":\"FW120\",\"deviceID\":\"0x10\",\"productID\":192,\"brand\":\"\",\"version\":\"01.00\",\"entities\":15," + "\"handlersReceived\":\"0x016F\",\"handlersFetched\":\"0x0170 0x0171\",\"handlersPending\":\"0xA3 0x06 0xA2 0x12 0x13 0x0172 0x0165 0x0168\"}]}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/info")); +} + +void test_21() { + auto expected_response = + "[{\"name\":\"locale\",\"circuit\":\"settings\",\"readable\":true,\"writable\":false,\"visible\":true,\"value\":\"en\",\"type\":\"string\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings/locale")); +} + +void test_22() { + auto expected_response = "[{}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/fetch")); +} + +void test_23() { + auto expected_response = "[{\"network\":\"WiFi\",\"hostname\":\"ems-esp\",\"RSSI\":\"-23\",\"TxPowerSetting\":\"0\",\"staticIP\":\"false\"," + "\"lowBandwidth\":\"false\",\"disableSleep\":\"false\",\"enableMDNS\":\"true\",\"enableCORS\":\"false\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("api/system/network/values")); +} + +void test_24() { + auto expected_response = "[{\"test_scheduler\":\"on\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler")); +} + +void test_25() { + auto expected_response = "[{\"test_scheduler\":\"on\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/info")); +} + +void test_26() { + auto expected_response = "[{\"name\":\"test_scheduler\",\"type\":\"boolean\",\"value\":\"on\",\"time\":\"12:00\",\"command\":\"system/" + "fetch\",\"cmd_data\":\"10\",\"readable\":true,\"writeable\":true,\"visible\":true}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler")); +} + +void test_27() { + auto expected_response = "[{\"test_sensor1\":12.3,\"test_sensor2\":45.6}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor")); +} + +void test_28() { + auto expected_response = "[{\"test_sensor1\":12.3,\"test_sensor2\":45.6}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/info")); +} + +void test_29() { + auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_sensor2\",\"value\":45.6,\"type\":\"number\",\"uom\":\"°C\",\"writeable\":false}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor2")); +} + +void test_30() { + auto expected_response = "[{\"id\":\"0B_0C0D_0E0F_1011\",\"name\":\"test_sensor2\",\"value\":45.6,\"type\":\"number\",\"uom\":\"°C\",\"writeable\":false}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_1011")); +} + +void test_31() { + auto expected_response = "[{\"api_data\":\"45.6\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor2/value")); +} + +void test_32() { + auto expected_response = "[{\"test_analog1\":0,\"test_analog2\":1}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor")); +} + +void test_33() { + auto expected_response = "[{\"test_analog1\":0,\"test_analog2\":1}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/info")); +} + +void test_34() { + auto expected_response = + "[{\"gpio\":36,\"type\":\"number\",\"analog\":\"adc\",\"value\":0,\"writeable\":false,\"offset\":0,\"factor\":0.1,\"uom\":\"mV\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog1")); +} + +void test_35() { + auto expected_response = "[{\"api_data\":\"0\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog1/offset")); +} + +void test_36() { + auto expected_response = "[{\"message\":\"unknown device boiler2\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler2")); +} + +void test_37() { + auto expected_response = "[{\"message\":\"no bad in boiler\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/bad/value")); +} + +void test_38() { + auto expected_response = "[{\"message\":\"Command comfort failed (Error)\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/boiler/comfort/valu")); +} + +void test_39() { + auto expected_response = "[{\"message\":\"no settings in system\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings/locale2")); +} + +void test_40() { + auto expected_response = "[{\"message\":\"no settings2 in system\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2")); +} + +void test_41() { + auto expected_response = "[{\"message\":\"no settings2 in system\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/system/settings2/locale2")); +} + +void test_42() { + auto expected_response = "[{\"message\":\"no test_scheduler2 in scheduler\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2")); +} + +void test_43() { + auto expected_response = "[{\"message\":\"no val in test_scheduler\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler/val")); +} + +void test_44() { + auto expected_response = "[{\"message\":\"no test_scheduler2 in scheduler\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/scheduler/test_scheduler2/val2")); +} + +void test_45() { + auto expected_response = "[{\"message\":\"no seltemp2 in custom\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp2")); +} + +void test_46() { + auto expected_response = "[{\"message\":\"no val in seltemp\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/custom/seltemp/val")); +} + +void test_47() { + auto expected_response = "[{\"message\":\"no test_sensor20 in temperaturesensor\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor20")); +} + +void test_48() { + auto expected_response = "[{\"message\":\"no 0b_0c0d_0e0f_xxxx in temperaturesensor\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/0B_0C0D_0E0F_XXXX")); +} + +void test_49() { + auto expected_response = "[{\"message\":\"no bad in test_sensor2\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/temperaturesensor/test_sensor2/bad")); +} + +void test_50() { + auto expected_response = "[{\"message\":\"no bad in test_analog1\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog1/bad")); +} + +void test_51() { + auto expected_response = "[{\"message\":\"no test_analog10 in analogsensor\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10")); +} + +void test_52() { + auto expected_response = "[{\"message\":\"no test_analog10 in analogsensor\"}]"; + TEST_ASSERT_EQUAL_STRING(expected_response, call_url("/api/analogsensor/test_analog10/bad2")); +} + +void run_tests() { + RUN_TEST(test_1); + RUN_TEST(test_2); + RUN_TEST(test_3); + RUN_TEST(test_4); + RUN_TEST(test_5); + RUN_TEST(test_6); + RUN_TEST(test_7); + RUN_TEST(test_8); + RUN_TEST(test_9); + RUN_TEST(test_10); + RUN_TEST(test_11); + RUN_TEST(test_12); + RUN_TEST(test_13); + RUN_TEST(test_14); + RUN_TEST(test_15); + RUN_TEST(test_16); + RUN_TEST(test_17); + RUN_TEST(test_18); + RUN_TEST(test_19); + RUN_TEST(test_20); + RUN_TEST(test_21); + RUN_TEST(test_22); + RUN_TEST(test_23); + RUN_TEST(test_24); + RUN_TEST(test_25); + RUN_TEST(test_26); + RUN_TEST(test_27); + RUN_TEST(test_28); + RUN_TEST(test_29); + RUN_TEST(test_30); + RUN_TEST(test_31); + RUN_TEST(test_32); + RUN_TEST(test_33); + RUN_TEST(test_34); + RUN_TEST(test_35); + RUN_TEST(test_36); + RUN_TEST(test_37); + RUN_TEST(test_38); + RUN_TEST(test_39); + RUN_TEST(test_40); + RUN_TEST(test_41); + RUN_TEST(test_42); + RUN_TEST(test_43); + RUN_TEST(test_44); + RUN_TEST(test_45); + RUN_TEST(test_46); + RUN_TEST(test_47); + RUN_TEST(test_48); + RUN_TEST(test_49); + RUN_TEST(test_50); + RUN_TEST(test_51); + RUN_TEST(test_52); +} + +// ---------- END - CUT HERE ---------- diff --git a/test/unity_config.h b/test/unity_config.h new file mode 100644 index 000000000..b7524f2d8 --- /dev/null +++ b/test/unity_config.h @@ -0,0 +1,251 @@ +/* ========================================================================= + Unity - A Test Framework for C + ThrowTheSwitch.org + Copyright (c) 2007-24 Mike Karlesky, Mark VanderVoord, & Greg Williams + SPDX-License-Identifier: MIT +========================================================================= */ + +/* Unity Configuration + * As of May 11th, 2016 at ThrowTheSwitch/Unity commit 837c529 + * Update: December 29th, 2016 + * See Also: Unity/docs/UnityConfigurationGuide.pdf + * + * Unity is designed to run on almost anything that is targeted by a C compiler. + * It would be awesome if this could be done with zero configuration. While + * there are some targets that come close to this dream, it is sadly not + * universal. It is likely that you are going to need at least a couple of the + * configuration options described in this document. + * + * All of Unity's configuration options are `#defines`. Most of these are simple + * definitions. A couple are macros with arguments. They live inside the + * unity_internals.h header file. We don't necessarily recommend opening that + * file unless you really need to. That file is proof that a cross-platform + * library is challenging to build. From a more positive perspective, it is also + * proof that a great deal of complexity can be centralized primarily to one + * place in order to provide a more consistent and simple experience elsewhere. + * + * Using These Options + * It doesn't matter if you're using a target-specific compiler and a simulator + * or a native compiler. In either case, you've got a couple choices for + * configuring these options: + * + * 1. Because these options are specified via C defines, you can pass most of + * these options to your compiler through command line compiler flags. Even + * if you're using an embedded target that forces you to use their + * overbearing IDE for all configuration, there will be a place somewhere in + * your project to configure defines for your compiler. + * 2. You can create a custom `unity_config.h` configuration file (present in + * your toolchain's search paths). In this file, you will list definitions + * and macros specific to your target. All you must do is define + * `UNITY_INCLUDE_CONFIG_H` and Unity will rely on `unity_config.h` for any + * further definitions it may need. + */ + +#ifndef UNITY_CONFIG_H +#define UNITY_CONFIG_H + +/* ************************* AUTOMATIC INTEGER TYPES *************************** + * C's concept of an integer varies from target to target. The C Standard has + * rules about the `int` matching the register size of the target + * microprocessor. It has rules about the `int` and how its size relates to + * other integer types. An `int` on one target might be 16 bits while on another + * target it might be 64. There are more specific types in compilers compliant + * with C99 or later, but that's certainly not every compiler you are likely to + * encounter. Therefore, Unity has a number of features for helping to adjust + * itself to match your required integer sizes. It starts off by trying to do it + * automatically. + **************************************************************************** */ + +/* The first attempt to guess your types is to check `limits.h`. Some compilers + * that don't support `stdint.h` could include `limits.h`. If you don't + * want Unity to check this file, define this to make it skip the inclusion. + * Unity looks at UINT_MAX & ULONG_MAX, which were available since C89. + */ +/* #define UNITY_EXCLUDE_LIMITS_H */ + +/* The second thing that Unity does to guess your types is check `stdint.h`. + * This file defines `UINTPTR_MAX`, since C99, that Unity can make use of to + * learn about your system. It's possible you don't want it to do this or it's + * possible that your system doesn't support `stdint.h`. If that's the case, + * you're going to want to define this. That way, Unity will know to skip the + * inclusion of this file and you won't be left with a compiler error. + */ +/* #define UNITY_EXCLUDE_STDINT_H */ + +/* ********************** MANUAL INTEGER TYPE DEFINITION *********************** + * If you've disabled all of the automatic options above, you're going to have + * to do the configuration yourself. There are just a handful of defines that + * you are going to specify if you don't like the defaults. + **************************************************************************** */ + +/* Define this to be the number of bits an `int` takes up on your system. The + * default, if not auto-detected, is 32 bits. + * + * Example: + */ +/* #define UNITY_INT_WIDTH 16 */ + +/* Define this to be the number of bits a `long` takes up on your system. The + * default, if not autodetected, is 32 bits. This is used to figure out what + * kind of 64-bit support your system can handle. Does it need to specify a + * `long` or a `long long` to get a 64-bit value. On 16-bit systems, this option + * is going to be ignored. + * + * Example: + */ +/* #define UNITY_LONG_WIDTH 16 */ + +/* Define this to be the number of bits a pointer takes up on your system. The + * default, if not autodetected, is 32-bits. If you're getting ugly compiler + * warnings about casting from pointers, this is the one to look at. + * + * Example: + */ +/* #define UNITY_POINTER_WIDTH 64 */ + +/* Unity will automatically include 64-bit support if it auto-detects it, or if + * your `int`, `long`, or pointer widths are greater than 32-bits. Define this + * to enable 64-bit support if none of the other options already did it for you. + * There can be a significant size and speed impact to enabling 64-bit support + * on small targets, so don't define it if you don't need it. + */ +/* #define UNITY_INCLUDE_64 */ + + +/* *************************** FLOATING POINT TYPES **************************** + * In the embedded world, it's not uncommon for targets to have no support for + * floating point operations at all or to have support that is limited to only + * single precision. We are able to guess integer sizes on the fly because + * integers are always available in at least one size. Floating point, on the + * other hand, is sometimes not available at all. Trying to include `float.h` on + * these platforms would result in an error. This leaves manual configuration as + * the only option. + **************************************************************************** */ + +/* By default, Unity guesses that you will want single precision floating point + * support, but not double precision. It's easy to change either of these using + * the include and exclude options here. You may include neither, just float, + * or both, as suits your needs. + */ +/* #define UNITY_EXCLUDE_FLOAT */ +/* #define UNITY_INCLUDE_DOUBLE */ +/* #define UNITY_EXCLUDE_DOUBLE */ + +/* For features that are enabled, the following floating point options also + * become available. + */ + +/* Unity aims for as small of a footprint as possible and avoids most standard + * library calls (some embedded platforms don't have a standard library!). + * Because of this, its routines for printing integer values are minimalist and + * hand-coded. To keep Unity universal, though, we eventually chose to develop + * our own floating point print routines. Still, the display of floating point + * values during a failure are optional. By default, Unity will print the + * actual results of floating point assertion failures. So a failed assertion + * will produce a message like "Expected 4.0 Was 4.25". If you would like less + * verbose failure messages for floating point assertions, use this option to + * give a failure message `"Values Not Within Delta"` and trim the binary size. + */ +/* #define UNITY_EXCLUDE_FLOAT_PRINT */ + +/* If enabled, Unity assumes you want your `FLOAT` asserts to compare standard C + * floats. If your compiler supports a specialty floating point type, you can + * always override this behavior by using this definition. + * + * Example: + */ +/* #define UNITY_FLOAT_TYPE float16_t */ + +/* If enabled, Unity assumes you want your `DOUBLE` asserts to compare standard + * C doubles. If you would like to change this, you can specify something else + * by using this option. For example, defining `UNITY_DOUBLE_TYPE` to `long + * double` could enable gargantuan floating point types on your 64-bit processor + * instead of the standard `double`. + * + * Example: + */ +/* #define UNITY_DOUBLE_TYPE long double */ + +/* If you look up `UNITY_ASSERT_EQUAL_FLOAT` and `UNITY_ASSERT_EQUAL_DOUBLE` as + * documented in the Unity Assertion Guide, you will learn that they are not + * really asserting that two values are equal but rather that two values are + * "close enough" to equal. "Close enough" is controlled by these precision + * configuration options. If you are working with 32-bit floats and/or 64-bit + * doubles (the normal on most processors), you should have no need to change + * these options. They are both set to give you approximately 1 significant bit + * in either direction. The float precision is 0.00001 while the double is + * 10^-12. For further details on how this works, see the appendix of the Unity + * Assertion Guide. + * + * Example: + */ +/* #define UNITY_FLOAT_PRECISION 0.001f */ +/* #define UNITY_DOUBLE_PRECISION 0.001f */ + + +/* *************************** MISCELLANEOUS *********************************** + * Miscellaneous configuration options for Unity + **************************************************************************** */ + +/* Unity uses the stddef.h header included in the C standard library for the + * "NULL" macro. Define this in order to disable the include of stddef.h. If you + * do this, you have to make sure to provide your own "NULL" definition. + */ +/* #define UNITY_EXCLUDE_STDDEF_H */ + +/* Define this to enable the unity formatted print macro: + * "TEST_PRINTF" + */ +/* #define UNITY_INCLUDE_PRINT_FORMATTED */ + + +/* *************************** TOOLSET CUSTOMIZATION *************************** + * In addition to the options listed above, there are a number of other options + * which will come in handy to customize Unity's behavior for your specific + * toolchain. It is possible that you may not need to touch any of these but + * certain platforms, particularly those running in simulators, may need to jump + * through extra hoops to operate properly. These macros will help in those + * situations. + **************************************************************************** */ + +/* By default, Unity prints its results to `stdout` as it runs. This works + * perfectly fine in most situations where you are using a native compiler for + * testing. It works on some simulators as well so long as they have `stdout` + * routed back to the command line. There are times, however, where the + * simulator will lack support for dumping results or you will want to route + * results elsewhere for other reasons. In these cases, you should define the + * `UNITY_OUTPUT_CHAR` macro. This macro accepts a single character at a time + * (as an `int`, since this is the parameter type of the standard C `putchar` + * function most commonly used). You may replace this with whatever function + * call you like. + * + * Example: + * Say you are forced to run your test suite on an embedded processor with no + * `stdout` option. You decide to route your test result output to a custom + * serial `RS232_putc()` function you wrote like thus: + */ +/* #define UNITY_OUTPUT_CHAR(a) RS232_putc(a) */ +/* #define UNITY_OUTPUT_CHAR_HEADER_DECLARATION RS232_putc(int) */ +/* #define UNITY_OUTPUT_FLUSH() RS232_flush() */ +/* #define UNITY_OUTPUT_FLUSH_HEADER_DECLARATION RS232_flush(void) */ +/* #define UNITY_OUTPUT_START() RS232_config(115200,1,8,0) */ +/* #define UNITY_OUTPUT_COMPLETE() RS232_close() */ + +/* Some compilers require a custom attribute to be assigned to pointers, like + * `near` or `far`. In these cases, you can give Unity a safe default for these + * by defining this option with the attribute you would like. + * + * Example: + */ +/* #define UNITY_PTR_ATTRIBUTE __attribute__((far)) */ +/* #define UNITY_PTR_ATTRIBUTE near */ + +/* Print execution time of each test when executed in verbose mode + * + * Example: + * + * TEST - PASS (10 ms) + */ +/* #define UNITY_INCLUDE_EXEC_TIME */ + +#endif /* UNITY_CONFIG_H */ \ No newline at end of file