From 76d4b3ddc9dd9b5fc2eb8a0216977e6ed6935881 Mon Sep 17 00:00:00 2001 From: Jesus Carrillo Date: Tue, 22 Jul 2025 13:53:50 -0700 Subject: [PATCH 1/2] Cleanup tests --- .github/workflows/ci-cd.yml | 77 ++--- .github/workflows/size-baseline.yml | 28 +- Makefile | 24 +- platformio.ini | 46 +-- test/stubs/Adafruit_MAX1704X.h | 10 - test/stubs/Arduino.cpp | 88 ------ test/stubs/Arduino.h | 130 -------- test/stubs/ArduinoJson.h | 75 ----- test/stubs/CayenneLPP.cpp | 2 - test/stubs/CayenneLPP.h | 19 -- test/stubs/FastLED.h | 39 --- test/stubs/HTTPClient.h | 31 -- test/stubs/Preferences.h | 83 ----- test/stubs/String.h | 14 - test/stubs/WiFi.h | 153 ---------- test/stubs/WiFiManager.h | 43 --- test/stubs/Wire.h | 14 - test/stubs/config.hpp | 26 -- test/stubs/driver/adc.h | 10 - test/stubs/esp32_gpio.h | 29 -- test/stubs/esp_bt.h | 8 - test/stubs/esp_sleep.h | 24 -- test/stubs/esp_system.h | 9 - test/stubs/hal/hal.h | 2 - test/stubs/lmic.h | 86 ------ test/stubs/lmic_stub.cpp | 11 - test/stubs/lorawan_settings.cpp | 14 - test/stubs/lorawan_settings.hpp | 18 -- test/stubs/mbedtls.h | 39 --- test/stubs/menu_stub.cpp | 39 --- test/stubs/pgmspace.h | 10 - .../test_constants_simple.cpp | 48 --- test/test_main/test_constants.cpp | 53 ---- .../test_main_constants.cpp | 71 ----- .../test_memory_management.cpp | 84 ------ .../test_ota_signature/test_ota_signature.cpp | 283 ------------------ test/test_utils/test_safe_strncpy.cpp | 115 ------- test/test_utils/test_safe_strncpy_bounds.cpp | 25 -- test/test_utils_simple/test_utils_simple.cpp | 143 --------- .../test_wifi_auto_test.cpp | 180 ----------- test/test_wifi_simple/test_wifi_simple.cpp | 127 -------- 41 files changed, 55 insertions(+), 2275 deletions(-) delete mode 100644 test/stubs/Adafruit_MAX1704X.h delete mode 100644 test/stubs/Arduino.cpp delete mode 100644 test/stubs/Arduino.h delete mode 100644 test/stubs/ArduinoJson.h delete mode 100644 test/stubs/CayenneLPP.cpp delete mode 100644 test/stubs/CayenneLPP.h delete mode 100644 test/stubs/FastLED.h delete mode 100644 test/stubs/HTTPClient.h delete mode 100644 test/stubs/Preferences.h delete mode 100644 test/stubs/String.h delete mode 100644 test/stubs/WiFi.h delete mode 100644 test/stubs/WiFiManager.h delete mode 100644 test/stubs/Wire.h delete mode 100644 test/stubs/config.hpp delete mode 100644 test/stubs/driver/adc.h delete mode 100644 test/stubs/esp32_gpio.h delete mode 100644 test/stubs/esp_bt.h delete mode 100644 test/stubs/esp_sleep.h delete mode 100644 test/stubs/esp_system.h delete mode 100644 test/stubs/hal/hal.h delete mode 100644 test/stubs/lmic.h delete mode 100644 test/stubs/lmic_stub.cpp delete mode 100644 test/stubs/lorawan_settings.cpp delete mode 100644 test/stubs/lorawan_settings.hpp delete mode 100644 test/stubs/mbedtls.h delete mode 100644 test/stubs/menu_stub.cpp delete mode 100644 test/stubs/pgmspace.h delete mode 100644 test/test_constants_simple/test_constants_simple.cpp delete mode 100644 test/test_main/test_constants.cpp delete mode 100644 test/test_main_constants/test_main_constants.cpp delete mode 100644 test/test_memory_management/test_memory_management.cpp delete mode 100644 test/test_ota_signature/test_ota_signature.cpp delete mode 100644 test/test_utils/test_safe_strncpy.cpp delete mode 100644 test/test_utils/test_safe_strncpy_bounds.cpp delete mode 100644 test/test_utils_simple/test_utils_simple.cpp delete mode 100644 test/test_wifi_auto_test/test_wifi_auto_test.cpp delete mode 100644 test/test_wifi_simple/test_wifi_simple.cpp diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 468a6e4..360767c 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -17,7 +17,7 @@ env: jobs: # Stage 1: Branch Development Pipeline branch-pipeline: - name: Branch Pipeline (Unit Test + Compile + Coverage + SonarQube) + name: Branch Pipeline (Compile + SonarQube) runs-on: ubuntu-latest if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref != 'refs/heads/main') steps: @@ -44,19 +44,19 @@ jobs: - name: Install dependencies run: | pip install --upgrade platformio - pip install gcovr==7.2 sudo apt-get install build-essential bc jq - - name: Run unit tests (native) - run: pio test -e native -f test_ota_simple -f test_wifi_simple -f test_utils_simple -f test_constants_simple - - name: Compile project - run: pio run -e ttgo-lora32-v1-release + - name: Compile project (ttgo-lora32-v1) + run: pio run -e ttgo-lora32-v1 + + - name: Compile project (minimal) + run: pio run -e minimal - name: Check flash size limit run: | # Get build output and extract flash usage - pio run -e ttgo-lora32-v1-release 2>&1 | tee build_output.log + pio run -e ttgo-lora32-v1 2>&1 | tee build_output.log # Extract flash usage from build output FLASH_USED=$(grep -oP 'Flash:.*used \K\d+' build_output.log | tail -n 1) @@ -91,22 +91,16 @@ jobs: with: name: build-artifacts-branch-${{ github.sha }} path: | - .pio/build/ttgo-lora32-v1-release/firmware.map - .pio/build/ttgo-lora32-v1-release/firmware.elf + .pio/build/ttgo-lora32-v1/firmware.map + .pio/build/ttgo-lora32-v1/firmware.elf + .pio/build/minimal/firmware.map + .pio/build/minimal/firmware.elf retention-days: 30 if: always() - name: Generate Compilation DB run: pio run -t compiledb - - name: Generate coverage report - run: | - gcovr \ - --root . \ - --exclude '\.pio/libdeps/' \ - --exclude 'tests/.*' \ - --sonarqube \ - --output coverage.xml - name: SonarQube Scan uses: SonarSource/sonarqube-scan-action@v5 @@ -115,11 +109,10 @@ jobs: with: args: > --define sonar.cfamily.compile-commands="compile_commands.json" - --define sonar.coverageReportPaths="coverage.xml" # Stage 2: Main Branch Pipeline main-pipeline: - name: Main Pipeline (Unit Test + Compile + Coverage + SonarQube) + name: Main Pipeline (Compile + SonarQube) runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: @@ -146,19 +139,18 @@ jobs: - name: Install dependencies run: | pip install --upgrade platformio - pip install gcovr==7.2 sudo apt-get install build-essential bc jq - - name: Run unit tests (native) - run: pio test -e native -f test_ota_simple -f test_wifi_simple -f test_utils_simple -f test_constants_simple + - name: Compile project (ttgo-lora32-v1) + run: pio run -e ttgo-lora32-v1 - - name: Compile project - run: pio run -e ttgo-lora32-v1-release + - name: Compile project (minimal) + run: pio run -e minimal - name: Check flash size limit run: | # Get build output and extract flash usage - pio run -e ttgo-lora32-v1-release 2>&1 | tee build_output.log + pio run -e ttgo-lora32-v1 2>&1 | tee build_output.log # Extract flash usage from build output FLASH_USED=$(grep -oP 'Flash:.*used \K\d+' build_output.log | tail -n 1) @@ -193,23 +185,16 @@ jobs: with: name: build-artifacts-main-${{ github.sha }} path: | - .pio/build/ttgo-lora32-v1-release/firmware.map - .pio/build/ttgo-lora32-v1-release/firmware.elf + .pio/build/ttgo-lora32-v1/firmware.map + .pio/build/ttgo-lora32-v1/firmware.elf + .pio/build/minimal/firmware.map + .pio/build/minimal/firmware.elf retention-days: 30 if: always() - name: Generate Compilation DB run: pio run -t compiledb - - name: Generate coverage report - run: | - gcovr \ - --root . \ - --exclude '\.pio/libdeps/' \ - --exclude 'tests/.*' \ - --sonarqube \ - --output coverage.xml - - name: SonarQube Scan uses: SonarSource/sonarqube-scan-action@v5 env: @@ -217,7 +202,6 @@ jobs: with: args: > --define sonar.cfamily.compile-commands="compile_commands.json" - --define sonar.coverageReportPaths="coverage.xml" # Stage 3: Release Pipeline release-pipeline: @@ -283,16 +267,17 @@ jobs: pip install --upgrade platformio sudo apt-get install build-essential bc jq - - name: Run unit tests (native) - run: pio test -e native -f test_ota_simple -f test_wifi_simple -f test_utils_simple -f test_constants_simple - - name: Compile with updated firmware version - run: pio run -e ttgo-lora32-v1-release + - name: Compile with updated firmware version (ttgo-lora32-v1) + run: pio run -e ttgo-lora32-v1 + + - name: Compile with updated firmware version (minimal) + run: pio run -e minimal - name: Check flash size limit run: | # Get build output and extract flash usage - pio run -e ttgo-lora32-v1-release 2>&1 | tee build_output.log + pio run -e ttgo-lora32-v1 2>&1 | tee build_output.log # Extract flash usage from build output FLASH_USED=$(grep -oP 'Flash:.*used \K\d+' build_output.log | tail -n 1) @@ -325,10 +310,10 @@ jobs: - name: Create release artifacts run: | mkdir -p artifacts - cp .pio/build/ttgo-lora32-v1-release/firmware.bin artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.bin - cp .pio/build/ttgo-lora32-v1-release/firmware.elf artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.elf - cp .pio/build/ttgo-lora32-v1-release/firmware.map artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.map - cp .pio/build/ttgo-lora32-v1-release/partitions.bin artifacts/partitions-${{ steps.validate-tag.outputs.tag_name }}.bin + cp .pio/build/ttgo-lora32-v1/firmware.bin artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.bin + cp .pio/build/ttgo-lora32-v1/firmware.elf artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.elf + cp .pio/build/ttgo-lora32-v1/firmware.map artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.map + cp .pio/build/ttgo-lora32-v1/partitions.bin artifacts/partitions-${{ steps.validate-tag.outputs.tag_name }}.bin # Create a manifest file with build info cat > artifacts/build-info-${{ steps.validate-tag.outputs.tag_name }}.json << EOF diff --git a/.github/workflows/size-baseline.yml b/.github/workflows/size-baseline.yml index 056b7dc..84dafbc 100644 --- a/.github/workflows/size-baseline.yml +++ b/.github/workflows/size-baseline.yml @@ -38,8 +38,8 @@ jobs: - name: Build release and capture artifacts run: | - # Clean build with verbose output - use release environment for size baseline - pio run -e ttgo-lora32-v1-release -v > ci-build-release.log 2>&1 || RELEASE_BUILD_FAILED=1 + # Clean build with verbose output - use ttgo-lora32-v1 environment for size baseline + pio run -e ttgo-lora32-v1 -v be ci-build-release.log 21 || RELEASE_BUILD_FAILED=1 # Create baseline artifacts directory mkdir -p ci-baseline-artifacts @@ -48,14 +48,14 @@ jobs: cp ci-build-release.log ci-baseline-artifacts/ # Copy generated files if they exist - if [ -f ".pio/build/ttgo-lora32-v1-release/firmware.elf" ]; then - cp .pio/build/ttgo-lora32-v1-release/firmware.elf ci-baseline-artifacts/release-firmware.elf + if [ -f ".pio/build/ttgo-lora32-v1/firmware.elf" ]; then + cp .pio/build/ttgo-lora32-v1/firmware.elf ci-baseline-artifacts/release-firmware.elf else echo "WARNING: release firmware.elf not found" >> ci-baseline-artifacts/build-warnings.txt fi - if [ -f ".pio/build/ttgo-lora32-v1-release/firmware.map" ]; then - cp .pio/build/ttgo-lora32-v1-release/firmware.map ci-baseline-artifacts/release-firmware.map + if [ -f ".pio/build/ttgo-lora32-v1/firmware.map" ]; then + cp .pio/build/ttgo-lora32-v1/firmware.map ci-baseline-artifacts/release-firmware.map else echo "WARNING: release firmware.map not found" >> ci-baseline-artifacts/build-warnings.txt fi @@ -71,7 +71,7 @@ jobs: echo "" >> ci-baseline-artifacts/ci-memory-usage.txt # Extract RAM and Flash usage from build log - echo "RELEASE BUILD (ttgo-lora32-v1-release):" >> ci-baseline-artifacts/ci-memory-usage.txt + echo "TTGO-LORA32-V1 BUILD (ttgo-lora32-v1):" >> ci-baseline-artifacts/ci-memory-usage.txt echo "========================================" >> ci-baseline-artifacts/ci-memory-usage.txt if grep -q "RAM:\|Flash:" ci-build-release.log; then grep -A 2 -B 2 "RAM:\|Flash:" ci-build-release.log >> ci-baseline-artifacts/ci-memory-usage.txt @@ -91,27 +91,27 @@ jobs: - name: Build debug and capture artifacts run: | - # Build debug environment for comparison - pio run -e ttgo-lora32-v1-debug -v > ci-build-debug.log 2>&1 || DEBUG_BUILD_FAILED=1 + # Build minimal environment for comparison + pio run -e minimal -v be ci-build-debug.log 21 || DEBUG_BUILD_FAILED=1 # Copy debug build log cp ci-build-debug.log ci-baseline-artifacts/ # Copy debug generated files if they exist - if [ -f ".pio/build/ttgo-lora32-v1-debug/firmware.elf" ]; then - cp .pio/build/ttgo-lora32-v1-debug/firmware.elf ci-baseline-artifacts/debug-firmware.elf + if [ -f ".pio/build/minimal/firmware.elf" ]; then + cp .pio/build/minimal/firmware.elf ci-baseline-artifacts/debug-firmware.elf else echo "WARNING: debug firmware.elf not found" >> ci-baseline-artifacts/build-warnings.txt fi - if [ -f ".pio/build/ttgo-lora32-v1-debug/firmware.map" ]; then - cp .pio/build/ttgo-lora32-v1-debug/firmware.map ci-baseline-artifacts/debug-firmware.map + if [ -f ".pio/build/minimal/firmware.map" ]; then + cp .pio/build/minimal/firmware.map ci-baseline-artifacts/debug-firmware.map else echo "WARNING: debug firmware.map not found" >> ci-baseline-artifacts/build-warnings.txt fi # Add debug build info to memory usage report - echo "DEBUG BUILD (ttgo-lora32-v1-debug):" >> ci-baseline-artifacts/ci-memory-usage.txt + echo "MINIMAL BUILD (minimal):" >> ci-baseline-artifacts/ci-memory-usage.txt echo "====================================" >> ci-baseline-artifacts/ci-memory-usage.txt if grep -q "RAM:\|Flash:" ci-build-debug.log; then grep -A 2 -B 2 "RAM:\|Flash:" ci-build-debug.log >> ci-baseline-artifacts/ci-memory-usage.txt diff --git a/Makefile b/Makefile index 3d3a6bf..a10ec41 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # CREATED BY VIM-PIO -all: +all: release platformio -f -c vim run upload: @@ -15,22 +15,12 @@ uploadfs: platformio -f -c vim run --target uploadfs # Build the optimized release firmware -.PHONY: release -release: - platformio -f -c vim run -e ttgo-lora32-v1-release - -# Build and immediately upload the release firmware -.PHONY: upload-release -upload-release: release - platformio -f -c vim run -e ttgo-lora32-v1-release --target upload - -.PHONY: test -test: - platformio test -e native - -.PHONY: test-cli -test-cli: - cd cli && go test ./... -v +.PHONY: release minimal +release: ## Build main release env + platformio run -e ttgo-lora32-v1 +minimal: + platformio run -e minimal + .PHONY: build-cli build-cli: diff --git a/platformio.ini b/platformio.ini index bfb8ce7..da06725 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = ttgo-lora32-v1-debug +default_envs = ttgo-lora32-v1 [options] ; Common build flags for all configurations @@ -58,34 +58,11 @@ release_build_flags = release_lib_deps = ${options.common_lib_deps} -unittesting_buildflag = -D unitTesting -generic_hw_buildflag = -D generic_hw -coverage_buildflag = - -lgcov - --coverage - -fprofile-abs-path platform = espressif32 board = ttgo-lora32-v1 framework = arduino -[env:ttgo-lora32-v1-debug] -monitor_speed = 115200 -platform = ${options.platform} -board = ${options.board} -framework = ${options.framework} -board_build.partition_table = huge_app.csv -board_build.maximum_size = 1572864 -; Skip program size check -extra_scripts = pre:skip_size_check.py -; Override the board's default maximum size -board_upload.maximum_size = 1572864 -build_flags = - ${options.debug_build_flags} - -std=gnu++14 -lib_deps = ${options.debug_lib_deps} - -[env:ttgo-lora32-v1-release] -monitor_speed = 115200 +[env:ttgo-lora32-v1] platform = ${options.platform} board = ${options.board} framework = ${options.framework} @@ -98,8 +75,7 @@ build_flags = -std=gnu++14 lib_deps = ${options.release_lib_deps} -[env:ttgo-lora32-v1-minimal] -monitor_speed = 115200 +[env:minimal] platform = ${options.platform} board = ${options.board} framework = ${options.framework} @@ -111,23 +87,7 @@ build_flags = ${options.common_build_flags} -std=gnu++14 -D MINIMAL_BUILD=1 - -D LMIC_DEBUG_LEVEL=0 - -D DISABLE_OTA=1 lib_deps = mcci-catena/MCCI LoRaWAN LMIC library @ ^4.1.1 adafruit/Adafruit MAX1704X@^1.0.3 -# Native host environment used only for unit testing of platform-independent -# helper functions. This allows running `pio test` on any CI host without -# requiring embedded tool-chains or hardware. -[env:native] -platform = native -build_flags = - ${options.unittesting_buildflag} - -std=c++17 - -Itest/stubs - ${options.coverage_buildflag} - -DUNIT_TEST -build_src_filter = +<*> + - + + -lib_deps = - ArduinoJson @ ^7.2.0 diff --git a/test/stubs/Adafruit_MAX1704X.h b/test/stubs/Adafruit_MAX1704X.h deleted file mode 100644 index fd6c5fc..0000000 --- a/test/stubs/Adafruit_MAX1704X.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include - -class Adafruit_MAX17048 { -public: - bool begin() { return false; } - float cellVoltage() { return 0.0f; } - float cellPercent() { return 0.0f; } - float chargeRate() { return 0.0f; } -}; diff --git a/test/stubs/Arduino.cpp b/test/stubs/Arduino.cpp deleted file mode 100644 index 4f4be52..0000000 --- a/test/stubs/Arduino.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "Arduino.h" -#include "WiFi.h" -#include "FastLED.h" -#include "lmic.h" -#include "lorawan_settings.hpp" - -// OTA structure definition for native testing -struct OtaUpdateInfo { - String url; - String md5sum; - String version; -}; - -// Global instances -SerialClass Serial; -WiFiClass WiFi; -FastLEDClass FastLED; -ESPClass ESP; - -// LMIC global instance -lmic_t LMIC = {}; - -// Settings global instance -Preferences settings; - -// Global lpp instance -CayenneLPP lpp(51); - -// WiFi global variables for UNIT_TEST -#ifdef UNIT_TEST -char wifi_ssid_str[32] = ""; -char wifi_password_str[64] = ""; -#endif - -// Arduino function implementations -void delay(unsigned long) {} -unsigned long millis() { return 0; } - -void pinMode(int, int) {} -void digitalWrite(int, int) {} -int digitalRead(int) { return 0; } -int analogRead(int) { return 0; } - -void randomSeed(unsigned long) {} -int random(int) { return 0; } -int random(int, int) { return 0; } - -// LMIC function implementations -void LMIC_reset() {} -void LMIC_setLinkCheckMode(int) {} -void LMIC_selectSubBand(int) {} -void LMIC_shutdown() {} -void LMIC_setTxData2(uint8_t, const uint8_t*, uint8_t, uint8_t) {} -void os_init() {} -void os_runloop_once() {} -bool os_queryTimeCriticalJobs(unsigned long) { return false; } -unsigned long os_getTime() { return 0; } -unsigned long ms2osticksRound(unsigned long x) { return x; } -unsigned long osticks2ms(unsigned long x) { return x; } -unsigned long LMICbandplan_nextTx(unsigned long) { return 0; } -void LMIC_getSessionKeys(uint32_t*, uint32_t*, uint8_t*, uint8_t*) {} -void os_getArtEui(uint8_t*) {} -void os_getDevEui(uint8_t*) {} -void os_getDevKey(uint8_t*) {} - -// Settings function implementations -String settings_get_string(const char* key) { return String(""); } -void settings_put_string(const char* key, const String& value) {} -void settings_clear() {} -bool settings_has_key(const char* key) { return false; } -bool settings_get_bool(const char* key, bool defaultValue) { return defaultValue; } - -// OTA function implementations (stubs for native testing) -bool verifyMd5Sum(const uint8_t* data, unsigned long len, const String& expectedHash) { return false; } - -// Calibration function implementations with return statements -int get_calibration_air_value() { return 0; } -int get_calibration_water_value() { return 0; } - -// Missing function implementations -void LoraWANDebug(const lmic_t&) {} -void handleDownlinkMessage(uint8_t*, uint8_t) {} -void reportFirmwareVersion(class CayenneLPP&) {} - -// Additional missing functions -void loadSetings() {} -void initMenu() {} -void saveConfigCallback() {} \ No newline at end of file diff --git a/test/stubs/Arduino.h b/test/stubs/Arduino.h deleted file mode 100644 index 13c2fdd..0000000 --- a/test/stubs/Arduino.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef ARDUINO_H -#define ARDUINO_H - -#include -#include -#include -#include - -// Minimal Arduino String class -class String { -private: - std::string str; - -public: - String() : str("") {} - String(const char* s) : str(s ? s : "") {} - String(const char* s, size_t n) : str(s ? std::string(s, n) : "") {} - String(const String& s) : str(s.str) {} - String(const std::string& s) : str(s) {} - - const char* c_str() const { return str.c_str(); } - int length() const { return static_cast(str.length()); } - bool isEmpty() const { return str.empty(); } - - String& operator=(const char* s) { str = s ? s : ""; return *this; } - String& operator=(const String& s) { str = s.str; return *this; } - - bool operator==(const String& s) const { return str == s.str; } - bool operator==(const char* s) const { return str == (s ? s : ""); } - - char operator[](int index) const { return str[index]; } - char& operator[](int index) { return str[index]; } - - String substring(int start, int end = -1) const { - if (end == -1) end = static_cast(str.length()); - if (start < 0) start = 0; - if (end > static_cast(str.length())) end = static_cast(str.length()); - if (start >= end) return String(""); - return String(str.substr(start, end - start)); - } - - int toInt() const { - try { return std::stoi(str); } catch (...) { return 0; } - } - - float toFloat() const { - try { return std::stof(str); } catch (...) { return 0.0f; } - } - - // ArduinoJson compatibility - int read() { - static size_t read_pos = 0; - if (read_pos < str.length()) return str[read_pos++]; - return -1; - } -}; - -// Basic Arduino types -typedef unsigned char uint8_t; -typedef signed char int8_t; -typedef unsigned short uint16_t; -typedef signed short int16_t; -typedef unsigned int uint32_t; -typedef signed int int32_t; - -// Basic Arduino constants -#define HIGH 1 -#define LOW 0 -#define INPUT 0 -#define OUTPUT 1 -#define DEC 10 -#define HEX 16 -#define F(x) x - -// Basic Arduino functions -void delay(unsigned long ms); -unsigned long millis(); -void digitalWrite(int pin, int value); -int digitalRead(int pin); -void pinMode(int pin, int mode); -int analogRead(int pin); -int map(int value, int fromLow, int fromHigh, int toLow, int toHigh); -float map(float value, float fromLow, float fromHigh, float toLow, float toHigh); - -// memcpy_P stub -#define memcpy_P(dest, src, n) std::memcpy((dest), (src), (n)) - -// Minimal Serial class -class SerialClass { -public: - void begin(unsigned long baud) {} - void print(const char* str) {} - void print(const String& str) {} - void print(int value) {} - void print(int value, int base) {} - void print(unsigned int value) {} - void print(unsigned int value, int base) {} - void print(long value) {} - void print(long value, int base) {} - void print(unsigned long value) {} - void print(unsigned long value, int base) {} - void print(float value) {} - void println(const char* str) {} - void println(const String& str) {} - void println(int value) {} - void println(int value, int base) {} - void println(unsigned int value) {} - void println(unsigned int value, int base) {} - void println(long value) {} - void println(long value, int base) {} - void println(unsigned long value) {} - void println(unsigned long value, int base) {} - void println(float value) {} - void println() {} - void printf(const char* format, ...) {} - int available() { return 0; } - int read() { return -1; } -}; - -extern SerialClass Serial; - -// ESP32 specific -class ESPClass { -public: - void restart() {} -}; - -extern ESPClass ESP; - -#endif // ARDUINO_H diff --git a/test/stubs/ArduinoJson.h b/test/stubs/ArduinoJson.h deleted file mode 100644 index 2a2652e..0000000 --- a/test/stubs/ArduinoJson.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef ARDUINOJSON_H -#define ARDUINOJSON_H - -#include -#include -#include - -class DynamicJsonDocument { -private: - size_t capacity_; - void* data_; - std::string url_; - std::string md5sum_; - std::string version_; - -public: - DynamicJsonDocument(size_t capacity) : capacity_(capacity), data_(nullptr) { - // Set default test values - url_ = "https://example.com/firmware.bin"; - md5sum_ = "d41d8cd98f00b204e9800998ecf8427e"; - version_ = "101"; - } - ~DynamicJsonDocument() {} - - bool containsKey(const char* key) const { - return (strcmp(key, "url") == 0 || - strcmp(key, "md5sum") == 0 || - strcmp(key, "version") == 0); - } - - String operator[](const char* key) const { - if (strcmp(key, "url") == 0) return String(url_.c_str()); - if (strcmp(key, "md5sum") == 0) return String(md5sum_.c_str()); - if (strcmp(key, "version") == 0) return String(version_.c_str()); - return String(""); - } -}; - -enum DeserializationErrorType { - DeserializationError_Ok, - DeserializationError_InvalidInput, - DeserializationError_NoMemory, - DeserializationError_EmptyInput, - DeserializationError_IncompleteInput -}; - -class DeserializationError { -private: - DeserializationErrorType error_; - -public: - DeserializationError(DeserializationErrorType error) : error_(error) {} - bool operator!() const { return error_ == DeserializationError_Ok; } - const char* c_str() const { return "Test error"; } -}; - -DeserializationError deserializeJson(DynamicJsonDocument& doc, const String& input) { - // Convert Arduino String to std::string for native compatibility - std::string inputStr = input.c_str(); - - // Check for malformed JSON - if (inputStr.find("invalid json") != std::string::npos) { - return DeserializationError(DeserializationError_InvalidInput); - } - - // Check for missing fields - if (inputStr.find("\"md5sum\"") == std::string::npos || - inputStr.find("\"version\"") == std::string::npos) { - return DeserializationError(DeserializationError_InvalidInput); - } - - return DeserializationError(DeserializationError_Ok); -} - -#endif // ARDUINOJSON_H \ No newline at end of file diff --git a/test/stubs/CayenneLPP.cpp b/test/stubs/CayenneLPP.cpp deleted file mode 100644 index 294728b..0000000 --- a/test/stubs/CayenneLPP.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "CayenneLPP.h" -CayenneLPP lpp; \ No newline at end of file diff --git a/test/stubs/CayenneLPP.h b/test/stubs/CayenneLPP.h deleted file mode 100644 index 6dc2cd2..0000000 --- a/test/stubs/CayenneLPP.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef CAYENNE_LPP_H -#define CAYENNE_LPP_H - -#include - -class CayenneLPP { -public: - void reset() {} - void addGenericSensor(uint8_t, float) {} - void addVoltage(uint8_t, float) {} - void addPercentage(uint8_t, float) {} - void addSwitch(uint8_t, bool) {} - uint8_t* getBuffer() { static uint8_t buf[64] = {0}; return buf; } - uint8_t getSize() { return 0; } -}; - -extern CayenneLPP lpp; - -#endif // CAYENNE_LPP_H diff --git a/test/stubs/FastLED.h b/test/stubs/FastLED.h deleted file mode 100644 index f28abb5..0000000 --- a/test/stubs/FastLED.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef FASTLED_H -#define FASTLED_H - -#include - -struct CRGB { - uint8_t r, g, b; - - CRGB() : r(0), g(0), b(0) {} - CRGB(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} - - // Predefined colors - static const CRGB Black; - static const CRGB Red; - static const CRGB Green; - static const CRGB Orange; - static const CRGB DarkOrange; -}; - -// Define the static members as inline to avoid multiple definition errors -inline const CRGB CRGB::Black(0, 0, 0); -inline const CRGB CRGB::Red(255, 0, 0); -inline const CRGB CRGB::Green(0, 255, 0); -inline const CRGB CRGB::Orange(255, 165, 0); -inline const CRGB CRGB::DarkOrange(255, 140, 0); - -class FastLEDClass { -public: - static void addLeds(int, int, int) {} - static void setBrightness(int) {} - static void show() {} -}; - -#define WS2812B 0 -#define GRB 0 - -extern FastLEDClass FastLED; - -#endif // FASTLED_H diff --git a/test/stubs/HTTPClient.h b/test/stubs/HTTPClient.h deleted file mode 100644 index 29e01e6..0000000 --- a/test/stubs/HTTPClient.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef HTTPCLIENT_H -#define HTTPCLIENT_H - -#include -#include - -#define HTTP_CODE_OK 200 -#define UPDATE_SIZE_UNKNOWN 0 - -class HTTPClient { -public: - HTTPClient() {} - ~HTTPClient() {} - - void begin(const String& url) {} - int GET() { return HTTP_CODE_OK; } - int getSize() { return 1024; } - String errorToString(int code) { return "Test error"; } - void end() {} - WiFiClient* getStreamPtr() { return nullptr; } -}; - -class Update { -public: - static bool begin(size_t size) { return true; } - static size_t writeStream(WiFiClient& client) { return 1024; } - static bool setMD5(const char* md5) { return true; } - static bool end() { return true; } -}; - -#endif // HTTPCLIENT_H \ No newline at end of file diff --git a/test/stubs/Preferences.h b/test/stubs/Preferences.h deleted file mode 100644 index 64eed6b..0000000 --- a/test/stubs/Preferences.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef PREFERENCES_H -#define PREFERENCES_H - -#include -#include -#include -#include - -class Preferences { -private: - std::unordered_map stringStore; - std::unordered_map longStore; - std::unordered_map> bytesStore; - -public: - bool begin(const char* name, bool readOnly = false) { return true; } - void end() {} - - String getString(const char* key, const String& defaultValue = "") { - auto it = stringStore.find(key); - return (it != stringStore.end()) ? String(it->second.c_str()) : defaultValue; - } - void putString(const char* key, const String& value) { - stringStore[key] = value.c_str(); - } - - bool getBool(const char* key, bool defaultValue = false) { return defaultValue; } - void putBool(const char* key, bool value) {} - - int getInt(const char* key, int defaultValue = 0) { return defaultValue; } - void putInt(const char* key, int value) {} - - long getLong(const char* key, long defaultValue = 0) { - auto it = longStore.find(key); - return (it != longStore.end()) ? it->second : defaultValue; - } - void putLong(const char* key, long value) { - longStore[key] = value; - } - - bool isKey(const char* key) { - return stringStore.find(key) != stringStore.end() || - longStore.find(key) != longStore.end() || - bytesStore.find(key) != bytesStore.end(); - } - - size_t putBytes(const char* key, const void* value, size_t len) { - const uint8_t* data = static_cast(value); - bytesStore[key] = std::vector(data, data + len); - return len; - } - - size_t getBytesLength(const char* key) { - auto it = bytesStore.find(key); - return (it != bytesStore.end()) ? it->second.size() : 0; - } - - size_t getBytes(const char* key, void* buf, size_t len) { - auto it = bytesStore.find(key); - if (it == bytesStore.end()) return 0; - - size_t copyLen = std::min(len, it->second.size()); - std::copy(it->second.begin(), it->second.begin() + copyLen, - static_cast(buf)); - return copyLen; - } - - void remove(const char* key) { - stringStore.erase(key); - longStore.erase(key); - bytesStore.erase(key); - } - - void clear() { - stringStore.clear(); - longStore.clear(); - bytesStore.clear(); - } - - bool hasKey(const char* key) { return isKey(key); } -}; - -#endif // PREFERENCES_H diff --git a/test/stubs/String.h b/test/stubs/String.h deleted file mode 100644 index 7b15818..0000000 --- a/test/stubs/String.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -class String { - std::string data; -public: - String() : data("") {} - String(const char* s) : data(s ? s : "") {} - String(const std::string& s) : data(s) {} - String(const String& s) : data(s.data) {} - String& operator=(const String& s) { data = s.data; return *this; } - const char* c_str() const { return data.c_str(); } - int length() const { return static_cast(data.length()); } - bool operator==(const String& other) const { return data == other.data; } -}; \ No newline at end of file diff --git a/test/stubs/WiFi.h b/test/stubs/WiFi.h deleted file mode 100644 index 95048d5..0000000 --- a/test/stubs/WiFi.h +++ /dev/null @@ -1,153 +0,0 @@ -// Minimal stub for the Arduino WiFi (ESP32) API so that the production code -// can be compiled natively during unit-testing. - -#ifndef WIFI_H -#define WIFI_H - -#include "Arduino.h" - -// WiFi status constants -#define WL_CONNECTED 3 -#define WL_DISCONNECTED 6 -#define WL_NO_SSID_AVAIL 7 -#define WL_CONNECT_FAILED 4 - -#define WIFI_STA 1 -#define WIFI_POWER_8_5dBm 34 - -// Minimal WiFi class -class WiFiClass { -public: - WiFiClass() = default; - ~WiFiClass() = default; - - bool begin(const char* ssid, const char* password) { return true; } - bool begin(const char* ssid) { return true; } - void disconnect() {} - int status() { return WL_CONNECTED; } - bool isConnected() { return true; } - const char* SSID() { return "test_ssid"; } - int8_t RSSI() { return -50; } - void mode(int mode) {} - int getMode() { return 1; } - void setSleepMode(int sleepMode) {} - void setTxPower(int power) {} - - static WiFiClass& getInstance() { - static WiFiClass instance; - return instance; - } -}; - -// Global WiFi instance -extern WiFiClass WiFi; - -// Minimal WiFiManager class -class WiFiManager { -public: - WiFiManager() = default; - ~WiFiManager() = default; - - void setAPCallback(void (*func)(WiFiManager*)) {} - void setSaveConfigCallback(void (*func)()) {} - void setConfigPortalTimeout(int timeout) {} - void setConnectTimeout(int timeout) {} - void setBreakAfterConfig(bool shouldBreak) {} - void setMinimumSignalQuality(int quality) {} - void setRemoveDuplicateAPs(bool remove) {} - void setCountry(const char* country) {} - void setHostname(const char* hostname) {} - - bool startConfigPortal(const char* apName, const char* apPassword = nullptr) { return true; } - bool autoConnect(const char* apName, const char* apPassword = nullptr) { return true; } - bool autoConnect() { return true; } - - void resetSettings() {} - void erase() {} - - // Parameter management - class WiFiManagerParameter* addParameter(class WiFiManagerParameter* p) { return p; } - class WiFiManagerParameter* getParameter(const char* id) { return nullptr; } - - // Configuration - void setConfigPortalBlocking(bool blocking) {} - void setEnableConfigPortal(bool enable) {} - void setEnableConnect(bool enable) {} - void setEnableDebugOutput(bool enable) {} - - // Utility - void process() {} - bool getConfigPortalActive() { return false; } - void stopConfigPortal() {} - - // Network info - String getSSID() { return "test_ssid"; } - String getPassword() { return "test_password"; } - String getConfigPortalSSID() { return "config_ssid"; } - String getConfigPortalPassword() { return "config_password"; } -}; - -// Minimal WiFiManagerParameter class -class WiFiManagerParameter { -public: - WiFiManagerParameter(const char* custom) : id_(""), placeholder_(""), value_(""), length_(0), custom_(custom) {} - WiFiManagerParameter(const char* id, const char* placeholder, const char* defaultValue, int length) - : id_(id ? id : ""), placeholder_(placeholder ? placeholder : ""), value_(defaultValue ? defaultValue : ""), length_(length), custom_("") {} - WiFiManagerParameter(const char* id, const char* placeholder, const char* defaultValue, int length, const char* custom) - : id_(id ? id : ""), placeholder_(placeholder ? placeholder : ""), value_(defaultValue ? defaultValue : ""), length_(length), custom_(custom ? custom : "") {} - WiFiManagerParameter(const char* id, const char* placeholder, const char* defaultValue, int length, const char* custom, int labelPlacement) - : id_(id ? id : ""), placeholder_(placeholder ? placeholder : ""), value_(defaultValue ? defaultValue : ""), length_(length), custom_(custom ? custom : ""), labelPlacement_(labelPlacement) {} - - ~WiFiManagerParameter() = default; - - const char* getID() { return id_.c_str(); } - const char* getValue() { return value_.c_str(); } - const char* getPlaceholder() { return placeholder_.c_str(); } - int getValueLength() { return length_; } - const char* getCustomHTML() { return custom_.c_str(); } - int getLabelPlacement() { return labelPlacement_; } - - void setValue(const char* value, int length) { - value_ = value ? value : ""; - length_ = length; - } - void setValue(const char* value) { value_ = value ? value : ""; } - void setPlaceholder(const char* placeholder) { placeholder_ = placeholder ? placeholder : ""; } - void setLabelPlacement(int labelPlacement) { labelPlacement_ = labelPlacement; } - void setCustomHTML(const char* customHTML) { custom_ = customHTML ? customHTML : ""; } - -private: - std::string id_; - std::string placeholder_; - std::string value_; - int length_; - std::string custom_; - int labelPlacement_ = 0; -}; - -// WiFi modes -#define WIFI_OFF 0 -#define WIFI_AP 2 -#define WIFI_AP_STA 3 - -// WiFi sleep modes -#define WIFI_NONE_SLEEP 0 -#define WIFI_LIGHT_SLEEP 1 -#define WIFI_MODEM_SLEEP 2 - -// WiFi power levels -#define WIFI_POWER_19_5dBm 78 -#define WIFI_POWER_19dBm 76 -#define WIFI_POWER_18_5dBm 74 -#define WIFI_POWER_17dBm 68 -#define WIFI_POWER_15dBm 60 -#define WIFI_POWER_13dBm 52 -#define WIFI_POWER_11dBm 44 -#define WIFI_POWER_8_5dBm 34 -#define WIFI_POWER_7dBm 28 -#define WIFI_POWER_5dBm 20 -#define WIFI_POWER_2dBm 8 -#define WIFI_POWER_MINUS_1dBm -4 - -#endif // WIFI_H - diff --git a/test/stubs/WiFiManager.h b/test/stubs/WiFiManager.h deleted file mode 100644 index 32bcb92..0000000 --- a/test/stubs/WiFiManager.h +++ /dev/null @@ -1,43 +0,0 @@ -// Minimal stub of the WiFiManager library just enough for unit-testing on the -// native host. The real library is only required on the ESP32 target – in -// the host environment we merely need the types and methods referenced by the -// production code so that it links successfully. - -#pragma once - -#include - -class WiFiManagerParameter { -public: - WiFiManagerParameter(const char * /*id*/, const char * /*placeholder*/, - char * /*value*/, int /*length*/) {} - WiFiManagerParameter(const char * /*id*/, const char * /*placeholder*/, - char * /*value*/, int /*length*/, const char * /*custom*/) {} - - const char *getValue() const { return nullptr; } - -private: - char *value_; -}; - -class WiFiManager { -public: - void setMinimumSignalQuality(int /*quality*/) {} - void setRemoveDuplicateAPs(bool /*remove*/) {} - - using SaveCallback = void (*)(); - void setSaveParamsCallback(SaveCallback /*cb*/) {} - - void addParameter(WiFiManagerParameter * /*param*/) {} - - void setMenu(const char ** /*menu*/, std::size_t /*cnt*/) {} - - void setConfigPortalTimeout(int /*seconds*/) {} - - bool startConfigPortal(const char* apName, const char* password = nullptr) { return true; } - void setAPCallback(void (*callback)(WiFiManager*)) {} - void setBreakAfterConfig(bool shouldBreak) {} - void resetSettings() {} - void setCustomMenuHTML_P(const char* html) {} -}; - diff --git a/test/stubs/Wire.h b/test/stubs/Wire.h deleted file mode 100644 index 10a8efa..0000000 --- a/test/stubs/Wire.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -// Very small stub for the Arduino Wire (I2C) library sufficient for the unit -// tests. Provides only the members accessed by src/main.cpp. - -class TwoWire { -public: - void begin(int /*sda*/, int /*scl*/) {} - void end() {} -}; - -// Global instance mimicking the AVR `Wire` object. -inline TwoWire Wire; - diff --git a/test/stubs/config.hpp b/test/stubs/config.hpp deleted file mode 100644 index b838a8e..0000000 --- a/test/stubs/config.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -// Centralised configuration constants for the project. -// Config stub for native tests -namespace config { - constexpr int MaxPayloadSize = 51; - constexpr int MaxSensorRead = 1; -} - -// Constants from lorawan.hpp -constexpr int MAX_PAYLOAD_SIZE = config::MaxPayloadSize; -constexpr int MAX_SENSOR_READ = config::MaxSensorRead; - -// Constants from main.cpp -constexpr int VCC_ENA_PIN = 13; -constexpr int START_WEB_CONFIG_PIN = 16; -constexpr int NUM_LEDS = 1; -constexpr int LED_DATA_PIN = 17; -constexpr unsigned long long uS_TO_S_FACTOR = 1000000ULL; -const unsigned TX_INTERVAL = 3600; - -// Add externs for WiFi test symbols -#ifdef UNIT_TEST -extern char wifi_ssid_str[32]; -extern char wifi_password_str[64]; -#endif \ No newline at end of file diff --git a/test/stubs/driver/adc.h b/test/stubs/driver/adc.h deleted file mode 100644 index 580fdea..0000000 --- a/test/stubs/driver/adc.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -// Minimal stub for the ESP-IDF ADC driver header used in the production code. -// Only the symbols required by src/main.cpp are provided. They do not perform -// any real hardware interaction but allow the code to compile in the native -// host environment. - -inline void adc_power_acquire() {} -inline void adc_power_release() {} - diff --git a/test/stubs/esp32_gpio.h b/test/stubs/esp32_gpio.h deleted file mode 100644 index e436d85..0000000 --- a/test/stubs/esp32_gpio.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -// Minimal stub for ESP32 GPIO types/constants - -typedef int gpio_num_t; -#define GPIO_NUM_0 0 -#define GPIO_NUM_2 2 -#define GPIO_NUM_4 4 -#define GPIO_NUM_5 5 -#define GPIO_NUM_12 12 -#define GPIO_NUM_14 14 -#define GPIO_NUM_15 15 -#define GPIO_NUM_18 18 -#define GPIO_NUM_19 19 -#define GPIO_NUM_21 21 -#define GPIO_NUM_22 22 -#define GPIO_NUM_23 23 -#define GPIO_NUM_25 25 -#define GPIO_NUM_26 26 -#define GPIO_NUM_27 27 -#define GPIO_NUM_32 32 -#define GPIO_NUM_33 33 -#define GPIO_NUM_34 34 -#define GPIO_NUM_35 35 -#define GPIO_NUM_36 36 -#define GPIO_NUM_37 37 -#define GPIO_NUM_38 38 -#define GPIO_NUM_39 39 -#define GPIO_FLOATING 0 -#define GPIO_MODE_INPUT 0 \ No newline at end of file diff --git a/test/stubs/esp_bt.h b/test/stubs/esp_bt.h deleted file mode 100644 index 238ee6a..0000000 --- a/test/stubs/esp_bt.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -// Minimal stub for the ESP32 Bluetooth controller API used by the production -// code. We only need to provide the functions that are referenced, all of -// which can be empty no-ops in the host environment. - -inline void esp_bt_controller_disable() {} - diff --git a/test/stubs/esp_sleep.h b/test/stubs/esp_sleep.h deleted file mode 100644 index d77e7fe..0000000 --- a/test/stubs/esp_sleep.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -// Stub for the ESP32 deep-sleep API. Only the subset used by main.cpp is -// provided and the functions are implemented as empty no-ops so that the code -// can be compiled and linked on the host. - -using esp_sleep_pd_domain_t = int; -using esp_sleep_pd_option_t = int; - -inline void esp_sleep_pd_config(esp_sleep_pd_domain_t, esp_sleep_pd_option_t) {} -inline void esp_sleep_enable_timer_wakeup(unsigned long long) {} -inline void esp_deep_sleep_start() {} - -// Power-domain constants referenced by the production code. The concrete -// numeric values are irrelevant for the unit tests – they just need to be -// unique. - -constexpr esp_sleep_pd_domain_t ESP_PD_DOMAIN_RTC_PERIPH = 0; -constexpr esp_sleep_pd_domain_t ESP_PD_DOMAIN_RTC_SLOW_MEM = 1; -constexpr esp_sleep_pd_domain_t ESP_PD_DOMAIN_RTC_FAST_MEM = 2; -constexpr esp_sleep_pd_domain_t ESP_PD_DOMAIN_XTAL = 3; - -constexpr esp_sleep_pd_option_t ESP_PD_OPTION_OFF = 0; - diff --git a/test/stubs/esp_system.h b/test/stubs/esp_system.h deleted file mode 100644 index 5212d0d..0000000 --- a/test/stubs/esp_system.h +++ /dev/null @@ -1,9 +0,0 @@ -// Minimal stub for ESP functions used by the production code during -// unit-testing on the host. - -#pragma once - -struct { - void restart() {} -} ESP; - diff --git a/test/stubs/hal/hal.h b/test/stubs/hal/hal.h deleted file mode 100644 index 3f59c93..0000000 --- a/test/stubs/hal/hal.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once - diff --git a/test/stubs/lmic.h b/test/stubs/lmic.h deleted file mode 100644 index 17aafed..0000000 --- a/test/stubs/lmic.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef LMIC_H -#define LMIC_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned char u1_t; -typedef unsigned int u4_t; -typedef unsigned int devaddr_t; -typedef struct { int dummy; } osjob_t; -typedef struct { - int seqnoUp, globalDutyRate, globalDutyAvail, txend, txChnl, opmode; - uint8_t dataLen, dataBeg; - uint8_t txrxFlags; - uint8_t frame[256]; -} lmic_t; - -typedef struct { - int nss; - int rxtx; - int rst; - int dio[3]; - int rxtx_rx_active; - int spi_freq; -} lmic_pinmap; - -typedef enum { - EV_SCAN_TIMEOUT, - EV_BEACON_FOUND, - EV_BEACON_MISSED, - EV_BEACON_TRACKED, - EV_JOINING, - EV_JOINED, - EV_JOIN_FAILED, - EV_REJOIN_FAILED, - EV_TXCOMPLETE, - EV_LOST_TSYNC, - EV_RESET, - EV_RXCOMPLETE, - EV_LINK_DEAD, - EV_LINK_ALIVE, - EV_TXSTART, - EV_TXCANCELED, - EV_RXSTART, - EV_JOIN_TXCOMPLETE -} ev_t; - -#define OP_NONE 0x0000 -#define OP_SCAN 0x0001 -#define OP_TRACK 0x0002 -#define OP_JOINING 0x0004 -#define OP_TXDATA 0x0008 -#define OP_POLL 0x0010 -#define OP_REJOIN 0x0020 -#define OP_SHUTDOWN 0x0040 -#define OP_TXRXPEND 0x0080 -#define OP_RNDTX 0x0100 -#define OP_PINGINI 0x0200 -#define OP_PINGABLE 0x0400 -#define OP_NEXTCHNL 0x0800 -#define OP_LINKDEAD 0x1000 -#define OP_TESTMODE 0x2000 -#define OP_UNJOIN 0x4000 - -#define ARDUINO_LMIC_VERSION 0x04010000 -#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) (((v) >> 24) & 0xFF) -#define ARDUINO_LMIC_VERSION_GET_MINOR(v) (((v) >> 16) & 0xFF) -#define ARDUINO_LMIC_VERSION_GET_PATCH(v) (((v) >> 8) & 0xFF) -#define ARDUINO_LMIC_VERSION_GET_LOCAL(v) ((v) & 0xFF) - -#define TXRX_PORT 0x01 - -#ifdef __cplusplus -} -// Inline C++ stubs for test linkage -inline unsigned long os_getTime() { return 0; } -inline void LMIC_setLinkCheckMode(int) {} -inline void LMIC_getSessionKeys(unsigned int*, unsigned int*, unsigned char*, unsigned char*) {} -inline void LMIC_setTxData2(unsigned char, const unsigned char*, unsigned char, unsigned char) {} -inline void handleDownlinkMessage(unsigned char*, unsigned char) {} -inline void reportFirmwareVersion(void*) {} -inline void LoraWANDebug(const lmic_t&) {} -#endif - -#endif // LMIC_H diff --git a/test/stubs/lmic_stub.cpp b/test/stubs/lmic_stub.cpp deleted file mode 100644 index c43f581..0000000 --- a/test/stubs/lmic_stub.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "lmic.h" - -extern "C" { - unsigned long os_getTime() { return 0; } - void LMIC_setLinkCheckMode(int) {} - void LMIC_getSessionKeys(unsigned int*, unsigned int*, unsigned char*, unsigned char*) {} - void LMIC_setTxData2(unsigned char, const unsigned char*, unsigned char, unsigned char) {} - void handleDownlinkMessage(unsigned char*, unsigned char) {} - void reportFirmwareVersion(void*) {} - void LoraWANDebug(const lmic_t&) {} -} \ No newline at end of file diff --git a/test/stubs/lorawan_settings.cpp b/test/stubs/lorawan_settings.cpp deleted file mode 100644 index 4a76e88..0000000 --- a/test/stubs/lorawan_settings.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "lorawan_settings.hpp" -#include "lmic.h" - -// Global variables for testing -char wifi_ssid_str[32] = ""; -char wifi_password_str[64] = ""; - -extern CayenneLPP lpp; - -// Function implementations -void loadSetings() {} -void initMenu() {} -void saveConfigCallback() {} -void settings_clear() {} \ No newline at end of file diff --git a/test/stubs/lorawan_settings.hpp b/test/stubs/lorawan_settings.hpp deleted file mode 100644 index 7693db2..0000000 --- a/test/stubs/lorawan_settings.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef LORAWAN_SETTINGS_HPP -#define LORAWAN_SETTINGS_HPP - -#include "Arduino.h" -#include "lmic.h" - -// Minimal lorawan_settings functions -void loadSetings(); -void initMenu(); -void saveConfigCallback(); -void settings_clear(); - -// Global variables for testing -extern char wifi_ssid_str[32]; -extern char wifi_password_str[64]; -extern CayenneLPP lpp; - -#endif // LORAWAN_SETTINGS_HPP \ No newline at end of file diff --git a/test/stubs/mbedtls.h b/test/stubs/mbedtls.h deleted file mode 100644 index 69f16dc..0000000 --- a/test/stubs/mbedtls.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef MBEDTLS_H -#define MBEDTLS_H - -#include - -namespace mbedtls { - int md5(const unsigned char* input, size_t ilen, unsigned char* output) { - // Simple stub implementation that returns a predictable hash - if (input == nullptr || output == nullptr) { - return -1; - } - - // For testing purposes, return a known hash for "Hello, World!" - if (ilen == 13 && strncmp((char*)input, "Hello, World!", 13) == 0) { - const unsigned char known_hash[] = { - 0x65, 0xa8, 0xe2, 0x7d, 0x88, 0x79, 0x28, 0x38, - 0x31, 0xb6, 0x64, 0xbd, 0x8b, 0x7f, 0x0a, 0xd4 - }; - memcpy(output, known_hash, 16); - return 0; - } - - // For empty input, return empty hash - if (ilen == 0) { - const unsigned char empty_hash[] = { - 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, - 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e - }; - memcpy(output, empty_hash, 16); - return 0; - } - - // Default: return zeros - memset(output, 0, 16); - return 0; - } -} - -#endif // MBEDTLS_H \ No newline at end of file diff --git a/test/stubs/menu_stub.cpp b/test/stubs/menu_stub.cpp deleted file mode 100644 index d6b126d..0000000 --- a/test/stubs/menu_stub.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "Arduino.h" -#include "WiFi.h" -#include "lorawan_settings.hpp" - -// Global WiFi instance -WiFiClass WiFi; - -// Global Serial instance -SerialClass Serial; - -// Global ESP instance -ESPClass ESP; - -// Global variables for testing -char wifi_ssid_str[32] = ""; -char wifi_password_str[64] = ""; - -// Minimal menu stub functions -void loadSetings() {} -void initMenu() {} -void saveConfigCallback() {} -void settings_clear() {} - -// Arduino function stubs -void delay(unsigned long ms) {} -unsigned long millis() { return 0; } -void digitalWrite(int pin, int value) {} -int digitalRead(int pin) { return 0; } -void pinMode(int pin, int mode) {} -int analogRead(int pin) { return 0; } - -// Map function stubs -int map(int value, int fromLow, int fromHigh, int toLow, int toHigh) { - return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; -} - -float map(float value, float fromLow, float fromHigh, float toLow, float toHigh) { - return (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow) + toLow; -} \ No newline at end of file diff --git a/test/stubs/pgmspace.h b/test/stubs/pgmspace.h deleted file mode 100644 index 3270003..0000000 --- a/test/stubs/pgmspace.h +++ /dev/null @@ -1,10 +0,0 @@ -// Minimal substitute for AVR on the host – simply maps the -// program-memory copy routine used by the CayenneLPP library to the standard -// memcpy available on every platform. - -#pragma once - -#include - -#define memcpy_P(dest, src, len) std::memcpy((dest), (src), (len)) - diff --git a/test/test_constants_simple/test_constants_simple.cpp b/test/test_constants_simple/test_constants_simple.cpp deleted file mode 100644 index fc0c3b5..0000000 --- a/test/test_constants_simple/test_constants_simple.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include - -// Simple constants for testing -const int FIRMWARE_VERSION = 100; -const int MAX_WIFI_SSID_LENGTH = 32; -const int MAX_WIFI_PASSWORD_LENGTH = 64; -const int OTA_PORT = 1; -const int SENSOR_PORT = 2; - -void test_firmware_version() { - TEST_ASSERT_EQUAL(100, FIRMWARE_VERSION); -} - -void test_wifi_lengths() { - TEST_ASSERT_EQUAL(32, MAX_WIFI_SSID_LENGTH); - TEST_ASSERT_EQUAL(64, MAX_WIFI_PASSWORD_LENGTH); -} - -void test_port_numbers() { - TEST_ASSERT_EQUAL(1, OTA_PORT); - TEST_ASSERT_EQUAL(2, SENSOR_PORT); -} - -void test_constant_relationships() { - // Test logical relationships between constants - TEST_ASSERT_TRUE(MAX_WIFI_SSID_LENGTH > 0); - TEST_ASSERT_TRUE(MAX_WIFI_PASSWORD_LENGTH > 0); - TEST_ASSERT_TRUE(MAX_WIFI_PASSWORD_LENGTH >= MAX_WIFI_SSID_LENGTH); - TEST_ASSERT_TRUE(FIRMWARE_VERSION > 0); - TEST_ASSERT_TRUE(OTA_PORT > 0); - TEST_ASSERT_TRUE(SENSOR_PORT > 0); -} - -void test_port_uniqueness() { - TEST_ASSERT_NOT_EQUAL(OTA_PORT, SENSOR_PORT); -} - -int main() { - UNITY_BEGIN(); - - RUN_TEST(test_firmware_version); - RUN_TEST(test_wifi_lengths); - RUN_TEST(test_port_numbers); - RUN_TEST(test_constant_relationships); - RUN_TEST(test_port_uniqueness); - - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_main/test_constants.cpp b/test/test_main/test_constants.cpp deleted file mode 100644 index 4b9ed1b..0000000 --- a/test/test_main/test_constants.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include "../stubs/Arduino.h" -#include "../stubs/config.hpp" -#include "lorawan_settings.hpp" -#include "menu.hpp" - -void test_main_constants() { - // Test that constexpr constants are properly defined - TEST_ASSERT_EQUAL(13, VCC_ENA_PIN); - TEST_ASSERT_EQUAL(16, START_WEB_CONFIG_PIN); - TEST_ASSERT_EQUAL(1, NUM_LEDS); - TEST_ASSERT_EQUAL(17, LED_DATA_PIN); - TEST_ASSERT_EQUAL(1000000ULL, uS_TO_S_FACTOR); - TEST_ASSERT_EQUAL(3600, TX_INTERVAL); -} - -void test_lorawan_settings_constants() { - // Test lorawan_settings.hpp constants - TEST_ASSERT_EQUAL_STRING("lmic", LMIC_PREF_NS_NAME); - TEST_ASSERT_EQUAL_STRING("init", LMIC_INIT_NEEDED_KEY_NAME); - TEST_ASSERT_EQUAL_STRING("lmic_struct", LMIC_BYTES_KEY_NAME); - TEST_ASSERT_EQUAL_STRING("lorawan_config", LORAWAN_CONFIG_PRESENT_KEY); - TEST_ASSERT_EQUAL_STRING("euid", APP_EUID_KEY); - TEST_ASSERT_EQUAL_STRING("runtime", RUNTIME_KEY); - TEST_ASSERT_EQUAL(100, MAX_LORAWAN_CONF_CHAR_LEN); - TEST_ASSERT_FALSE(RW_MODE); - TEST_ASSERT_TRUE(RO_MODE); -} - -void test_menu_constants() { - // Test menu.hpp constants - TEST_ASSERT_EQUAL(120, CONFIG_TIMEOUT_SECONDS); - TEST_ASSERT_EQUAL(17, APP_EUID_LEN); - TEST_ASSERT_EQUAL(10, MAX_INT_STR_LEN); -} - -void test_lorawan_constants() { - // Test lorawan.hpp constants - TEST_ASSERT_EQUAL(config::MaxPayloadSize, MAX_PAYLOAD_SIZE); - TEST_ASSERT_EQUAL(config::MaxSensorRead, MAX_SENSOR_READ); -} - -void setUp() {} -void tearDown() {} - -int main(int argc, char **argv) { - UNITY_BEGIN(); - RUN_TEST(test_main_constants); - RUN_TEST(test_lorawan_settings_constants); - RUN_TEST(test_menu_constants); - RUN_TEST(test_lorawan_constants); - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_main_constants/test_main_constants.cpp b/test/test_main_constants/test_main_constants.cpp deleted file mode 100644 index 93c6034..0000000 --- a/test/test_main_constants/test_main_constants.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include - -#include -#include -#include - -// Helper that loads the whole file into a string. Throws on failure because -// the unit-test framework will treat uncaught exceptions as test failures. -static std::string load_source_file(const std::string &path) { - std::ifstream ifs(path); - if (!ifs) { - throw std::runtime_error("Failed to open " + path); - } - std::ostringstream oss; - oss << ifs.rdbuf(); - return oss.str(); -} - -// Extract the integer literal that appears after an '=' in a `constexpr` line. -static int extract_constexpr_value(const std::string &source, const std::string &name) { - auto pos = source.find("constexpr int " + name); - TEST_ASSERT_TRUE_MESSAGE(pos != std::string::npos, ("Missing constexpr for " + name).c_str()); - - // Move past the constexpr declaration and any whitespace up to the value. - pos = source.find_first_of("0123456789", pos); - TEST_ASSERT_NOT_EQUAL(-1, static_cast(pos)); - - // Read the number. - size_t end = pos; - while (end < source.size() && isdigit(static_cast(source[end]))) { - ++end; - } - return std::stoi(source.substr(pos, end - pos)); -} - -void test_constants_are_as_expected() { - std::string src; - try { - src = load_source_file("src/main.cpp"); - } catch (...) { - // When the executable is launched from the build directory the path - // needs one extra level up. - src = load_source_file("../src/main.cpp"); - } - - // Validate a handful of critical compile-time constants. The concrete - // numbers encode hardware layout or timing behaviour and should therefore - // remain stable. - TEST_ASSERT_EQUAL_INT_MESSAGE(13, extract_constexpr_value(src, "VCC_ENA_PIN"), "VCC_ENA_PIN changed – verify hardware wiring"); - TEST_ASSERT_EQUAL_INT_MESSAGE(16, extract_constexpr_value(src, "START_WEB_CONFIG_PIN"), "START_WEB_CONFIG_PIN changed – update schematic"); - TEST_ASSERT_EQUAL_INT_MESSAGE(1, extract_constexpr_value(src, "NUM_LEDS"), "NUM_LEDS changed – revisit power budget and animations"); - TEST_ASSERT_EQUAL_INT_MESSAGE(17, extract_constexpr_value(src, "LED_DATA_PIN"), "LED_DATA_PIN changed – verify PCB routing"); - - // TX_INTERVAL is declared as a `const unsigned` rather than a macro – we - // locate it textually as well for simplicity. - TEST_ASSERT_TRUE_MESSAGE(src.find("const unsigned TX_INTERVAL = 3600") != std::string::npos, - "TX_INTERVAL expected to be 3600 seconds"); -} - -// --------------------------------------------------------------------------- -// Unity test runner -// --------------------------------------------------------------------------- - -void setUp() {} -void tearDown() {} - -int main(int argc, char **argv) { - UNITY_BEGIN(); - RUN_TEST(test_constants_are_as_expected); - return UNITY_END(); -} diff --git a/test/test_memory_management/test_memory_management.cpp b/test/test_memory_management/test_memory_management.cpp deleted file mode 100644 index 990b634..0000000 --- a/test/test_memory_management/test_memory_management.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include -#include - -// Mock WiFiManagerParameter for testing -class MockWiFiManagerParameter { -public: - MockWiFiManagerParameter(const char* id, const char* label, const char* value, int length) - : id_(id), label_(label), value_(value), length_(length) {} - - const char* getValue() const { return value_.c_str(); } - const char* getID() const { return id_.c_str(); } - const char* getLabel() const { return label_.c_str(); } - int getValueLength() const { return length_; } - -private: - std::string id_; - std::string label_; - std::string value_; - int length_; -}; - -void test_unique_ptr_creation() { - // Test that unique_ptr can be created and managed properly - auto param = std::make_unique( - "test_id", "Test Label", "test_value", 20); - - TEST_ASSERT_NOT_NULL(param.get()); - TEST_ASSERT_EQUAL_STRING("test_id", param->getID()); - TEST_ASSERT_EQUAL_STRING("Test Label", param->getLabel()); - TEST_ASSERT_EQUAL_STRING("test_value", param->getValue()); - TEST_ASSERT_EQUAL(20, param->getValueLength()); -} - -void test_unique_ptr_array() { - // Test that arrays of unique_ptr work correctly - std::unique_ptr params[3]; - - params[0] = std::make_unique("id1", "Label1", "value1", 10); - params[1] = std::make_unique("id2", "Label2", "value2", 10); - params[2] = std::make_unique("id3", "Label3", "value3", 10); - - TEST_ASSERT_NOT_NULL(params[0].get()); - TEST_ASSERT_NOT_NULL(params[1].get()); - TEST_ASSERT_NOT_NULL(params[2].get()); - - TEST_ASSERT_EQUAL_STRING("id1", params[0]->getID()); - TEST_ASSERT_EQUAL_STRING("id2", params[1]->getID()); - TEST_ASSERT_EQUAL_STRING("id3", params[2]->getID()); -} - -void test_const_array_access() { - // Test that const arrays work correctly - const char* const testMenu[] = {"param", "restart"}; - - TEST_ASSERT_EQUAL_STRING("param", testMenu[0]); - TEST_ASSERT_EQUAL_STRING("restart", testMenu[1]); - TEST_ASSERT_EQUAL(2, sizeof(testMenu) / sizeof(testMenu[0])); -} - -void test_string_operations() { - // Test that string operations work correctly - std::string testString = "test_value"; - char buffer[20]; - - // Simulate safe_strncpy behavior - size_t copyLen = std::min(testString.length(), sizeof(buffer) - 1); - testString.copy(buffer, copyLen); - buffer[copyLen] = '\0'; - - TEST_ASSERT_EQUAL_STRING("test_value", buffer); -} - -void setUp() {} -void tearDown() {} - -int main(int argc, char **argv) { - UNITY_BEGIN(); - RUN_TEST(test_unique_ptr_creation); - RUN_TEST(test_unique_ptr_array); - RUN_TEST(test_const_array_access); - RUN_TEST(test_string_operations); - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_ota_signature/test_ota_signature.cpp b/test/test_ota_signature/test_ota_signature.cpp deleted file mode 100644 index 90b480f..0000000 --- a/test/test_ota_signature/test_ota_signature.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef UNIT_TEST -#if defined(HAVE_SODIUM) || defined(__SODIUM_H__) -#define TEST_HAS_SODIUM 1 -#else -#define TEST_HAS_SODIUM 0 -#endif -#else -#define TEST_HAS_SODIUM 0 -#endif - -constexpr int OTA_MAX_CHUNKS = 10; -constexpr int OTA_CHUNK_SIZE = 51; - -struct OtaChunkBuffer { - std::string decoded_chunks[OTA_MAX_CHUNKS]; - bool received[OTA_MAX_CHUNKS]; - int total_chunks = 0; - int received_count = 0; - void reset() { - for (int i = 0; i < OTA_MAX_CHUNKS; ++i) { - decoded_chunks[i] = ""; - received[i] = false; - } - total_chunks = 0; - received_count = 0; - } - void update_total_chunks(int idx) { - if (idx + 1 > total_chunks) total_chunks = idx + 1; - } -}; -OtaChunkBuffer ota_chunk_buffer; - -// Helper: base64 decode (replace with your project's base64 if needed) -std::string b64decode(const std::string& in) { - static const std::string b64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string out; - std::vector T(256, -1); - for (int i = 0; i < 64; i++) T[b64_chars[i]] = i; - int val = 0, valb = -8; - for (unsigned char c : in) { - if (T[c] == -1) break; - val = (val << 6) + T[c]; - valb += 6; - if (valb >= 0) { - out.push_back(char((val >> valb) & 0xFF)); - valb -= 8; - } - } - return out; -} - -// Each chunk is base64-encoded; decode and store -void handleOtaChunk(const uint8_t* data, uint8_t dataLen, uint8_t fport) { - if (fport < 1 || fport > OTA_MAX_CHUNKS) return; - int idx = fport - 1; - std::string b64chunk((const char*)data, dataLen); - std::string decoded = b64decode(b64chunk); - if (!ota_chunk_buffer.received[idx]) { - ota_chunk_buffer.decoded_chunks[idx] = decoded; - ota_chunk_buffer.received[idx] = true; - ota_chunk_buffer.received_count++; - ota_chunk_buffer.update_total_chunks(idx); - } -} - -// Reassemble the decoded binary data in order -bool tryReassembleOtaJson(std::string& out_json) { - if (ota_chunk_buffer.total_chunks == 0 || ota_chunk_buffer.received_count < ota_chunk_buffer.total_chunks) return false; - // Ensure all chunks from 0 to total_chunks-1 are present - for (int i = 0; i < ota_chunk_buffer.total_chunks; ++i) { - if (!ota_chunk_buffer.received[i]) return false; - } - out_json.clear(); - for (int i = 0; i < ota_chunk_buffer.total_chunks; ++i) { - out_json += ota_chunk_buffer.decoded_chunks[i]; - } - return true; -} - -void test_chunked_reassembly() { - ota_chunk_buffer.reset(); - std::string json = "{\"u\":\"https://t.co/abc\",\"m\":\"deadbeefdeadbeefdeadbeefdeadbeef\",\"v\":\"214\",\"s\":\"SIG\"}"; - int chunk_size = 20; - int total_chunks = (json.size() + chunk_size - 1) / chunk_size; - ota_chunk_buffer.total_chunks = total_chunks; - for (int i = 0; i < total_chunks; ++i) { - int len = std::min(chunk_size, (int)json.size() - i * chunk_size); - std::string chunk = json.substr(i * chunk_size, len); - // Base64-encode each chunk - std::string b64chunk; - static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int val = 0, valb = -6; - for (unsigned char c : chunk) { - val = (val << 8) + c; - valb += 8; - while (valb >= 0) { - b64chunk.push_back(b64_chars[(val >> valb) & 0x3F]); - valb -= 6; - } - } - if (valb > -6) b64chunk.push_back(b64_chars[((val << 8) >> (valb + 8)) & 0x3F]); - while (b64chunk.size() % 4) b64chunk.push_back('='); - handleOtaChunk((const uint8_t*)b64chunk.data(), b64chunk.size(), i + 1); - } - std::string reassembled; - TEST_ASSERT_TRUE(tryReassembleOtaJson(reassembled)); - TEST_ASSERT_EQUAL_STRING(json.c_str(), reassembled.c_str()); -} - -void test_chunked_missing_chunk() { - ota_chunk_buffer.reset(); - std::string json = "{\"u\":\"https://t.co/abc\",\"m\":\"deadbeefdeadbeefdeadbeefdeadbeef\",\"v\":\"214\",\"s\":\"SIG\"}"; - int chunk_size = 20; - int total_chunks = (json.size() + chunk_size - 1) / chunk_size; - ota_chunk_buffer.total_chunks = total_chunks; - for (int i = 0; i < total_chunks; ++i) { - if (i == 1) continue; // skip chunk 2 - int len = std::min(chunk_size, (int)json.size() - i * chunk_size); - std::string chunk = json.substr(i * chunk_size, len); - // Base64-encode each chunk - std::string b64chunk; - static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int val = 0, valb = -6; - for (unsigned char c : chunk) { - val = (val << 8) + c; - valb += 8; - while (valb >= 0) { - b64chunk.push_back(b64_chars[(val >> valb) & 0x3F]); - valb -= 6; - } - } - if (valb > -6) b64chunk.push_back(b64_chars[((val << 8) >> (valb + 8)) & 0x3F]); - while (b64chunk.size() % 4) b64chunk.push_back('='); - handleOtaChunk((const uint8_t*)b64chunk.data(), b64chunk.size(), i + 1); - } - std::string reassembled; - TEST_ASSERT_FALSE(tryReassembleOtaJson(reassembled)); -} - -void test_ttn_chunked_downlink() { - // Example TTN downlink (2 chunks, base64-encoded) - const char* chunk1 = "eyJ1IjoiaHR0cDovL2Zvby5iYXIvZi5lbGYiLCJtIjoiWFhYWFhYWFhYWFhYWFhYWFhY"; - const char* chunk2 = "WFgiLCJ2IjoiMjAwIiwicyI6Ik5OTk5OTk5OTk5OTk5OTk5OTk5OIn0="; - // Simulate receiving chunks on fport 1 and 2 (pass base64 directly) - ota_chunk_buffer.reset(); - handleOtaChunk((const uint8_t*)chunk1, strlen(chunk1), 1); - handleOtaChunk((const uint8_t*)chunk2, strlen(chunk2), 2); - // Try to reassemble - std::string reassembled; - TEST_ASSERT_TRUE(tryReassembleOtaJson(reassembled)); - // Expected JSON - const char* expected = "{\"u\":\"http://foo.bar/f.elf\",\"m\":\"XXXXXXXXXXXXXXXXXXXX\",\"v\":\"200\",\"s\":\"NNNNNNNNNNNNNNNNNNNN\"}"; - TEST_ASSERT_EQUAL_STRING(expected, reassembled.c_str()); -} - -void test_chunked_out_of_order() { - ota_chunk_buffer.reset(); - std::string json = "{\"u\":\"https://t.co/abc\",\"m\":\"deadbeefdeadbeefdeadbeefdeadbeef\",\"v\":\"214\",\"s\":\"SIG\"}"; - int chunk_size = 20; - int total_chunks = (json.size() + chunk_size - 1) / chunk_size; - ota_chunk_buffer.total_chunks = total_chunks; - std::vector> chunks; - for (int i = 0; i < total_chunks; ++i) { - int len = std::min(chunk_size, (int)json.size() - i * chunk_size); - std::string chunk = json.substr(i * chunk_size, len); - // Base64-encode each chunk - std::string b64chunk; - static const char b64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int val = 0, valb = -6; - for (unsigned char c : chunk) { - val = (val << 8) + c; - valb += 8; - while (valb >= 0) { - b64chunk.push_back(b64_chars[(val >> valb) & 0x3F]); - valb -= 6; - } - } - if (valb > -6) b64chunk.push_back(b64_chars[((val << 8) >> (valb + 8)) & 0x3F]); - while (b64chunk.size() % 4) b64chunk.push_back('='); - chunks.push_back({i, b64chunk}); - } - // Shuffle chunks to simulate out-of-order arrival - std::swap(chunks[0], chunks[1]); - // Add first chunk (actually chunk 1) - handleOtaChunk((const uint8_t*)chunks[0].second.data(), chunks[0].second.size(), chunks[0].first + 1); - std::string reassembled; - TEST_ASSERT_FALSE(tryReassembleOtaJson(reassembled)); - // Add second chunk (actually chunk 0) - handleOtaChunk((const uint8_t*)chunks[1].second.data(), chunks[1].second.size(), chunks[1].first + 1); - // Add remaining chunks in order - for (size_t i = 2; i < chunks.size(); ++i) { - handleOtaChunk((const uint8_t*)chunks[i].second.data(), chunks[i].second.size(), chunks[i].first + 1); - } - TEST_ASSERT_TRUE(tryReassembleOtaJson(reassembled)); - TEST_ASSERT_EQUAL_STRING(json.c_str(), reassembled.c_str()); -} - -// --- Signature validation tests --- -// Test keys (ed25519) for unit test only -const unsigned char test_private_key[64] = { - 0x1a,0x2b,0x3c,0x4d,0x5e,0x6f,0x7a,0x8b,0x9c,0xad,0xbe,0xcf,0xda,0xeb,0xfc,0x0d, - 0x1e,0x2f,0x3a,0x4b,0x5c,0x6d,0x7e,0x8f,0x9a,0xab,0xbc,0xcd,0xde,0xef,0xfa,0x0b, - 0x1a,0x2b,0x3c,0x4d,0x5e,0x6f,0x7a,0x8b,0x9c,0xad,0xbe,0xcf,0xda,0xeb,0xfc,0x0d, - 0x1e,0x2f,0x3a,0x4b,0x5c,0x6d,0x7e,0x8f,0x9a,0xab,0xbc,0xcd,0xde,0xef,0xfa,0x0b -}; -const unsigned char test_public_key[32] = { - 0x1a,0x2b,0x3c,0x4d,0x5e,0x6f,0x7a,0x8b,0x9c,0xad,0xbe,0xcf,0xda,0xeb,0xfc,0x0d, - 0x1e,0x2f,0x3a,0x4b,0x5c,0x6d,0x7e,0x8f,0x9a,0xab,0xbc,0xcd,0xde,0xef,0xfa,0x0b -}; -std::string base64_encode(const unsigned char* data, size_t len) { - static const char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string out; - int val = 0, valb = -6; - for (size_t i = 0; i < len; ++i) { - val = (val << 8) + data[i]; - valb += 8; - while (valb >= 0) { - out.push_back(b64[(val >> valb) & 0x3F]); - valb -= 6; - } - } - if (valb > -6) out.push_back(b64[((val << 8) >> (valb + 8)) & 0x3F]); - while (out.size() % 4) out.push_back('='); - return out; -} -void test_signature_validation_valid() { -#if TEST_HAS_SODIUM - std::string url = "http://foo.bar/f.elf"; - std::string md5 = "cafebabe1234567890abcdef12345678"; - std::string message = url + md5; - unsigned char sig[64]; - crypto_sign_detached(sig, nullptr, (const unsigned char*)message.data(), message.size(), test_private_key); - std::string sig_b64 = base64_encode(sig, 64); - extern bool verify_signature(const std::string&, const std::string&, const std::string&); - TEST_ASSERT_TRUE(verify_signature(url, md5, sig_b64)); -#else - TEST_IGNORE_MESSAGE("libsodium not available"); -#endif -} -void test_signature_validation_invalid() { -#if TEST_HAS_SODIUM - std::string url = "http://foo.bar/f.elf"; - std::string md5 = "cafebabe1234567890abcdef12345678"; - std::string message = url + md5; - unsigned char sig[64]; - crypto_sign_detached(sig, nullptr, (const unsigned char*)message.data(), message.size(), test_private_key); - std::string sig_b64 = base64_encode(sig, 64); - extern bool verify_signature(const std::string&, const std::string&, const std::string&); - // Tamper with message - TEST_ASSERT_FALSE(verify_signature(url, "badmd5", sig_b64)); - // Tamper with signature - std::string bad_sig = sig_b64; - bad_sig[0] = (bad_sig[0] == 'A' ? 'B' : 'A'); - TEST_ASSERT_FALSE(verify_signature(url, md5, bad_sig)); -#else - TEST_IGNORE_MESSAGE("libsodium not available"); -#endif -} - -int main(int argc, char **argv) { - UNITY_BEGIN(); - RUN_TEST(test_chunked_reassembly); - RUN_TEST(test_chunked_missing_chunk); - RUN_TEST(test_ttn_chunked_downlink); - RUN_TEST(test_chunked_out_of_order); - RUN_TEST(test_signature_validation_valid); - RUN_TEST(test_signature_validation_invalid); - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_utils/test_safe_strncpy.cpp b/test/test_utils/test_safe_strncpy.cpp deleted file mode 100644 index 1a95262..0000000 --- a/test/test_utils/test_safe_strncpy.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include - -#include "utils.hpp" - -#ifdef UNIT_TEST -#include "../../test/stubs/esp32_gpio.h" -#endif - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -template -static bool buffer_is_zeroed(const char (&buf)[N]) { - for (std::size_t i = 0; i < N; ++i) { - if (buf[i] != '\0') { - return false; - } - } - return true; -} - -// --------------------------------------------------------------------------- -// Test cases -// --------------------------------------------------------------------------- - -void test_null_source() { - char dest[8]; - // Fill with non-zero pattern to verify that safe_strncpy overwrites it. - memset(dest, 'X', sizeof(dest)); - - safe_strncpy(dest, nullptr); - - // Destination must contain an empty string (just terminator) and all - // remaining bytes past the first must be untouched (i.e. still zero). - TEST_ASSERT_EQUAL_CHAR('\0', dest[0]); -} - -void test_exact_fit() { - constexpr char kSrc[] = "ESP32"; // length 5 incl. terminator 6 - char dest[sizeof(kSrc)]; // Exact same size - - safe_strncpy(dest, kSrc); - - TEST_ASSERT_EQUAL_STRING(kSrc, dest); -} - -void test_truncation() { - constexpr char kSrc[] = "PlatformIO unit-testing"; - char dest[10]; // smaller than source - - safe_strncpy(dest, kSrc); - - // The resulting string must be properly terminated. - TEST_ASSERT_EQUAL_CHAR('\0', dest[sizeof(dest) - 1]); - - // If we copy again into a larger buffer we should match the original - // prefix of length dest_size - 1. - char larger[sizeof(kSrc)]; - strncpy(larger, kSrc, sizeof(dest) - 1); - larger[sizeof(dest) - 1] = '\0'; - - TEST_ASSERT_EQUAL_STRING(larger, dest); -} - -void test_buffer_integrity() { - // Ensure the function never writes past the destination buffer. - constexpr char kSrc[] = "123456789"; - char dest[5]; - - // Place sentinel bytes after the buffer in a struct to detect overflow. - struct { - char buf[5]; - uint32_t sentinel; - } wrapper; - - wrapper.sentinel = 0xDEADBEEF; - - safe_strncpy(wrapper.buf, kSrc); - - TEST_ASSERT_EQUAL_UINT32(0xDEADBEEF, wrapper.sentinel); -} - -void test_null_dest() { - // Cannot test nullptr with template function that expects array reference - // This test is not applicable for the current implementation - TEST_ASSERT_TRUE(true); -} - -// --------------------------------------------------------------------------- -// Forward declarations of tests located in other translation units -// --------------------------------------------------------------------------- - -void test_dest_size_one(); -void test_empty_source(); - -// --------------------------------------------------------------------------- -// Unity test runner -// --------------------------------------------------------------------------- - -void setUp() {} -void tearDown() {} - -int main(int argc, char **argv) { - UNITY_BEGIN(); - RUN_TEST(test_null_source); - RUN_TEST(test_exact_fit); - RUN_TEST(test_truncation); - RUN_TEST(test_buffer_integrity); - // Tests defined in test_safe_strncpy_bounds.cpp - RUN_TEST(test_dest_size_one); - RUN_TEST(test_empty_source); - RUN_TEST(test_null_dest); - return UNITY_END(); -} diff --git a/test/test_utils/test_safe_strncpy_bounds.cpp b/test/test_utils/test_safe_strncpy_bounds.cpp deleted file mode 100644 index e2d7c56..0000000 --- a/test/test_utils/test_safe_strncpy_bounds.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include "utils.hpp" - -void test_dest_size_one() { - char dest[1]; - safe_strncpy(dest, "ABC"); // should truncate to empty string but remain null-terminated - TEST_ASSERT_EQUAL_CHAR('\0', dest[0]); -} - -void test_empty_source() { - constexpr char kSrc[] = ""; // already empty - char dest[4] = {'x', 'x', 'x', 'x'}; - safe_strncpy(dest, kSrc); - TEST_ASSERT_EQUAL_STRING(kSrc, dest); - // Ensure the remaining bytes were not overwritten past terminator - TEST_ASSERT_EQUAL_CHAR('x', dest[1]); -} - -// setUp and tearDown as well as the Unity test runner `main` are -// provided by another translation unit in this test suite -// (see `test_safe_strncpy.cpp`). Defining them here as well would result -// in multiple-definition linker errors when PlatformIO links all source -// files in the `test_utils` suite into a single executable. Therefore we -// only declare the test cases in this file – the shared runner will pick -// them up automatically via `RUN_TEST()` calls added there. diff --git a/test/test_utils_simple/test_utils_simple.cpp b/test/test_utils_simple/test_utils_simple.cpp deleted file mode 100644 index 661a2d5..0000000 --- a/test/test_utils_simple/test_utils_simple.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include -#include -#include -#include // Added for base64 tests - -// Simple safe string copy function for testing -void safe_strncpy(char* dest, const char* src, size_t dest_size) { - if (dest == nullptr || dest_size == 0) { - return; - } - - if (src == nullptr) { - dest[0] = '\0'; - return; - } - - size_t src_len = strlen(src); - size_t copy_len = (src_len < dest_size - 1) ? src_len : dest_size - 1; - - strncpy(dest, src, copy_len); - dest[copy_len] = '\0'; -} - -// Test functions -void test_safe_strncpy_normal() { - char dest[10]; - const char* src = "Hello"; - - safe_strncpy(dest, src, sizeof(dest)); - - TEST_ASSERT_EQUAL_STRING("Hello", dest); -} - -void test_safe_strncpy_truncation() { - char dest[5]; - const char* src = "Hello World"; - - safe_strncpy(dest, src, sizeof(dest)); - - TEST_ASSERT_EQUAL_STRING("Hell", dest); - TEST_ASSERT_EQUAL(4, strlen(dest)); -} - -void test_safe_strncpy_null_dest() { - const char* src = "Hello"; - - // Should not crash - safe_strncpy(nullptr, src, 10); - - TEST_ASSERT_TRUE(true); // If we get here, no crash occurred -} - -void test_safe_strncpy_null_src() { - char dest[10]; - - safe_strncpy(dest, nullptr, sizeof(dest)); - - TEST_ASSERT_EQUAL_STRING("", dest); -} - -void test_safe_strncpy_zero_size() { - char dest[10]; - const char* src = "Hello"; - - safe_strncpy(dest, src, 0); - - // dest should remain unchanged (implementation dependent) - TEST_ASSERT_TRUE(true); // If we get here, no crash occurred -} - -void test_safe_strncpy_exact_fit() { - char dest[6]; // "Hello" + null terminator - const char* src = "Hello"; - - safe_strncpy(dest, src, sizeof(dest)); - - TEST_ASSERT_EQUAL_STRING("Hello", dest); -} - -void test_safe_strncpy_empty_src() { - char dest[10]; - const char* src = ""; - - safe_strncpy(dest, src, sizeof(dest)); - - TEST_ASSERT_EQUAL_STRING("", dest); -} - -// --- Base64 decode helper for tests --- -std::string test_b64decode(const std::string& in) { - static const std::string b64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string out; - std::vector T(256, -1); - for (int i = 0; i < 64; i++) T[b64_chars[i]] = i; - int val = 0, valb = -8; - for (unsigned char c : in) { - if (T[c] == -1) break; - val = (val << 6) + T[c]; - valb += 6; - if (valb >= 0) { - out.push_back(char((val >> valb) & 0xFF)); - valb -= 8; - } - } - return out; -} - -void test_base64_decode_valid() { - TEST_ASSERT_EQUAL_STRING("hello", test_b64decode("aGVsbG8=").c_str()); - TEST_ASSERT_EQUAL_STRING("foobar", test_b64decode("Zm9vYmFy").c_str()); - TEST_ASSERT_EQUAL_STRING("", test_b64decode("").c_str()); -} - -void test_base64_decode_padding() { - TEST_ASSERT_EQUAL_STRING("any carnal pleasure.", test_b64decode("YW55IGNhcm5hbCBwbGVhc3VyZS4=").c_str()); - TEST_ASSERT_EQUAL_STRING("any carnal pleasure", test_b64decode("YW55IGNhcm5hbCBwbGVhc3VyZQ==").c_str()); - TEST_ASSERT_EQUAL_STRING("any carnal pleasur", test_b64decode("YW55IGNhcm5hbCBwbGVhc3Vy").c_str()); -} - -void test_base64_decode_invalid() { - // Should stop at first invalid char - TEST_ASSERT_EQUAL_STRING("", test_b64decode("!@#$").c_str()); - // Partial valid - TEST_ASSERT_EQUAL_STRING("f", test_b64decode("Zg!@#").c_str()); -} - -int main() { - UNITY_BEGIN(); - - RUN_TEST(test_safe_strncpy_normal); - RUN_TEST(test_safe_strncpy_truncation); - RUN_TEST(test_safe_strncpy_null_dest); - RUN_TEST(test_safe_strncpy_null_src); - RUN_TEST(test_safe_strncpy_zero_size); - RUN_TEST(test_safe_strncpy_exact_fit); - RUN_TEST(test_safe_strncpy_empty_src); - RUN_TEST(test_base64_decode_valid); - RUN_TEST(test_base64_decode_padding); - RUN_TEST(test_base64_decode_invalid); - - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_wifi_auto_test/test_wifi_auto_test.cpp b/test/test_wifi_auto_test/test_wifi_auto_test.cpp deleted file mode 100644 index c9a561d..0000000 --- a/test/test_wifi_auto_test/test_wifi_auto_test.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include -#include -#include - -// Mock settings storage -std::map mock_settings_string; -std::map mock_settings_bool; - -// Mock functions -void settings_put_string(const std::string& key, const std::string& value) { - mock_settings_string[key] = value; -} - -void settings_put_bool(const std::string& key, bool value) { - mock_settings_bool[key] = value; -} - -std::string settings_get_string(const std::string& key, const std::string& default_value = "") { - auto it = mock_settings_string.find(key); - return (it != mock_settings_string.end()) ? it->second : default_value; -} - -bool settings_get_bool(const std::string& key, bool default_value = false) { - auto it = mock_settings_bool.find(key); - return (it != mock_settings_bool.end()) ? it->second : default_value; -} - -// Mock WiFi test function -bool testWifiConnection(const std::string& ssid, const std::string& password) { - // Mock implementation - return true for valid credentials - return !ssid.empty() && !password.empty(); -} - -// Test helper function to simulate the WiFi testing logic -bool shouldTestWiFi(const std::string& current_ssid, const std::string& current_password) { - bool wifi_tested_ok = settings_get_bool("wifi_tested_ok", false); - - // Get previous WiFi settings for comparison - std::string prev_ssid = settings_get_string("wifi_ssid", ""); - std::string prev_password = settings_get_string("wifi_password", ""); - - // Test WiFi if: - // 1. wifi_tested_ok is false/nil and SSID is provided - // 2. WiFi config has changed - if (current_ssid.length() > 0) { - if (!wifi_tested_ok) { - return true; - } else if (current_ssid != prev_ssid || current_password != prev_password) { - return true; - } - } - return false; -} - -void setUp(void) { - // Clear mock settings before each test - mock_settings_string.clear(); - mock_settings_bool.clear(); -} - -void tearDown(void) { - // Cleanup after each test - mock_settings_string.clear(); - mock_settings_bool.clear(); -} - -void test_wifi_test_when_not_tested_before() { - // Test case: WiFi has never been tested before - std::string ssid = "test_network"; - std::string password = "test_password"; - - // wifi_tested_ok should be false by default - bool should_test = shouldTestWiFi(ssid, password); - TEST_ASSERT_TRUE(should_test); -} - -void test_wifi_test_when_config_changed() { - // Test case: WiFi config has changed - std::string old_ssid = "old_network"; - std::string old_password = "old_password"; - std::string new_ssid = "new_network"; - std::string new_password = "new_password"; - - // Set up previous config and mark as tested - settings_put_string("wifi_ssid", old_ssid); - settings_put_string("wifi_password", old_password); - settings_put_bool("wifi_tested_ok", true); - - // Test with new config - bool should_test = shouldTestWiFi(new_ssid, new_password); - TEST_ASSERT_TRUE(should_test); -} - -void test_wifi_skip_when_config_unchanged() { - // Test case: WiFi config unchanged and previously tested - std::string ssid = "test_network"; - std::string password = "test_password"; - - // Set up same config and mark as tested - settings_put_string("wifi_ssid", ssid); - settings_put_string("wifi_password", password); - settings_put_bool("wifi_tested_ok", true); - - // Test with same config - bool should_test = shouldTestWiFi(ssid, password); - TEST_ASSERT_FALSE(should_test); -} - -void test_wifi_skip_when_no_ssid() { - // Test case: No SSID provided - std::string ssid = ""; - std::string password = "test_password"; - - bool should_test = shouldTestWiFi(ssid, password); - TEST_ASSERT_FALSE(should_test); -} - -void test_wifi_test_when_password_changed() { - // Test case: SSID same but password changed - std::string ssid = "test_network"; - std::string old_password = "old_password"; - std::string new_password = "new_password"; - - // Set up previous config and mark as tested - settings_put_string("wifi_ssid", ssid); - settings_put_string("wifi_password", old_password); - settings_put_bool("wifi_tested_ok", true); - - // Test with new password - bool should_test = shouldTestWiFi(ssid, new_password); - TEST_ASSERT_TRUE(should_test); -} - -void test_wifi_test_success_updates_flag() { - // Test case: WiFi test success should update the flag - std::string ssid = "test_network"; - std::string password = "test_password"; - - // Initially not tested - TEST_ASSERT_FALSE(settings_get_bool("wifi_tested_ok", false)); - - // Simulate successful test - if (testWifiConnection(ssid, password)) { - settings_put_bool("wifi_tested_ok", true); - } - - TEST_ASSERT_TRUE(settings_get_bool("wifi_tested_ok", false)); -} - -void test_wifi_test_failure_updates_flag() { - // Test case: WiFi test failure should update the flag - std::string ssid = ""; - std::string password = "test_password"; - - // Initially tested successfully - settings_put_bool("wifi_tested_ok", true); - - // Simulate failed test - if (testWifiConnection(ssid, password)) { - settings_put_bool("wifi_tested_ok", true); - } else { - settings_put_bool("wifi_tested_ok", false); - } - - TEST_ASSERT_FALSE(settings_get_bool("wifi_tested_ok", true)); -} - -int main() { - UNITY_BEGIN(); - - RUN_TEST(test_wifi_test_when_not_tested_before); - RUN_TEST(test_wifi_test_when_config_changed); - RUN_TEST(test_wifi_skip_when_config_unchanged); - RUN_TEST(test_wifi_skip_when_no_ssid); - RUN_TEST(test_wifi_test_when_password_changed); - RUN_TEST(test_wifi_test_success_updates_flag); - RUN_TEST(test_wifi_test_failure_updates_flag); - - return UNITY_END(); -} \ No newline at end of file diff --git a/test/test_wifi_simple/test_wifi_simple.cpp b/test/test_wifi_simple/test_wifi_simple.cpp deleted file mode 100644 index 4466a57..0000000 --- a/test/test_wifi_simple/test_wifi_simple.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include - -// Simple in-memory settings storage for testing -class SimpleSettings { -private: - std::map storage; - -public: - void putString(const std::string& key, const std::string& value) { - storage[key] = value; - } - - std::string getString(const std::string& key, const std::string& defaultValue = "") { - auto it = storage.find(key); - return (it != storage.end()) ? it->second : defaultValue; - } - - bool hasKey(const std::string& key) { - return storage.find(key) != storage.end(); - } - - void clear() { - storage.clear(); - } -}; - -// Simple WiFi settings structure -struct SimpleWifiSettings { - std::string ssid; - std::string password; -}; - -// Test functions -void test_wifi_settings_save_load() { - SimpleSettings settings; - SimpleWifiSettings wifi; - - // Test saving - wifi.ssid = "TestNetwork"; - wifi.password = "TestPassword123"; - - settings.putString("wifi_ssid", wifi.ssid); - settings.putString("wifi_password", wifi.password); - - // Test loading - std::string loaded_ssid = settings.getString("wifi_ssid"); - std::string loaded_password = settings.getString("wifi_password"); - - TEST_ASSERT_EQUAL_STRING("TestNetwork", loaded_ssid.c_str()); - TEST_ASSERT_EQUAL_STRING("TestPassword123", loaded_password.c_str()); -} - -void test_wifi_settings_empty() { - SimpleSettings settings; - - std::string ssid = settings.getString("wifi_ssid"); - std::string password = settings.getString("wifi_password"); - - TEST_ASSERT_EQUAL_STRING("", ssid.c_str()); - TEST_ASSERT_EQUAL_STRING("", password.c_str()); -} - -void test_wifi_settings_partial() { - SimpleSettings settings; - - // Only set SSID - settings.putString("wifi_ssid", "NetworkOnly"); - - std::string ssid = settings.getString("wifi_ssid"); - std::string password = settings.getString("wifi_password"); - - TEST_ASSERT_EQUAL_STRING("NetworkOnly", ssid.c_str()); - TEST_ASSERT_EQUAL_STRING("", password.c_str()); -} - -void test_wifi_settings_persistence() { - SimpleSettings settings; - - // Set initial values - settings.putString("wifi_ssid", "InitialNetwork"); - settings.putString("wifi_password", "InitialPassword"); - - // Verify initial values - TEST_ASSERT_EQUAL_STRING("InitialNetwork", settings.getString("wifi_ssid").c_str()); - TEST_ASSERT_EQUAL_STRING("InitialPassword", settings.getString("wifi_password").c_str()); - - // Update values - settings.putString("wifi_ssid", "UpdatedNetwork"); - settings.putString("wifi_password", "UpdatedPassword"); - - // Verify updated values - TEST_ASSERT_EQUAL_STRING("UpdatedNetwork", settings.getString("wifi_ssid").c_str()); - TEST_ASSERT_EQUAL_STRING("UpdatedPassword", settings.getString("wifi_password").c_str()); -} - -void test_wifi_settings_clear() { - SimpleSettings settings; - - // Set some values - settings.putString("wifi_ssid", "TestNetwork"); - settings.putString("wifi_password", "TestPassword"); - - // Verify values exist - TEST_ASSERT_TRUE(settings.hasKey("wifi_ssid")); - TEST_ASSERT_TRUE(settings.hasKey("wifi_password")); - - // Clear settings - settings.clear(); - - // Verify values are gone - TEST_ASSERT_FALSE(settings.hasKey("wifi_ssid")); - TEST_ASSERT_FALSE(settings.hasKey("wifi_password")); -} - -int main() { - UNITY_BEGIN(); - - RUN_TEST(test_wifi_settings_save_load); - RUN_TEST(test_wifi_settings_empty); - RUN_TEST(test_wifi_settings_partial); - RUN_TEST(test_wifi_settings_persistence); - RUN_TEST(test_wifi_settings_clear); - - return UNITY_END(); -} \ No newline at end of file From 3f75270e352c4496b2de9743f1fd55cf5059b44f Mon Sep 17 00:00:00 2001 From: Jesus Carrillo Date: Tue, 22 Jul 2025 14:07:07 -0700 Subject: [PATCH 2/2] Standardise builds via Makefile & fix CI failures --- .github/workflows/ci-cd.yml | 92 +++++++++++++---------------- .github/workflows/size-baseline.yml | 5 +- Makefile | 5 +- README.md | 8 +++ platformio.ini | 5 +- 5 files changed, 57 insertions(+), 58 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 360767c..10894d2 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -47,31 +47,27 @@ jobs: sudo apt-get install build-essential bc jq - - name: Compile project (ttgo-lora32-v1) - run: pio run -e ttgo-lora32-v1 + - name: Compile project (release) + run: make release - name: Compile project (minimal) - run: pio run -e minimal + run: make minimal - name: Check flash size limit run: | - # Get build output and extract flash usage - pio run -e ttgo-lora32-v1 2>&1 | tee build_output.log + # Parse the ELF file that was already built to extract flash usage + ELF_FILE=".pio/build/ttgo-lora32-v1/firmware.elf" - # Extract flash usage from build output - FLASH_USED=$(grep -oP 'Flash:.*used \K\d+' build_output.log | tail -n 1) - - if [ -z "$FLASH_USED" ]; then - echo "⚠️ Warning: Could not extract flash usage from build output" - echo "Looking for alternative patterns..." - # Try alternative patterns - FLASH_USED=$(grep -oP 'used \K\d+' build_output.log | tail -n 1) + if [ ! -f "$ELF_FILE" ]; then + echo "❌ ERROR: ELF file not found at $ELF_FILE" + exit 1 fi - if [ -z "$FLASH_USED" ]; then - echo "❌ ERROR: Could not determine flash usage from build output" - echo "Build output (last 20 lines):" - tail -n 20 build_output.log + # Extract flash usage from ELF file using size command + FLASH_USED=$(size "$ELF_FILE" | tail -n 1 | awk '{print $1 + $2}') + + if [ -z "$FLASH_USED" ] || [ "$FLASH_USED" -eq 0 ]; then + echo "❌ ERROR: Could not determine flash usage from ELF file" exit 1 fi @@ -141,31 +137,27 @@ jobs: pip install --upgrade platformio sudo apt-get install build-essential bc jq - - name: Compile project (ttgo-lora32-v1) - run: pio run -e ttgo-lora32-v1 + - name: Compile project (release) + run: make release - name: Compile project (minimal) - run: pio run -e minimal + run: make minimal - name: Check flash size limit run: | - # Get build output and extract flash usage - pio run -e ttgo-lora32-v1 2>&1 | tee build_output.log + # Parse the ELF file that was already built to extract flash usage + ELF_FILE=".pio/build/ttgo-lora32-v1/firmware.elf" - # Extract flash usage from build output - FLASH_USED=$(grep -oP 'Flash:.*used \K\d+' build_output.log | tail -n 1) - - if [ -z "$FLASH_USED" ]; then - echo "⚠️ Warning: Could not extract flash usage from build output" - echo "Looking for alternative patterns..." - # Try alternative patterns - FLASH_USED=$(grep -oP 'used \K\d+' build_output.log | tail -n 1) + if [ ! -f "$ELF_FILE" ]; then + echo "❌ ERROR: ELF file not found at $ELF_FILE" + exit 1 fi - if [ -z "$FLASH_USED" ]; then - echo "❌ ERROR: Could not determine flash usage from build output" - echo "Build output (last 20 lines):" - tail -n 20 build_output.log + # Extract flash usage from ELF file using size command + FLASH_USED=$(size "$ELF_FILE" | tail -n 1 | awk '{print $1 + $2}') + + if [ -z "$FLASH_USED" ] || [ "$FLASH_USED" -eq 0 ]; then + echo "❌ ERROR: Could not determine flash usage from ELF file" exit 1 fi @@ -268,31 +260,27 @@ jobs: sudo apt-get install build-essential bc jq - - name: Compile with updated firmware version (ttgo-lora32-v1) - run: pio run -e ttgo-lora32-v1 + - name: Compile project (release) + run: make release - - name: Compile with updated firmware version (minimal) - run: pio run -e minimal + - name: Compile project (minimal) + run: make minimal - name: Check flash size limit run: | - # Get build output and extract flash usage - pio run -e ttgo-lora32-v1 2>&1 | tee build_output.log - - # Extract flash usage from build output - FLASH_USED=$(grep -oP 'Flash:.*used \K\d+' build_output.log | tail -n 1) + # Parse the ELF file that was already built to extract flash usage + ELF_FILE=".pio/build/ttgo-lora32-v1/firmware.elf" - if [ -z "$FLASH_USED" ]; then - echo "⚠️ Warning: Could not extract flash usage from build output" - echo "Looking for alternative patterns..." - # Try alternative patterns - FLASH_USED=$(grep -oP 'used \K\d+' build_output.log | tail -n 1) + if [ ! -f "$ELF_FILE" ]; then + echo "❌ ERROR: ELF file not found at $ELF_FILE" + exit 1 fi - if [ -z "$FLASH_USED" ]; then - echo "❌ ERROR: Could not determine flash usage from build output" - echo "Build output (last 20 lines):" - tail -n 20 build_output.log + # Extract flash usage from ELF file using size command + FLASH_USED=$(size "$ELF_FILE" | tail -n 1 | awk '{print $1 + $2}') + + if [ -z "$FLASH_USED" ] || [ "$FLASH_USED" -eq 0 ]; then + echo "❌ ERROR: Could not determine flash usage from ELF file" exit 1 fi diff --git a/.github/workflows/size-baseline.yml b/.github/workflows/size-baseline.yml index 84dafbc..1e7c6ec 100644 --- a/.github/workflows/size-baseline.yml +++ b/.github/workflows/size-baseline.yml @@ -7,6 +7,7 @@ on: env: PLATFORMIO_CACHE_DIR: ~/.platformio/.cache + PLATFORMIO_BUILD_FLAGS: -v jobs: capture-size-baseline: @@ -39,7 +40,7 @@ jobs: - name: Build release and capture artifacts run: | # Clean build with verbose output - use ttgo-lora32-v1 environment for size baseline - pio run -e ttgo-lora32-v1 -v be ci-build-release.log 21 || RELEASE_BUILD_FAILED=1 + make release >&1 | tee ci-build-release.log 2>&1 || RELEASE_BUILD_FAILED=1 # Create baseline artifacts directory mkdir -p ci-baseline-artifacts @@ -92,7 +93,7 @@ jobs: - name: Build debug and capture artifacts run: | # Build minimal environment for comparison - pio run -e minimal -v be ci-build-debug.log 21 || DEBUG_BUILD_FAILED=1 + make minimal >&1 | tee ci-build-debug.log 2>&1 || DEBUG_BUILD_FAILED=1 # Copy debug build log cp ci-build-debug.log ci-baseline-artifacts/ diff --git a/Makefile b/Makefile index a10ec41..964722c 100644 --- a/Makefile +++ b/Makefile @@ -15,12 +15,15 @@ uploadfs: platformio -f -c vim run --target uploadfs # Build the optimized release firmware -.PHONY: release minimal +.PHONY: release minimal test release: ## Build main release env platformio run -e ttgo-lora32-v1 minimal: platformio run -e minimal +# Test target that calls minimal build for CI readability +test: minimal ## Build minimal env (alias for CI) + .PHONY: build-cli build-cli: diff --git a/README.md b/README.md index 8b39a30..d59e61a 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,14 @@ This project uses a single optimized build configuration: For detailed build information, see [docs/BUILD.md](docs/BUILD.md). +## Build Commands + +- `make release`: Build the main release environment (ttgo-lora32-v1) +- `make minimal`: Build the minimal environment for comparison +- For verbose output, set environment variable: `PLATFORMIO_BUILD_FLAGS=-v` + + Example: `PLATFORMIO_BUILD_FLAGS=-v make release` + # Node Provisioning 1. Build the firmware and upload it to the board. diff --git a/platformio.ini b/platformio.ini index da06725..0423102 100644 --- a/platformio.ini +++ b/platformio.ini @@ -87,7 +87,6 @@ build_flags = ${options.common_build_flags} -std=gnu++14 -D MINIMAL_BUILD=1 -lib_deps = - mcci-catena/MCCI LoRaWAN LMIC library @ ^4.1.1 - adafruit/Adafruit MAX1704X@^1.0.3 + -D NDEBUG +lib_deps = ${options.common_lib_deps}