From eaab5703c8ccb0d4414c3d4e2dc8347b26ef080b Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 26 Mar 2025 16:23:07 -0500 Subject: [PATCH 01/48] wip kestrel testing on dirty submods --- .gitmodules | 3 - CMakeLists.txt | 2 +- test/CMakeLists.txt | 71 ++-- test/external/fff | 1 - test/main.cpp | 3 - test/mocks/Arduino.cpp | 72 ++++ test/mocks/Arduino.h | 106 ++++-- test/mocks/MockArduino.cpp | 31 -- test/mocks/MockArduino.h | 89 ----- test/mocks/MockBMA456.cpp | 11 - test/mocks/MockBMA456.h | 37 -- test/mocks/MockGNSS.cpp | 18 - test/mocks/MockGNSS.h | 58 --- test/mocks/MockIncludes.h | 65 ---- test/mocks/MockMCP79412.cpp | 13 - test/mocks/MockMCP79412.h | 34 +- test/mocks/MockMXC6655.cpp | 6 - test/mocks/MockMXC6655.h | 33 +- test/mocks/MockPAC1934.cpp | 17 - test/mocks/MockPAC1934.h | 21 -- test/mocks/MockPCA9634.cpp | 9 - test/mocks/MockPCA9634.h | 36 +- test/mocks/MockPCAL9535A.cpp | 17 - test/mocks/MockPCAL9535A.h | 34 +- test/mocks/MockParticle.h | 143 +++++--- test/mocks/MockSFE_UBLOX_GNSS.h | 36 ++ test/mocks/MockSHT4x.cpp | 17 - test/mocks/MockSHT4x.h | 29 -- test/mocks/MockSPI.cpp | 4 - test/mocks/MockSensor.h | 53 +-- test/mocks/MockVEML3328.cpp | 19 - test/mocks/MockVEML3328.h | 32 +- test/mocks/MockWire.cpp | 17 - test/mocks/MockWireDeclare.h | 41 --- test/mocks/Particle.cpp | 30 ++ test/mocks/Particle.h | 251 +++++++++++-- test/mocks/SPI.h | 33 +- test/mocks/TestKestrel.h | 336 ------------------ test/mocks/WProgram.h | 9 +- test/mocks/Wire.h | 10 +- .../Driver_-_KestrelFunctionTests.cpp | 151 -------- .../Driver_-_KestrelSetupTests.cpp | 317 ----------------- test/unit/Driver_-_Kestrel/KestrelTest.cpp | 238 +++++++++++++ .../FlightControl_DemoTest.cpp | 48 --- .../FlightControl_DemoTestHarness.cpp | 14 - .../FlightControl_DemoTestHarness.h | 12 - 46 files changed, 899 insertions(+), 1728 deletions(-) delete mode 160000 test/external/fff create mode 100644 test/mocks/Arduino.cpp delete mode 100644 test/mocks/MockArduino.cpp delete mode 100644 test/mocks/MockArduino.h delete mode 100644 test/mocks/MockBMA456.cpp delete mode 100644 test/mocks/MockBMA456.h delete mode 100644 test/mocks/MockGNSS.cpp delete mode 100644 test/mocks/MockGNSS.h delete mode 100644 test/mocks/MockIncludes.h delete mode 100644 test/mocks/MockMCP79412.cpp delete mode 100644 test/mocks/MockMXC6655.cpp delete mode 100644 test/mocks/MockPAC1934.cpp delete mode 100644 test/mocks/MockPAC1934.h delete mode 100644 test/mocks/MockPCA9634.cpp delete mode 100644 test/mocks/MockPCAL9535A.cpp create mode 100644 test/mocks/MockSFE_UBLOX_GNSS.h delete mode 100644 test/mocks/MockSHT4x.cpp delete mode 100644 test/mocks/MockSHT4x.h delete mode 100644 test/mocks/MockSPI.cpp delete mode 100644 test/mocks/MockVEML3328.cpp delete mode 100644 test/mocks/MockWire.cpp delete mode 100644 test/mocks/MockWireDeclare.h create mode 100644 test/mocks/Particle.cpp delete mode 100644 test/mocks/TestKestrel.h delete mode 100644 test/unit/Driver_-_Kestrel/Driver_-_KestrelFunctionTests.cpp delete mode 100644 test/unit/Driver_-_Kestrel/Driver_-_KestrelSetupTests.cpp create mode 100644 test/unit/Driver_-_Kestrel/KestrelTest.cpp delete mode 100644 test/unit/FlightControl_Demo/FlightControl_DemoTest.cpp delete mode 100644 test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.cpp delete mode 100644 test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.h diff --git a/.gitmodules b/.gitmodules index fc0685c..a0b5eea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -128,6 +128,3 @@ [submodule "test/external/googletest"] path = test/external/googletest url = https://github.com/google/googletest.git -[submodule "test/external/fff"] - path = test/external/fff - url = https://github.com/meekrosoft/fff.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e61118..bd21d6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.14) project(FlightControl_Demo_Tests) # Specify C++ standard -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Add test directory diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index af8e3f2..7480e2b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,29 +5,35 @@ add_subdirectory(external/googletest) # Define the TESTING preprocessor macro for all test builds add_compile_definitions(TESTING) -# IMPORTANT: Make sure mocks are included first to override system headers -include_directories(BEFORE mocks) +# Set C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Include FFF headers -include_directories(external/fff) - -# Include Google Test headers +# Include Google Test and Google Mock headers include_directories(external/googletest/googletest/include) +include_directories(external/googletest/googlemock/include) + +# Find all driver directories and add them to the include path +file(GLOB DRIVER_DIRS ${CMAKE_SOURCE_DIR}/lib/*/src) -# Create a mocks library -add_library(mocks STATIC - mocks/MockWire.cpp - mocks/MockArduino.cpp - mocks/MockPCAL9535A.cpp - mocks/MockMCP79412.cpp - mocks/MockSPI.cpp - mocks/MockPCA9634.cpp - mocks/MockSHT4x.cpp - mocks/MockVEML3328.cpp - mocks/MockPAC1934.cpp - mocks/MockMXC6655.cpp - mocks/MockBMA456.cpp - mocks/MockGNSS.cpp +# Create a library for the mocks +file(GLOB MOCK_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/mocks/*.cpp +) + +# Add a library of mocks - if there are no source files yet, use an interface library +if(MOCK_SOURCES) + add_library(mocks ${MOCK_SOURCES}) +else() + add_library(mocks INTERFACE) +endif() + +# Add header include directories for mocks - mock directory MUST be first +target_include_directories(mocks BEFORE INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/mocks # Mocks FIRST + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/src + ${DRIVER_DIRS} # Add all driver directories ) # Specify test executable @@ -36,27 +42,10 @@ add_executable(unit_tests main.cpp # Kestrel tests - unit/Driver_-_Kestrel/Driver_-_KestrelSetupTests.cpp - unit/Driver_-_Kestrel/Driver_-_KestrelFunctionTests.cpp -) - -# Link against mocks and GoogleTest -target_link_libraries(unit_tests mocks gtest gtest_main) - -# Find all driver directories and add them to the include path -file(GLOB DRIVER_DIRS ${CMAKE_SOURCE_DIR}/lib/*/src) + unit/Driver_-_Kestrel/KestrelTest.cpp + ${CMAKE_SOURCE_DIR}/lib/Driver_-_Kestrel/src/Kestrel.cpp -# Add header include directories - mock directory MUST be first -target_include_directories(mocks BEFORE PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/mocks # Mocks FIRST - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/src - ${DRIVER_DIRS} # Add all driver directories ) -target_include_directories(unit_tests BEFORE PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/mocks # Mocks FIRST - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/src - ${DRIVER_DIRS} # Add all driver directories -) \ No newline at end of file +# Link against mocks and GoogleTest +target_link_libraries(unit_tests mocks gtest gtest_main gmock) \ No newline at end of file diff --git a/test/external/fff b/test/external/fff deleted file mode 160000 index 5111c61..0000000 --- a/test/external/fff +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5111c61e1ef7848e3afd3550044a8cf4405f4199 diff --git a/test/main.cpp b/test/main.cpp index ec03b1f..879b780 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,8 +1,5 @@ // FlightControl_Demo/test/main.cpp #include "gtest/gtest.h" -#include "fff.h" - -DEFINE_FFF_GLOBALS; int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/test/mocks/Arduino.cpp b/test/mocks/Arduino.cpp new file mode 100644 index 0000000..2d5da28 --- /dev/null +++ b/test/mocks/Arduino.cpp @@ -0,0 +1,72 @@ +#include "Arduino.h" + +// Global variables for time tracking +unsigned long _millis = 0; +unsigned long _micros = 0; + +// Implementation of Arduino functions +unsigned long millis() { + _millis += 100; // Advance time by 100ms each call for testing + return _millis; +} + +unsigned long micros() { + _micros += 1000; // Advance time by 1000us each call for testing + return _micros; +} + +void delay(unsigned long ms) { + // No-op in test +} + +void delayMicroseconds(unsigned int us) { + // No-op in test +} + +void pinMode(uint8_t pin, uint8_t mode) { + // No-op in test +} + +void digitalWrite(uint8_t pin, uint8_t val) { + // No-op in test +} + +int digitalRead(uint8_t pin) { + return LOW; // Default to LOW +} + +int analogRead(uint8_t pin) { + return 0; // Default to 0 +} + +void analogWrite(uint8_t pin, int val) { + // No-op in test +} + +int analogReadResolution(int bits) { + return bits; +} + +void analogWriteResolution(int bits) { + // No-op in test +} + +unsigned long pulseIn(uint8_t pin, uint8_t value, unsigned long timeout) { + return 0; // Default to 0 +} + +long random(long min, long max) { + return min; +} + +long random(long max) { + return 0; +} + +void randomSeed(unsigned long seed) { + // No-op in test +} + +// Global instances +TwoWire Wire; +SPIClass SPI; \ No newline at end of file diff --git a/test/mocks/Arduino.h b/test/mocks/Arduino.h index 371a6c9..99246ee 100644 --- a/test/mocks/Arduino.h +++ b/test/mocks/Arduino.h @@ -1,29 +1,93 @@ -#pragma once +#ifndef ARDUINO_H_MOCK +#define ARDUINO_H_MOCK -// Include our Arduino mocks -#include "MockArduino.h" +// Include Particle.h first to make sure shared types come from there +#include "Particle.h" -// Arduino standard libraries -#include -#include -#include - -// Forward declarations for common Arduino types and functions -// This will silence most Arduino.h include errors +// Basic Arduino types typedef uint8_t byte; +typedef bool boolean; + +// Arduino constants - only defined if not already defined +#ifndef HIGH +#define HIGH 0x1 +#endif +#ifndef LOW +#define LOW 0x0 +#endif +#ifndef INPUT +#define INPUT 0x0 +#endif +#ifndef OUTPUT +#define OUTPUT 0x1 +#endif +#ifndef INPUT_PULLUP +#define INPUT_PULLUP 0x2 +#endif + +// SPI related constants +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV32 0x06 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 + +// Wiring.h based functions +unsigned long millis(); +unsigned long micros(); +void delay(unsigned long ms); +void delayMicroseconds(unsigned int us); +void pinMode(uint8_t pin, uint8_t mode); +void digitalWrite(uint8_t pin, uint8_t val); +int digitalRead(uint8_t pin); +int analogRead(uint8_t pin); +void analogWrite(uint8_t pin, int val); +int analogReadResolution(int bits); +void analogWriteResolution(int bits); +unsigned long pulseIn(uint8_t pin, uint8_t value, unsigned long timeout = 1000000L); + +// Random functions +long random(long min, long max); +long random(long max); +void randomSeed(unsigned long seed); + +// External variables needed by Arduino libraries +extern unsigned long _millis; +extern unsigned long _micros; + +// Use MockSerialPort from Particle.h for Serial and Serial1 +// No need to redefine them here + +// Use TwoWire from Particle.h - no need to redefine +// Wire object instance is defined in Particle.cpp -class HardwareSerial { +// SPI class +class SPIClass { public: - void begin(unsigned long baud) {} - int available() { return 0; } - int read() { return -1; } - void print(const char* str) {} - void print(int val) {} - void println(const char* str) {} - void println(int val) {} + static const uint8_t MOSI = 11; + static const uint8_t MISO = 12; + static const uint8_t SCK = 13; + static const uint8_t SS = 10; + + void begin() {} + void end() {} + void beginTransaction(uint8_t pin, uint8_t mode, uint32_t speed) {} + void endTransaction() {} + uint8_t transfer(uint8_t data) { return 0; } + uint16_t transfer16(uint16_t data) { return 0; } + void transfer(void *buf, size_t count) {} + void setBitOrder(uint8_t bitOrder) {} + void setDataMode(uint8_t dataMode) {} + void setClockDivider(uint8_t clockDiv) {} }; -extern HardwareSerial Serial1; -extern HardwareSerial Serial2; +// SPI object instance +extern SPIClass SPI; -// Add more Arduino types/functions as needed \ No newline at end of file +#endif // ARDUINO_H_MOCK \ No newline at end of file diff --git a/test/mocks/MockArduino.cpp b/test/mocks/MockArduino.cpp deleted file mode 100644 index a3ebc20..0000000 --- a/test/mocks/MockArduino.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// test/mocks/MockArduino.cpp -#include "MockArduino.h" - -// Define fake functions -DEFINE_FAKE_VOID_FUNC(pinMode, uint8_t, uint8_t); -DEFINE_FAKE_VOID_FUNC(digitalWrite, uint8_t, uint8_t); -DEFINE_FAKE_VALUE_FUNC(int, digitalRead, uint8_t); -DEFINE_FAKE_VALUE_FUNC(int, analogRead, uint8_t); -DEFINE_FAKE_VOID_FUNC(analogWrite, uint8_t, int); - -// Time functions -DEFINE_FAKE_VALUE_FUNC(unsigned long, millis); -DEFINE_FAKE_VALUE_FUNC(unsigned long, micros); -DEFINE_FAKE_VOID_FUNC(delay, unsigned long); -DEFINE_FAKE_VOID_FUNC(delayMicroseconds, unsigned int); - -// Misc -DEFINE_FAKE_VOID_FUNC(randomSeed, unsigned long); -DEFINE_FAKE_VALUE_FUNC(long, random, long, long); - -// Serial functions -DEFINE_FAKE_VOID_FUNC(Serial_begin, unsigned long); -DEFINE_FAKE_VALUE_FUNC(int, Serial_available); -DEFINE_FAKE_VALUE_FUNC(int, Serial_read); -DEFINE_FAKE_VOID_FUNC(Serial_print_s, const char*); -DEFINE_FAKE_VOID_FUNC(Serial_print_i, int); -DEFINE_FAKE_VOID_FUNC(Serial_println_s, const char*); -DEFINE_FAKE_VOID_FUNC(Serial_println_i, int); - -// Define the global Serial instance -MockSerial Serial; \ No newline at end of file diff --git a/test/mocks/MockArduino.h b/test/mocks/MockArduino.h deleted file mode 100644 index 4e36f5a..0000000 --- a/test/mocks/MockArduino.h +++ /dev/null @@ -1,89 +0,0 @@ -// test/mocks/MockArduino.h -#pragma once - -#include -#include "fff.h" - -// Define Arduino pin modes -#define INPUT 0 -#define OUTPUT 1 -#define INPUT_PULLUP 2 -#define INPUT_PULLDOWN 3 - -// Define pin values -#define HIGH 1 -#define LOW 0 - -// Define common Arduino pins -#define A0 14 -#define A1 15 -#define A2 16 -#define A3 17 -#define A4 18 -#define A5 19 -#define A6 20 -#define A7 21 -#define D0 0 -#define D1 1 -#define D2 2 -#define D3 3 -#define D4 4 -#define D5 5 -#define D6 6 -#define D7 7 -#define D8 8 -#define D9 9 -#define D10 10 -#define D11 11 -#define D12 12 -#define D13 13 -#define D14 14 -#define D15 15 -#define D16 16 -#define D17 17 -#define D18 18 -#define D19 19 -#define D20 20 -#define D21 21 -#define D22 22 -#define D23 23 - -// Digital and Analog I/O -DECLARE_FAKE_VOID_FUNC(pinMode, uint8_t, uint8_t); -DECLARE_FAKE_VOID_FUNC(digitalWrite, uint8_t, uint8_t); -DECLARE_FAKE_VALUE_FUNC(int, digitalRead, uint8_t); -DECLARE_FAKE_VALUE_FUNC(int, analogRead, uint8_t); -DECLARE_FAKE_VOID_FUNC(analogWrite, uint8_t, int); - -// Time functions -DECLARE_FAKE_VALUE_FUNC(unsigned long, millis); -DECLARE_FAKE_VALUE_FUNC(unsigned long, micros); -DECLARE_FAKE_VOID_FUNC(delay, unsigned long); -DECLARE_FAKE_VOID_FUNC(delayMicroseconds, unsigned int); - -// Misc -DECLARE_FAKE_VOID_FUNC(randomSeed, unsigned long); -DECLARE_FAKE_VALUE_FUNC(long, random, long, long); - -// Forward declarations for Serial functions -DECLARE_FAKE_VOID_FUNC(Serial_begin, unsigned long); -DECLARE_FAKE_VALUE_FUNC(int, Serial_available); -DECLARE_FAKE_VALUE_FUNC(int, Serial_read); -DECLARE_FAKE_VOID_FUNC(Serial_print_s, const char*); -DECLARE_FAKE_VOID_FUNC(Serial_print_i, int); -DECLARE_FAKE_VOID_FUNC(Serial_println_s, const char*); -DECLARE_FAKE_VOID_FUNC(Serial_println_i, int); - -// Mock class for the Serial object -class MockSerial { -public: - void begin(unsigned long baud) { Serial_begin(baud); } - int available() { return Serial_available(); } - int read() { return Serial_read(); } - void print(const char* str) { Serial_print_s(str); } - void print(int val) { Serial_print_i(val); } - void println(const char* str) { Serial_println_s(str); } - void println(int val) { Serial_println_i(val); } -}; - -extern MockSerial Serial; \ No newline at end of file diff --git a/test/mocks/MockBMA456.cpp b/test/mocks/MockBMA456.cpp deleted file mode 100644 index 66d38e2..0000000 --- a/test/mocks/MockBMA456.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "MockBMA456.h" - -// Define fake functions for BMA456 -DEFINE_FAKE_VALUE_FUNC(bool, BMA456_begin); -DEFINE_FAKE_VALUE_FUNC(bool, BMA456_readAcceleration, float*, float*, float*); -DEFINE_FAKE_VALUE_FUNC(bool, BMA456_enableStepCounter, bool); -DEFINE_FAKE_VALUE_FUNC(uint32_t, BMA456_getStepCount); -DEFINE_FAKE_VALUE_FUNC(bool, BMA456_resetStepCounter); - -// Initialize the global object -BMA456 accel_bma456; \ No newline at end of file diff --git a/test/mocks/MockBMA456.h b/test/mocks/MockBMA456.h deleted file mode 100644 index 40549fa..0000000 --- a/test/mocks/MockBMA456.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "fff.h" -#include - -// Declare fake functions for BMA456 accelerometer -DECLARE_FAKE_VALUE_FUNC(bool, BMA456_begin); -DECLARE_FAKE_VALUE_FUNC(bool, BMA456_readAcceleration, float*, float*, float*); -DECLARE_FAKE_VALUE_FUNC(bool, BMA456_enableStepCounter, bool); -DECLARE_FAKE_VALUE_FUNC(uint32_t, BMA456_getStepCount); -DECLARE_FAKE_VALUE_FUNC(bool, BMA456_resetStepCounter); - -// Mock class for BMA456 via arduino_bma456 -class BMA456 { -public: - // Default mock values - float x_acceleration = 0.0f; - float y_acceleration = 0.0f; - float z_acceleration = 1.0f; // Default to 1G in Z direction (earth gravity) - uint32_t step_count = 0; - - bool begin() { return BMA456_begin(); } - - bool readAcceleration(float* x, float* y, float* z) { - *x = x_acceleration; - *y = y_acceleration; - *z = z_acceleration; - return BMA456_readAcceleration(x, y, z); - } - - bool enableStepCounter(bool enable) { return BMA456_enableStepCounter(enable); } - uint32_t getStepCount() { return BMA456_getStepCount(); } - bool resetStepCounter() { return BMA456_resetStepCounter(); } -}; - -// This simulates the global object provided by the arduino_bma456 library -extern BMA456 accel_bma456; \ No newline at end of file diff --git a/test/mocks/MockGNSS.cpp b/test/mocks/MockGNSS.cpp deleted file mode 100644 index 4d88aa9..0000000 --- a/test/mocks/MockGNSS.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "MockGNSS.h" - -// Define fake functions for GNSS -DEFINE_FAKE_VALUE_FUNC(bool, GNSS_begin, int); -DEFINE_FAKE_VALUE_FUNC(bool, GNSS_isConnected); -DEFINE_FAKE_VALUE_FUNC(bool, GNSS_setI2COutput, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, GNSS_setNavigationFrequency, uint8_t); -DEFINE_FAKE_VALUE_FUNC(long, GNSS_getLatitude); -DEFINE_FAKE_VALUE_FUNC(long, GNSS_getLongitude); -DEFINE_FAKE_VALUE_FUNC(long, GNSS_getAltitude); -DEFINE_FAKE_VALUE_FUNC(uint8_t, GNSS_getSIV); -DEFINE_FAKE_VALUE_FUNC(uint32_t, GNSS_getTimeOfWeek); -DEFINE_FAKE_VALUE_FUNC(uint16_t, GNSS_getYear); -DEFINE_FAKE_VALUE_FUNC(uint8_t, GNSS_getMonth); -DEFINE_FAKE_VALUE_FUNC(uint8_t, GNSS_getDay); -DEFINE_FAKE_VALUE_FUNC(uint8_t, GNSS_getHour); -DEFINE_FAKE_VALUE_FUNC(uint8_t, GNSS_getMinute); -DEFINE_FAKE_VALUE_FUNC(uint8_t, GNSS_getSecond); \ No newline at end of file diff --git a/test/mocks/MockGNSS.h b/test/mocks/MockGNSS.h deleted file mode 100644 index 7511d12..0000000 --- a/test/mocks/MockGNSS.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "fff.h" -#include -#include - -// Declare fake functions for u-blox GNSS -DECLARE_FAKE_VALUE_FUNC(bool, GNSS_begin, int); -DECLARE_FAKE_VALUE_FUNC(bool, GNSS_isConnected); -DECLARE_FAKE_VALUE_FUNC(bool, GNSS_setI2COutput, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, GNSS_setNavigationFrequency, uint8_t); -DECLARE_FAKE_VALUE_FUNC(long, GNSS_getLatitude); -DECLARE_FAKE_VALUE_FUNC(long, GNSS_getLongitude); -DECLARE_FAKE_VALUE_FUNC(long, GNSS_getAltitude); -DECLARE_FAKE_VALUE_FUNC(uint8_t, GNSS_getSIV); -DECLARE_FAKE_VALUE_FUNC(uint32_t, GNSS_getTimeOfWeek); -DECLARE_FAKE_VALUE_FUNC(uint16_t, GNSS_getYear); -DECLARE_FAKE_VALUE_FUNC(uint8_t, GNSS_getMonth); -DECLARE_FAKE_VALUE_FUNC(uint8_t, GNSS_getDay); -DECLARE_FAKE_VALUE_FUNC(uint8_t, GNSS_getHour); -DECLARE_FAKE_VALUE_FUNC(uint8_t, GNSS_getMinute); -DECLARE_FAKE_VALUE_FUNC(uint8_t, GNSS_getSecond); - -// Mock class for SFE_UBLOX_GNSS -class SFE_UBLOX_GNSS { -public: - // Default mock values - long latitude = 449673925; // Minneapolis ~44.96°N - long longitude = -932838386; // Minneapolis ~-93.28°E - long altitude = 25000; // 250m above MSL - uint8_t satellites = 8; // 8 satellites in view - - // Time values - uint16_t year = 2023; - uint8_t month = 5; - uint8_t day = 15; - uint8_t hour = 10; - uint8_t minute = 30; - uint8_t second = 0; - - bool begin(int wirePort = 0) { return GNSS_begin(wirePort); } - bool isConnected() { return GNSS_isConnected(); } - bool setI2COutput(uint8_t comSettings) { return GNSS_setI2COutput(comSettings); } - bool setNavigationFrequency(uint8_t navFreq) { return GNSS_setNavigationFrequency(navFreq); } - - long getLatitude() { return GNSS_getLatitude(); } - long getLongitude() { return GNSS_getLongitude(); } - long getAltitude() { return GNSS_getAltitude(); } - uint8_t getSIV() { return GNSS_getSIV(); } - - uint32_t getTimeOfWeek() { return GNSS_getTimeOfWeek(); } - uint16_t getYear() { return GNSS_getYear(); } - uint8_t getMonth() { return GNSS_getMonth(); } - uint8_t getDay() { return GNSS_getDay(); } - uint8_t getHour() { return GNSS_getHour(); } - uint8_t getMinute() { return GNSS_getMinute(); } - uint8_t getSecond() { return GNSS_getSecond(); } -}; \ No newline at end of file diff --git a/test/mocks/MockIncludes.h b/test/mocks/MockIncludes.h deleted file mode 100644 index 0a677c9..0000000 --- a/test/mocks/MockIncludes.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -// This file contains includes and settings for testing the Kestrel driver with real code -// It ensures we properly redirect hardware calls to mocks while avoiding redefinition errors - -// Tell the compiler we're in testing mode - this can be used in #ifdef blocks -#define TESTING - -// First define all the hardware classes as empty forward declarations -// This prevents the real implementations from being compiled -class Sensor; -class PCAL9535A; -class PCA9634; -class MCP79412; -class SFE_UBLOX_GNSS; -class PAC1934; -class VEML3328; -class Adafruit_SHT4x; -class MXC6655; -class BMA456; - -// Now include our mock implementations -#include "MockSensor.h" -#include "MockPCAL9535A.h" -#include "MockPCA9634.h" -#include "MockMCP79412.h" -#include "MockGNSS.h" -#include "MockPAC1934.h" -#include "MockVEML3328.h" -#include "MockSHT4x.h" -#include "MockMXC6655.h" -#include "MockBMA456.h" -#include "MockArduino.h" -#include "MockWireDeclare.h" - -// Define any constants that might be needed by Kestrel.h -#define OUTPUT 1 -#define INPUT 0 -#define HIGH 1 -#define LOW 0 - -// Arduino constants -#define A0 14 -#define A1 15 -#define A2 16 -#define A3 17 -#define A4 18 -#define A5 19 -#define A6 20 -#define A7 21 -#define D0 0 -#define D1 1 -#define D2 2 -#define D3 3 -#define D4 4 -#define D5 5 -#define D6 6 -#define D7 7 -#define D8 8 -#define D22 22 -#define D23 23 - -// Define additional constants that Kestrel.h might need -// If there are specific enum values or constants that Kestrel.h expects, -// add them here \ No newline at end of file diff --git a/test/mocks/MockMCP79412.cpp b/test/mocks/MockMCP79412.cpp deleted file mode 100644 index 2641e83..0000000 --- a/test/mocks/MockMCP79412.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "MockMCP79412.h" - -// Define fake functions -DEFINE_FAKE_VALUE_FUNC(bool, MCP79412_begin); -DEFINE_FAKE_VALUE_FUNC(time_t, MCP79412_getTime); -DEFINE_FAKE_VALUE_FUNC(bool, MCP79412_setTime, time_t); - -// Set default behavior -void setup_mock_MCP79412_defaults() { - MCP79412_begin_fake.return_val = true; - MCP79412_getTime_fake.return_val = time(NULL); // Current time - MCP79412_setTime_fake.return_val = true; -} \ No newline at end of file diff --git a/test/mocks/MockMCP79412.h b/test/mocks/MockMCP79412.h index b2117ae..53f91d8 100644 --- a/test/mocks/MockMCP79412.h +++ b/test/mocks/MockMCP79412.h @@ -1,23 +1,17 @@ -#pragma once +#ifndef MOCK_MCP79412_H +#define MOCK_MCP79412_H -#include "fff.h" -#include -#include +#include +#include "MCP79412.h" -// Declare fake functions -DECLARE_FAKE_VALUE_FUNC(bool, MCP79412_begin); -DECLARE_FAKE_VALUE_FUNC(time_t, MCP79412_getTime); -DECLARE_FAKE_VALUE_FUNC(bool, MCP79412_setTime, time_t); - -// Mock class for MCP79412 RTC -class MCP79412 { +class MockMCP79412 : public MCP79412 { public: - bool begin() { return MCP79412_begin(); } - time_t getTime() { return MCP79412_getTime(); } - bool setTime(time_t time) { return MCP79412_setTime(time); } - - // Additional properties to match the real implementation - uint8_t numErrors = 0; - - // Add more methods as needed for Kestrel tests -}; \ No newline at end of file + MOCK_METHOD(int, begin, (bool exOsc)); + MOCK_METHOD(time_t, getTimeUnix, ()); + MOCK_METHOD(String, getUUIDString, ()); + MOCK_METHOD(int, setMode, (MCP79412::Mode mode)); + MOCK_METHOD(int, enableAlarm, (bool enable, uint8_t alarmNum)); + MOCK_METHOD(uint8_t, readByte, (uint8_t address)); +}; + +#endif // MOCK_MCP79412_H \ No newline at end of file diff --git a/test/mocks/MockMXC6655.cpp b/test/mocks/MockMXC6655.cpp deleted file mode 100644 index 535b0dc..0000000 --- a/test/mocks/MockMXC6655.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "MockMXC6655.h" - -// Define fake functions for MXC6655 -DEFINE_FAKE_VALUE_FUNC(bool, MXC6655_begin); -DEFINE_FAKE_VALUE_FUNC(bool, MXC6655_readAcceleration, float*, float*, float*); -DEFINE_FAKE_VALUE_FUNC(bool, MXC6655_resetOrientation); \ No newline at end of file diff --git a/test/mocks/MockMXC6655.h b/test/mocks/MockMXC6655.h index 40509ef..b23bbab 100644 --- a/test/mocks/MockMXC6655.h +++ b/test/mocks/MockMXC6655.h @@ -1,29 +1,8 @@ -#pragma once +#include +#include "MXC6655.h" -#include "fff.h" -#include - -// Declare fake functions for MXC6655 accelerometer -DECLARE_FAKE_VALUE_FUNC(bool, MXC6655_begin); -DECLARE_FAKE_VALUE_FUNC(bool, MXC6655_readAcceleration, float*, float*, float*); -DECLARE_FAKE_VALUE_FUNC(bool, MXC6655_resetOrientation); - -// Mock class for MXC6655 accelerometer -class MXC6655 { -public: - // Default mock values - float x_acceleration = 0.0f; - float y_acceleration = 0.0f; - float z_acceleration = 1.0f; // Default to 1G in Z direction (earth gravity) - - bool begin() { return MXC6655_begin(); } - - bool readAcceleration(float* x, float* y, float* z) { - *x = x_acceleration; - *y = y_acceleration; - *z = z_acceleration; - return MXC6655_readAcceleration(x, y, z); - } - - bool resetOrientation() { return MXC6655_resetOrientation(); } +class MockMXC6655 : public MXC6655 { + public: + MOCK_METHOD(int, begin, ()); + MOCK_METHOD(float, getAccel, (uint8_t axis, uint8_t range)); }; \ No newline at end of file diff --git a/test/mocks/MockPAC1934.cpp b/test/mocks/MockPAC1934.cpp deleted file mode 100644 index a1c86ff..0000000 --- a/test/mocks/MockPAC1934.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "MockPAC1934.h" - -// Define fake functions -DEFINE_FAKE_VALUE_FUNC(bool, PAC1934_begin); -DEFINE_FAKE_VALUE_FUNC(float, PAC1934_readVoltage, uint8_t); -DEFINE_FAKE_VALUE_FUNC(float, PAC1934_readCurrent, uint8_t); -DEFINE_FAKE_VALUE_FUNC(float, PAC1934_readPower, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, PAC1934_enableChannel, uint8_t, bool); - -// Set default behavior -void setup_mock_PAC1934_defaults() { - PAC1934_begin_fake.return_val = true; - PAC1934_readVoltage_fake.return_val = 3.3f; - PAC1934_readCurrent_fake.return_val = 0.1f; - PAC1934_readPower_fake.return_val = 0.33f; - PAC1934_enableChannel_fake.return_val = true; -} diff --git a/test/mocks/MockPAC1934.h b/test/mocks/MockPAC1934.h deleted file mode 100644 index b39276f..0000000 --- a/test/mocks/MockPAC1934.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "fff.h" -#include - -// Declare fake functions for PAC1934 (power monitor) -DECLARE_FAKE_VALUE_FUNC(bool, PAC1934_begin); -DECLARE_FAKE_VALUE_FUNC(float, PAC1934_readVoltage, uint8_t); -DECLARE_FAKE_VALUE_FUNC(float, PAC1934_readCurrent, uint8_t); -DECLARE_FAKE_VALUE_FUNC(float, PAC1934_readPower, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, PAC1934_enableChannel, uint8_t, bool); - -// Mock class for PAC1934 -class PAC1934 { -public: - bool begin() { return PAC1934_begin(); } - float readVoltage(uint8_t channel) { return PAC1934_readVoltage(channel); } - float readCurrent(uint8_t channel) { return PAC1934_readCurrent(channel); } - float readPower(uint8_t channel) { return PAC1934_readPower(channel); } - bool enableChannel(uint8_t channel, bool enable) { return PAC1934_enableChannel(channel, enable); } -}; diff --git a/test/mocks/MockPCA9634.cpp b/test/mocks/MockPCA9634.cpp deleted file mode 100644 index 4bfd766..0000000 --- a/test/mocks/MockPCA9634.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "MockPCA9634.h" - -// Define fake functions for PCA9634 -DEFINE_FAKE_VALUE_FUNC(bool, PCA9634_begin); -DEFINE_FAKE_VALUE_FUNC(bool, PCA9634_begin_address, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, PCA9634_setLEDOutputMode, uint8_t, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, PCA9634_setLEDDriverMode, uint8_t, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, PCA9634_setBrightness, uint8_t, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, PCA9634_setOutputState, uint8_t, uint8_t); \ No newline at end of file diff --git a/test/mocks/MockPCA9634.h b/test/mocks/MockPCA9634.h index 76f41d4..ac23404 100644 --- a/test/mocks/MockPCA9634.h +++ b/test/mocks/MockPCA9634.h @@ -1,23 +1,19 @@ -#pragma once +#ifndef MOCK_PCA9634_H +#define MOCK_PCA9634_H -#include "fff.h" -#include +#include +#include "PCA9634.h" -// Declare fake functions for PCA9634 LED driver -DECLARE_FAKE_VALUE_FUNC(bool, PCA9634_begin); -DECLARE_FAKE_VALUE_FUNC(bool, PCA9634_begin_address, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, PCA9634_setLEDOutputMode, uint8_t, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, PCA9634_setLEDDriverMode, uint8_t, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, PCA9634_setBrightness, uint8_t, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, PCA9634_setOutputState, uint8_t, uint8_t); - -// Mock class for PCA9634 LED driver -class PCA9634 { +class MockPCA9634 : public PCA9634 { public: - bool begin() { return PCA9634_begin(); } - bool begin(uint8_t address) { return PCA9634_begin_address(address); } - bool setLEDOutputMode(uint8_t ledNum, uint8_t mode) { return PCA9634_setLEDOutputMode(ledNum, mode); } - bool setLEDDriverMode(uint8_t ledNum, uint8_t mode) { return PCA9634_setLEDDriverMode(ledNum, mode); } - bool setBrightness(uint8_t ledNum, uint8_t brightness) { return PCA9634_setBrightness(ledNum, brightness); } - bool setOutputState(uint8_t ledNum, uint8_t state) { return PCA9634_setOutputState(ledNum, state); } -}; \ No newline at end of file + MockPCA9634(uint8_t address = 0) : PCA9634(address) {} + + MOCK_METHOD(int, begin, ()); + MOCK_METHOD(int, setOutputMode, (OutputMode State)); + MOCK_METHOD(int, setGroupMode, (GroupMode State)); + MOCK_METHOD(int, setOutputArray, (PortState Val)); + MOCK_METHOD(int, setBrightnessArray, (float Brightness)); + MOCK_METHOD(int, setGroupOnTime, (uint16_t Period)); +}; + +#endif // MOCK_PCA9634_H \ No newline at end of file diff --git a/test/mocks/MockPCAL9535A.cpp b/test/mocks/MockPCAL9535A.cpp deleted file mode 100644 index 88c502f..0000000 --- a/test/mocks/MockPCAL9535A.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "MockPCAL9535A.h" - -// Define fake functions -DEFINE_FAKE_VALUE_FUNC(bool, PCAL9535A_begin); -DEFINE_FAKE_VALUE_FUNC(bool, PCAL9535A_pinMode, uint8_t, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, PCAL9535A_digitalWrite, uint8_t, uint8_t); -DEFINE_FAKE_VALUE_FUNC(uint8_t, PCAL9535A_digitalRead, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, PCAL9535A_begin_address, uint8_t); - -// Set default behavior -void setup_mock_PCAL9535A_defaults() { - PCAL9535A_begin_fake.return_val = true; - PCAL9535A_pinMode_fake.return_val = true; - PCAL9535A_digitalWrite_fake.return_val = true; - PCAL9535A_digitalRead_fake.return_val = 0; - PCAL9535A_begin_address_fake.return_val = true; -} \ No newline at end of file diff --git a/test/mocks/MockPCAL9535A.h b/test/mocks/MockPCAL9535A.h index 0646586..c6ce337 100644 --- a/test/mocks/MockPCAL9535A.h +++ b/test/mocks/MockPCAL9535A.h @@ -1,23 +1,17 @@ -#pragma once +#ifndef MOCK_PCAL9535A_H +#define MOCK_PCAL9535A_H -#include "fff.h" -#include +#include +#include "PCAL9535A.h" -// Declare fake functions -DECLARE_FAKE_VALUE_FUNC(bool, PCAL9535A_begin); -DECLARE_FAKE_VALUE_FUNC(bool, PCAL9535A_pinMode, uint8_t, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, PCAL9535A_digitalWrite, uint8_t, uint8_t); -DECLARE_FAKE_VALUE_FUNC(uint8_t, PCAL9535A_digitalRead, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, PCAL9535A_begin_address, uint8_t); - -// Mock class for PCAL9535A IO expander -class PCAL9535A { +class MockPCAL9535A : public PCAL9535A { public: - bool begin() { return PCAL9535A_begin(); } - bool pinMode(uint8_t pin, uint8_t mode) { return PCAL9535A_pinMode(pin, mode); } - bool digitalWrite(uint8_t pin, uint8_t value) { return PCAL9535A_digitalWrite(pin, value); } - uint8_t digitalRead(uint8_t pin) { return PCAL9535A_digitalRead(pin); } - bool begin(uint8_t address) { return PCAL9535A_begin_address(address); } - - // Add more methods as needed for Kestrel tests -}; \ No newline at end of file + MOCK_METHOD(bool, begin, (TwoWire* wire, uint8_t addr)); + MOCK_METHOD(bool, pinMode, (uint16_t pin, uint8_t mode)); + MOCK_METHOD(bool, digitalWrite, (uint16_t pin, uint8_t value)); + MOCK_METHOD(uint8_t, digitalRead, (uint16_t pin)); + MOCK_METHOD(bool, digitalWritePort, (uint8_t port, uint8_t value)); + MOCK_METHOD(uint8_t, digitalReadPort, (uint8_t port)); +}; + +#endif // MOCK_PCAL9535A_H \ No newline at end of file diff --git a/test/mocks/MockParticle.h b/test/mocks/MockParticle.h index 38ba1be..d28dcf0 100644 --- a/test/mocks/MockParticle.h +++ b/test/mocks/MockParticle.h @@ -1,70 +1,105 @@ -// test/mocks/mock_particle.h -#pragma once +#ifndef MOCK_PARTICLE_H +#define MOCK_PARTICLE_H + +#include #include -#include -#include -// Basic String implementation to match Particle's String class -class String { -private: - std::string data; +// Define system event type similar to Particle's definition +typedef int system_event_t; + +// Event type values +const int time_changed = 1; +const int out_of_memory = 2; +// Mock String class for tests +class String { public: - String() : data("") {} - String(const char* str) : data(str ? str : "") {} - String(const std::string& str) : data(str) {} - String(int value) : data(std::to_string(value)) {} + String() : value_("") {} + String(const char* str) : value_(str ? str : "") {} + String(const std::string& str) : value_(str) {} + String(int val) : value_(std::to_string(val)) {} + String(long val) : value_(std::to_string(val)) {} + String(float val, int decimalPlaces = 2) : value_(std::to_string(val)) {} - // Common methods used in the tests - bool equals(const String& other) const { return data == other.data; } - bool startsWith(const String& prefix) const { - return data.find(prefix.data) == 0; - } - bool endsWith(const String& suffix) const { - if (suffix.data.length() > data.length()) return false; - return data.rfind(suffix.data) == (data.length() - suffix.data.length()); + bool equals(const String& other) const { + return value_ == other.value_; } - - // Operators - String operator+(const String& rhs) const { - return String(data + rhs.data); + + String substring(size_t beginIndex) const { + return value_.substr(beginIndex); } - String& operator+=(const String& rhs) { - data += rhs.data; - return *this; + + String substring(size_t beginIndex, size_t endIndex) const { + return value_.substr(beginIndex, endIndex - beginIndex); } - bool operator==(const String& rhs) const { - return data == rhs.data; + + size_t length() const { + return value_.length(); } - // Conversion operators - operator const char*() const { return data.c_str(); } - - // Common String methods - size_t length() const { return data.length(); } - const char* c_str() const { return data.c_str(); } - - // Specific methods needed for your FlightControl_Demo - bool endsWith(char c) const { - return !data.empty() && data.back() == c; + const char* c_str() const { + return value_.c_str(); } - - int lastIndexOf(char c) const { - size_t pos = data.rfind(c); - return pos != std::string::npos ? static_cast(pos) : -1; + + bool operator==(const String& other) const { + return value_ == other.value_; } - - int lastIndexOf(const String& str) const { - size_t pos = data.rfind(str.data); - return pos != std::string::npos ? static_cast(pos) : -1; + + bool operator==(const char* other) const { + return value_ == other; } - - // Add more methods as they're needed + + String operator+(const String& other) const { + return String(value_ + other.value_); + } + + String operator+(const char* other) const { + return String(value_ + other); + } + +private: + std::string value_; +}; + +// For gtest to print Strings in a readable way +inline std::ostream& operator<<(std::ostream& os, const String& str) { + return os << str.c_str(); +} + +// Mock for Particle System functionality +class MockSystemClass { +public: + MOCK_METHOD(void, on, (int event, void (*handler)(system_event_t, int))); + MOCK_METHOD(uint32_t, resetReason, ()); + MOCK_METHOD(bool, connected, ()); +}; + +// Mock for Wire/I2C functionality +class MockTwoWire { +public: + MOCK_METHOD(bool, isEnabled, ()); + MOCK_METHOD(void, begin, ()); + MOCK_METHOD(void, setClock, (uint32_t)); + MOCK_METHOD(void, beginTransmission, (uint8_t)); + MOCK_METHOD(uint8_t, endTransmission, (bool)); + MOCK_METHOD(uint8_t, requestFrom, (uint8_t, uint8_t, bool)); + MOCK_METHOD(int, available, ()); + MOCK_METHOD(int, read, ()); + MOCK_METHOD(size_t, write, (uint8_t)); + MOCK_METHOD(size_t, write, (const uint8_t*, size_t)); }; -// Add other Particle types and functions as needed -// For example: -typedef uint8_t byte; +// Mock EEPROM namespace +namespace EEPROM { + template + void get(int addr, T& val) { + val = T(); // Default construct the value + } +} + +// Define platform IDs +#define PLATFORM_BSOM 23 +#define PLATFORM_B5SOM 32 +#define PLATFORM_ID PLATFORM_BSOM -// Time functions are implemented in MockArduino.cpp -// Don't define millis() here to avoid duplicate definition \ No newline at end of file +#endif // MOCK_PARTICLE_H \ No newline at end of file diff --git a/test/mocks/MockSFE_UBLOX_GNSS.h b/test/mocks/MockSFE_UBLOX_GNSS.h new file mode 100644 index 0000000..9582d3a --- /dev/null +++ b/test/mocks/MockSFE_UBLOX_GNSS.h @@ -0,0 +1,36 @@ +#ifndef MOCK_SFE_UBLOX_GNSS_H +#define MOCK_SFE_UBLOX_GNSS_H + +#include +#include "SparkFun_u-blox_GNSS_Arduino_Library.h" + +// Definitions for protocol types +#define COM_TYPE_UBX 0x01 + +// UBX classes and message IDs +#define UBX_CLASS_NAV 0x01 +#define UBX_NAV_STATUS 0x03 +#define UBX_CLASS_CFG 0x06 +#define UBX_MON_VER 0x04 + +// Default payload size +#define MAX_PAYLOAD_SIZE 256 + +// Mock for SFE_UBLOX_GNSS +class MockSFE_UBLOX_GNSS : public SFE_UBLOX_GNSS { +public: + MOCK_METHOD(bool, begin, ()); + MOCK_METHOD(void, setI2COutput, (uint8_t comType)); + MOCK_METHOD(bool, setNavigationFrequency, (uint8_t navFreq)); + MOCK_METHOD(void, setAutoPVT, (bool autoPVT)); + MOCK_METHOD(uint8_t, getNavigationFrequency, ()); + MOCK_METHOD(uint8_t, getMeasurementRate, ()); + MOCK_METHOD(uint8_t, getNavigationRate, ()); + MOCK_METHOD(int16_t, getATTroll, ()); + MOCK_METHOD(int16_t, getATTpitch, ()); + MOCK_METHOD(int16_t, getATTheading, ()); + MOCK_METHOD(void, setPacketCfgPayloadSize, (uint16_t payloadSize)); + MOCK_METHOD(uint8_t, sendCommand, (ubxPacket* outgoingUBX, uint16_t maxWait)); +}; + +#endif // MOCK_SFE_UBLOX_GNSS_H \ No newline at end of file diff --git a/test/mocks/MockSHT4x.cpp b/test/mocks/MockSHT4x.cpp deleted file mode 100644 index f944eb3..0000000 --- a/test/mocks/MockSHT4x.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "MockSHT4x.h" - -// Define fake functions -DEFINE_FAKE_VALUE_FUNC(bool, SHT4x_begin); -DEFINE_FAKE_VALUE_FUNC(bool, SHT4x_setPrecision, uint8_t); -DEFINE_FAKE_VALUE_FUNC(bool, SHT4x_readTemperature); -DEFINE_FAKE_VALUE_FUNC(bool, SHT4x_readHumidity); -DEFINE_FAKE_VALUE_FUNC(bool, SHT4x_getEvent); - -// Set default behavior -void setup_mock_SHT4x_defaults() { - SHT4x_begin_fake.return_val = true; - SHT4x_setPrecision_fake.return_val = true; - SHT4x_readTemperature_fake.return_val = true; - SHT4x_readHumidity_fake.return_val = true; - SHT4x_getEvent_fake.return_val = true; -} diff --git a/test/mocks/MockSHT4x.h b/test/mocks/MockSHT4x.h deleted file mode 100644 index 22081ea..0000000 --- a/test/mocks/MockSHT4x.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "fff.h" -#include - -// Constants to match Adafruit's SHT4x -#define SHT4X_HIGH_PRECISION 0 -#define SHT4X_MED_PRECISION 1 -#define SHT4X_LOW_PRECISION 2 - -// Declare fake functions -DECLARE_FAKE_VALUE_FUNC(bool, SHT4x_begin); -DECLARE_FAKE_VALUE_FUNC(bool, SHT4x_setPrecision, uint8_t); -DECLARE_FAKE_VALUE_FUNC(bool, SHT4x_readTemperature); -DECLARE_FAKE_VALUE_FUNC(bool, SHT4x_readHumidity); -DECLARE_FAKE_VALUE_FUNC(bool, SHT4x_getEvent); - -// Mock class for Adafruit_SHT4x -class Adafruit_SHT4x { -public: - float temperature = 25.0f; // Default mock values - float humidity = 50.0f; - - bool begin() { return SHT4x_begin(); } - bool setPrecision(uint8_t precision) { return SHT4x_setPrecision(precision); } - float readTemperature() { SHT4x_readTemperature(); return temperature; } - float readHumidity() { SHT4x_readHumidity(); return humidity; } - bool getEvent() { return SHT4x_getEvent(); } -}; diff --git a/test/mocks/MockSPI.cpp b/test/mocks/MockSPI.cpp deleted file mode 100644 index 430c89b..0000000 --- a/test/mocks/MockSPI.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "SPI.h" - -// Define the extern variable -SPIClass SPI; \ No newline at end of file diff --git a/test/mocks/MockSensor.h b/test/mocks/MockSensor.h index 3c27298..66a81e2 100644 --- a/test/mocks/MockSensor.h +++ b/test/mocks/MockSensor.h @@ -1,36 +1,19 @@ -#pragma once +#include +#include "Sensor.h" -#include -#include "MockParticle.h" - -// This is a minimal mock of the Sensor base class that Kestrel inherits from -class Sensor { -public: - Sensor() {} - virtual ~Sensor() {} - - // Common base class properties - static constexpr int MAX_NUM_ERRORS = 10; // Maximum number of errors to log - uint32_t errors[MAX_NUM_ERRORS] = {0}; - uint8_t numErrors = 0; - bool errorOverwrite = false; - - // Common methods - virtual int throwError(uint32_t error) { - // Simple error handling implementation - if (numErrors < MAX_NUM_ERRORS) { - errors[numErrors++] = error; - } else { - errors[MAX_NUM_ERRORS - 1] = error; - errorOverwrite = true; - } - return 0; - } - - // Virtual methods that would be implemented by derived classes - virtual String begin(bool &criticalFault, bool &fault) { return ""; } - virtual String getData(void) { return ""; } - virtual String getMetadata(void) { return ""; } - virtual String getErrors(void) { return ""; } - virtual String selfDiagnostic(uint8_t diagnosticLevel) { return ""; } -}; \ No newline at end of file +class MockSensor : public Sensor { + public: + MOCK_METHOD(String, begin, (time_t time, bool &criticalFault, bool &fault)); + MOCK_METHOD(String, getErrors, (), (override)); + MOCK_METHOD(uint8_t, totalErrors, (), (override)); + MOCK_METHOD(String, getData, (time_t time), (override)); + MOCK_METHOD(String, getMetadata, (), (override)); + MOCK_METHOD(String, selfDiagnostic, (uint8_t diagnosticLevel, time_t time), (override)); + MOCK_METHOD(uint8_t, getTalonPort, ()); + MOCK_METHOD(uint8_t, getSensorPort, ()); + MOCK_METHOD(void, setTalonPort, (uint8_t port)); + MOCK_METHOD(void, setSensorPort, (uint8_t port)); + MOCK_METHOD(bool, isPresent, (), (override)); + MOCK_METHOD(int, sleep, (), (override)); + MOCK_METHOD(int, wake, (), (override)); + }; \ No newline at end of file diff --git a/test/mocks/MockVEML3328.cpp b/test/mocks/MockVEML3328.cpp deleted file mode 100644 index 38909fe..0000000 --- a/test/mocks/MockVEML3328.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "MockVEML3328.h" - -// Define fake functions -DEFINE_FAKE_VALUE_FUNC(bool, VEML3328_begin); -DEFINE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readRed); -DEFINE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readGreen); -DEFINE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readBlue); -DEFINE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readClear); -DEFINE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readIR); - -// Set default behavior -void setup_mock_VEML3328_defaults() { - VEML3328_begin_fake.return_val = true; - VEML3328_readRed_fake.return_val = 500; - VEML3328_readGreen_fake.return_val = 600; - VEML3328_readBlue_fake.return_val = 450; - VEML3328_readClear_fake.return_val = 800; - VEML3328_readIR_fake.return_val = 300; -} diff --git a/test/mocks/MockVEML3328.h b/test/mocks/MockVEML3328.h index 619355e..9143545 100644 --- a/test/mocks/MockVEML3328.h +++ b/test/mocks/MockVEML3328.h @@ -1,23 +1,11 @@ -#pragma once +#include +#include "VEML3328.h" -#include "fff.h" -#include - -// Declare fake functions for VEML3328 (light sensor) -DECLARE_FAKE_VALUE_FUNC(bool, VEML3328_begin); -DECLARE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readRed); -DECLARE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readGreen); -DECLARE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readBlue); -DECLARE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readClear); -DECLARE_FAKE_VALUE_FUNC(uint16_t, VEML3328_readIR); - -// Mock class for VEML3328 -class VEML3328 { -public: - bool begin() { return VEML3328_begin(); } - uint16_t readRed() { return VEML3328_readRed(); } - uint16_t readGreen() { return VEML3328_readGreen(); } - uint16_t readBlue() { return VEML3328_readBlue(); } - uint16_t readClear() { return VEML3328_readClear(); } - uint16_t readIR() { return VEML3328_readIR(); } -}; +class MockVEML3328 : public VEML3328 { + public: + MOCK_METHOD(int, begin, ()); + MOCK_METHOD(int, autoRange, ()); + MOCK_METHOD(float, GetValue, (Channel Param, bool &State)); + MOCK_METHOD(float, GetValue, (Channel Param)); + MOCK_METHOD(float, GetLux, ()); +}; \ No newline at end of file diff --git a/test/mocks/MockWire.cpp b/test/mocks/MockWire.cpp deleted file mode 100644 index 2ef0b89..0000000 --- a/test/mocks/MockWire.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// test/mocks/wire_mock.cpp -#include "MockWireDeclare.h" - -// Define fake functions -DEFINE_FAKE_VOID_FUNC(Wire_begin); -DEFINE_FAKE_VOID_FUNC(Wire_setClock, unsigned long); -DEFINE_FAKE_VOID_FUNC(Wire_beginTransmission, uint8_t); -DEFINE_FAKE_VALUE_FUNC(uint8_t, Wire_endTransmission, bool); -DEFINE_FAKE_VALUE_FUNC(uint8_t, Wire_requestFrom_3args, uint8_t, uint8_t, bool); -DEFINE_FAKE_VALUE_FUNC(uint8_t, Wire_requestFrom_2args, uint8_t, uint8_t); -DEFINE_FAKE_VALUE_FUNC(int, Wire_available); -DEFINE_FAKE_VALUE_FUNC(int, Wire_read); -DEFINE_FAKE_VOID_FUNC(Wire_write_uint8, uint8_t); -DEFINE_FAKE_VOID_FUNC(Wire_write_buffer, const uint8_t*, size_t); - -// Define the global Wire instance -MockWire Wire; \ No newline at end of file diff --git a/test/mocks/MockWireDeclare.h b/test/mocks/MockWireDeclare.h deleted file mode 100644 index 334300c..0000000 --- a/test/mocks/MockWireDeclare.h +++ /dev/null @@ -1,41 +0,0 @@ -// test/mocks/mock_wire_defs.h -#ifndef WIRE_MOCK_DEFS_H -#define WIRE_MOCK_DEFS_H - -#include -#include "fff.h" - -// Declare fake functions -DECLARE_FAKE_VOID_FUNC(Wire_begin); -DECLARE_FAKE_VOID_FUNC(Wire_setClock, unsigned long); -DECLARE_FAKE_VOID_FUNC(Wire_beginTransmission, uint8_t); -DECLARE_FAKE_VALUE_FUNC(uint8_t, Wire_endTransmission, bool); -DECLARE_FAKE_VALUE_FUNC(uint8_t, Wire_requestFrom_3args, uint8_t, uint8_t, bool); -DECLARE_FAKE_VALUE_FUNC(uint8_t, Wire_requestFrom_2args, uint8_t, uint8_t); -DECLARE_FAKE_VALUE_FUNC(int, Wire_available); -DECLARE_FAKE_VALUE_FUNC(int, Wire_read); -DECLARE_FAKE_VOID_FUNC(Wire_write_uint8, uint8_t); -DECLARE_FAKE_VOID_FUNC(Wire_write_buffer, const uint8_t*, size_t); - -// Mock Wire class declaration -class MockWire { -public: - void begin() { Wire_begin(); } - void setClock(unsigned long clock) { Wire_setClock(clock); } - void beginTransmission(uint8_t address) { Wire_beginTransmission(address); } - uint8_t endTransmission(bool sendStop = true) { return Wire_endTransmission(sendStop); } - uint8_t requestFrom(uint8_t address, uint8_t quantity, bool sendStop) { - return Wire_requestFrom_3args(address, quantity, sendStop); - } - uint8_t requestFrom(uint8_t address, uint8_t quantity) { - return Wire_requestFrom_2args(address, quantity); - } - int available() { return Wire_available(); } - int read() { return Wire_read(); } - void write(uint8_t data) { Wire_write_uint8(data); } - void write(const uint8_t* data, size_t quantity) { Wire_write_buffer(data, quantity); } -}; - -extern MockWire Wire; - -#endif // WIRE_MOCK_DEFS_H \ No newline at end of file diff --git a/test/mocks/Particle.cpp b/test/mocks/Particle.cpp new file mode 100644 index 0000000..5348c6f --- /dev/null +++ b/test/mocks/Particle.cpp @@ -0,0 +1,30 @@ +#include "Particle.h" + +// Define global instances +SystemClass System; +TwoWire Wire; +MockSerialPort Serial; +MockSerialPort Serial1; + +// Basic implementations of Arduino functions +unsigned long millis() { + static unsigned long fake_millis = 0; + fake_millis += 100; // Increment by 100ms each call + return fake_millis; +} + +void delay(unsigned long ms) { + // No-op in test +} + +void pinMode(uint16_t pin, uint8_t mode) { + // No-op in test +} + +void digitalWrite(uint16_t pin, uint8_t value) { + // No-op in test +} + +int digitalRead(uint16_t pin) { + return 0; // Default to LOW +} \ No newline at end of file diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h index 31ed9a3..0dda129 100644 --- a/test/mocks/Particle.h +++ b/test/mocks/Particle.h @@ -1,45 +1,236 @@ -#pragma once +#ifndef PARTICLE_H_MOCK +#define PARTICLE_H_MOCK -#include "MockParticle.h" #include #include +#include +#include -// Mock Particle types and functions to satisfy dependencies -typedef uint8_t system_event_t; +// Must come before any GoogleTest stuff to avoid conflicts +#ifndef SERIAL_8N1 +#define SERIAL_8N1 0x00 +#endif -// System event handlers -constexpr system_event_t TIME_CHANGED = 1; -constexpr system_event_t LOW_MEMORY = 2; +// Undefine any macros that might cause conflicts with library headers +#ifdef A +#undef A +#endif -// Mock Particle functions -inline void System_on(system_event_t event, void (*handler)(system_event_t, int)) { - // Just do nothing in mock -} +// Define basic Particle types +typedef int system_event_t; -inline int Time_now() { - return time(NULL); -} +// Define common Particle event types +const int time_changed = 1; +const int out_of_memory = 2; -inline bool Particle_connected() { - return true; -} +// String class compatible with Particle's +class String { +public: + String() : value_("") {} + String(const char* str) : value_(str ? str : "") {} + String(const std::string& str) : value_(str) {} + String(int val) : value_(std::to_string(val)) {} + String(long val) : value_(std::to_string(val)) {} + String(float val, int decimalPlaces = 2) : value_(std::to_string(val)) {} + + bool equals(const String& other) const { + return value_ == other.value_; + } -inline bool Particle_syncTime() { - return true; -} + String substring(size_t beginIndex) const { + return value_.substr(beginIndex); + } + + String substring(size_t beginIndex, size_t endIndex) const { + return value_.substr(beginIndex, endIndex - beginIndex); + } -// Define the actual Particle API namespace/functions -namespace Particle { - inline bool connected() { return Particle_connected(); } - inline bool syncTime() { return Particle_syncTime(); } + size_t length() const { + return value_.length(); + } - namespace System { - inline void on(system_event_t event, void (*handler)(system_event_t, int)) { - System_on(event, handler); + const char* c_str() const { + return value_.c_str(); + } + + bool operator==(const String& other) const { + return value_ == other.value_; + } + + bool operator==(const char* other) const { + return value_ == other; + } + + String operator+(const String& other) const { + return String(value_ + other.value_); + } + + String operator+(const char* other) const { + return String(value_ + other); + } + +private: + std::string value_; +}; + +// For gtest to print Strings in a readable way +inline std::ostream& operator<<(std::ostream& os, const String& str) { + return os << str.c_str(); +} + +// Mock minimal Arduino functions that Particle depends on +typedef uint8_t byte; +typedef bool boolean; + +#define LOW 0 +#define HIGH 1 +#define INPUT 0 +#define OUTPUT 1 +#define INPUT_PULLUP 2 +#define SERIAL_8N1 0x00 + +// Stream base class +class Stream { +public: + virtual int available() = 0; + virtual int read() = 0; + virtual int peek() = 0; + virtual void flush() = 0; + virtual size_t write(uint8_t) = 0; + virtual ~Stream() {} +}; + +// Print base class +class Print { +public: + virtual size_t write(uint8_t) = 0; + virtual size_t write(const uint8_t *buffer, size_t size) { + size_t n = 0; + for (size_t i = 0; i < size; i++) { + n += write(buffer[i]); } + return n; } + virtual ~Print() {} +}; + +// Mock Serial objects with gmock that inherit from Stream for compatibility +class MockSerialPort : public Stream, public Print { +public: + // For MockSerialPort + MOCK_METHOD(void, begin, (unsigned long, uint8_t)); + MOCK_METHOD(void, print, (const char*)); + MOCK_METHOD(void, print, (int)); + MOCK_METHOD(void, print, (const String&)); + MOCK_METHOD(void, println, (const char*)); + MOCK_METHOD(void, println, (int)); + MOCK_METHOD(void, println, (const String&)); + MOCK_METHOD(void, println, ()); + + // From Stream + MOCK_METHOD(int, available, (), (override)); + MOCK_METHOD(int, read, (), (override)); + MOCK_METHOD(int, peek, (), (override)); + MOCK_METHOD(void, flush, (), (override)); - namespace Time { - inline int now() { return Time_now(); } + // From Print + MOCK_METHOD(size_t, write, (uint8_t), (override)); +}; + +extern MockSerialPort Serial; +extern MockSerialPort Serial1; + +// Wire class for I2C +class TwoWire { +public: + bool isEnabled() { return true; } + void begin() {} + void setClock(uint32_t clock) {} + void beginTransmission(uint8_t address) {} + uint8_t endTransmission(bool stopBit = true) { return 0; } + uint8_t requestFrom(uint8_t address, uint8_t quantity, bool stopBit = true) { return 0; } + int available() { return 0; } + int read() { return 0; } + size_t write(uint8_t data) { return 1; } + size_t write(const uint8_t* data, size_t len) { return len; } +}; + +extern TwoWire Wire; + +// EEPROM +namespace EEPROM { + template + void get(int addr, T& val) { + val = T(); } -} \ No newline at end of file +} + +// System class that appears in many Particle programs +class SystemClass { +public: + void on(int event, void (*handler)(system_event_t, int)) {} + uint32_t resetReason() { return 0; } + bool connected() { return true; } + void reset() {} + const char* version() { return "1.0.0"; } + String deviceID() { return "TEST_DEVICE_ID"; } +}; + +extern SystemClass System; + +// Define pin names +#define D0 0 +#define D1 1 +#define D2 2 +#define D3 3 +#define D4 4 +#define D5 5 +#define D6 6 +#define D7 7 +#define D8 8 +#define D9 9 +#define D10 10 +#define D11 11 +#define D12 12 +#define D13 13 +#define D14 14 +#define D15 15 +#define D16 16 +#define D17 17 +#define D18 18 +#define D19 19 +#define D20 20 +#define D21 21 +#define D22 22 +#define D23 23 +#define A0 24 +#define A1 25 +#define A2 26 +#define A3 27 +#define A4 28 +#define A5 29 +#define A6 30 +#define A7 31 + +// Platform IDs +#define PLATFORM_BSOM 23 +#define PLATFORM_B5SOM 32 +#define PLATFORM_ID PLATFORM_BSOM + +// Arduino-like functions +unsigned long millis(); +void delay(unsigned long ms); +void pinMode(uint16_t pin, uint8_t mode); +void digitalWrite(uint16_t pin, uint8_t value); +int digitalRead(uint16_t pin); + +// Define min/max functions if not already defined +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef max +#define max(a,b) ((a)>(b)?(a):(b)) +#endif + +#endif // PARTICLE_H_MOCK \ No newline at end of file diff --git a/test/mocks/SPI.h b/test/mocks/SPI.h index 1f4fc88..7632048 100644 --- a/test/mocks/SPI.h +++ b/test/mocks/SPI.h @@ -1,30 +1,9 @@ -#pragma once +#ifndef SPI_H_MOCK +#define SPI_H_MOCK -#include -#include // For size_t +#include "Arduino.h" // For SPIClass definition -// SPI settings -#define SPI_MODE0 0x00 -#define SPI_MODE1 0x01 -#define SPI_MODE2 0x02 -#define SPI_MODE3 0x03 +// This is just a placeholder that includes the SPIClass implementation from Arduino.h +// Libraries that include SPI.h will get the implementation from Arduino.h -#define SPI_CLOCK_DIV2 0x00 -#define SPI_CLOCK_DIV4 0x01 -#define SPI_CLOCK_DIV8 0x02 -#define SPI_CLOCK_DIV16 0x03 -#define SPI_CLOCK_DIV32 0x04 -#define SPI_CLOCK_DIV64 0x05 -#define SPI_CLOCK_DIV128 0x06 - -// Simple mock for the SPI class -class SPIClass { -public: - void begin() {} - void beginTransaction(uint32_t settings) {} - void endTransaction() {} - uint8_t transfer(uint8_t data) { return 0; } - void transfer(void *buf, size_t count) {} -}; - -extern SPIClass SPI; \ No newline at end of file +#endif // SPI_H_MOCK \ No newline at end of file diff --git a/test/mocks/TestKestrel.h b/test/mocks/TestKestrel.h deleted file mode 100644 index 8898d5f..0000000 --- a/test/mocks/TestKestrel.h +++ /dev/null @@ -1,336 +0,0 @@ -#pragma once - -// Modified Kestrel.h intended for testing only -// This provides a controlled environment without class redefinitions - -// Include all mocks we need -#include "MockArduino.h" -#include "MockSensor.h" -#include "MockPCAL9535A.h" -#include "MockPCA9634.h" -#include "MockMCP79412.h" -#include "MockGNSS.h" -#include "MockPAC1934.h" -#include "MockVEML3328.h" -#include "MockSHT4x.h" -#include "MockMXC6655.h" -#include "MockBMA456.h" -#include "MockWireDeclare.h" - -// Define constants and namespaces from Kestrel.h that we need for testing -namespace Pins { - constexpr uint16_t WD_HOLD = D2; - constexpr uint16_t SD_CS = D8; - constexpr uint16_t Clock_INT = D22; - constexpr uint16_t TALON1_GPIOA = A3; - constexpr uint16_t TALON1_GPIOB = D7; - constexpr uint16_t TALON2_GPIOA = A2; - constexpr uint16_t TALON2_GPIOB = D6; - constexpr uint16_t TALON3_GPIOA = A1; - constexpr uint16_t TALON3_GPIOB = D5; - constexpr uint16_t I2C_GLOBAL_EN = D23; - constexpr uint16_t I2C_OB_EN = A6; -} - -namespace PinsOB { - constexpr uint16_t I2C_EXT_EN = 10; - constexpr uint16_t SD_CD = 8; - constexpr uint16_t SD_EN = 12; - constexpr uint16_t AUX_EN = 15; - constexpr uint16_t CE = 11; - constexpr uint16_t LED_EN = 13; - constexpr uint16_t CSA_EN = 14; - constexpr uint16_t GPS_INT = 7; -} - -namespace PinsTalon { - constexpr uint8_t SEL[4] = {0, 4, 8, 12}; - constexpr uint8_t I2C_EN[4] = {1, 5, 9, 13}; - constexpr uint8_t EN[4] = {3, 7, 11, 15}; - constexpr uint8_t FAULT[4] = {2, 6, 10, 14}; -} - -namespace TimeSource { - constexpr uint8_t INCREMENT = 4; - constexpr uint8_t RTC = 3; - constexpr uint8_t GPS_RTC = 2; - constexpr uint8_t CELLULAR = 1; - constexpr uint8_t GPS = 0; - constexpr uint8_t NONE = 5; -} - -namespace IndicatorLight { - constexpr uint8_t SENSORS = 1; - constexpr uint8_t GPS = 2; - constexpr uint8_t CELL = 3; - constexpr uint8_t STAT = 4; - constexpr uint8_t ALL = 5; -} - -namespace IndicatorMode { - constexpr uint8_t NONE = 0; - constexpr uint8_t PASS = 1; - constexpr uint8_t WAITING = 2; - constexpr uint8_t ERROR = 3; - constexpr uint8_t ERROR_CRITICAL = 4; - constexpr uint8_t PREPASS = 5; - constexpr uint8_t INIT = 6; - constexpr uint8_t IDLE = 7; - constexpr uint8_t COMMAND = 8; -} - -namespace AccelType { - constexpr uint8_t MXC6655 = 0; - constexpr uint8_t BMA456 = 1; -} - -namespace HardwareVersion { - constexpr uint8_t PRE_1v9 = 0; - constexpr uint8_t MODEL_1v9 = 1; -} - -struct dateTimeStruct { - int year; - int month; - int day; - int hour; - int minute; - int second; - uint8_t source = TimeSource::NONE; -}; - -// Simplified version of Kestrel for testing -// We'll manually implement the methods we want to test -class Kestrel : public Sensor { -public: - constexpr static int MAX_NUM_ERRORS = 10; - const std::string FIRMWARE_VERSION = "1.7.5"; - static constexpr uint8_t numTalonPorts = 5; - static constexpr int MAX_MESSAGE_LENGTH = 1024; - - dateTimeStruct currentDateTime = {2049, 6, 16, 3, 27, 31, TimeSource::NONE}; - uint8_t timeFix = 0; - SFE_UBLOX_GNSS gps; - - // Constructor - Kestrel(bool useSensors = false) : reportSensors(useSensors) { - // Initialize member objects - } - - // Public methods we want to test - std::string begin(time_t time, bool &criticalFault, bool &fault) { - // Call internal device initializations - ioOB.begin(); - ioTalon.begin(); - rtc.begin(); - led.begin(); - - if (reportSensors) { - als.begin(); - csaAlpha.begin(); - csaBeta.begin(); - atmos.begin(); - accel.begin(); - gps.begin(); - } - - // Return success message - return "Kestrel " + FIRMWARE_VERSION + " initialized successfully"; - } - - bool enablePower(uint8_t port, bool state = true) { - if (port >= numTalonPorts) return false; - - // Call IO expander to enable/disable power - return ioTalon.digitalWrite(PinsTalon::EN[port], state); - } - - bool enableData(uint8_t port, bool state = true) { - if (port >= numTalonPorts) return false; - - // Call IO expander to enable/disable data - return ioTalon.digitalWrite(PinsTalon::I2C_EN[port], state); - } - - time_t getTime() { - return rtc.getTime(); - } - - bool enableI2C_OB(bool state = true) { - return ioOB.digitalWrite(PinsOB::I2C_EXT_EN, state); - } - - bool enableI2C_Global(bool state = true) { - // In our mock environment, digitalWrite returns void - // But in the real implementation it would return a status - digitalWrite(Pins::I2C_GLOBAL_EN, state); - return true; // Assume success - } - - bool enableI2C_External(bool state = true) { - return ioOB.digitalWrite(PinsOB::I2C_EXT_EN, state); - } - - bool setIndicatorState(uint8_t ledBank, uint8_t mode) { - int brightness = 0; - - // Convert mode to brightness - switch (mode) { - case IndicatorMode::PASS: - brightness = 75; - break; - case IndicatorMode::ERROR: - brightness = 100; - break; - default: - brightness = 0; - break; - } - - return led.setBrightness(ledBank, brightness); - } - - // Other methods as needed for testing... - -private: - PCAL9535A ioOB; - PCAL9535A ioTalon; - MCP79412 rtc; - PAC1934 csaAlpha; - PAC1934 csaBeta; - VEML3328 als; - Adafruit_SHT4x atmos; - MXC6655 accel; - PCA9634 led; - bool reportSensors = false; -}; - -// Helper functions for testing Kestrel -namespace KestrelTest { - // Setup a Kestrel instance with all mock dependencies properly configured - inline Kestrel* createKestrel() { - // Configure all mock return values for a successful initialization - PCAL9535A_begin_fake.return_val = true; - MCP79412_begin_fake.return_val = true; - MCP79412_getTime_fake.return_val = 1616161616; - PCA9634_begin_fake.return_val = true; - VEML3328_begin_fake.return_val = true; - PAC1934_begin_fake.return_val = true; - SHT4x_begin_fake.return_val = true; - MXC6655_begin_fake.return_val = true; - BMA456_begin_fake.return_val = true; - GNSS_begin_fake.return_val = true; - - // Create a Kestrel instance - return new Kestrel(true); // true = use sensors - } - - // Reset all mocks for a clean slate - inline void resetAllMocks() { - // Wire mocks - RESET_FAKE(Wire_begin); - RESET_FAKE(Wire_setClock); - RESET_FAKE(Wire_beginTransmission); - RESET_FAKE(Wire_endTransmission); - RESET_FAKE(Wire_requestFrom_3args); - RESET_FAKE(Wire_requestFrom_2args); - RESET_FAKE(Wire_available); - RESET_FAKE(Wire_read); - RESET_FAKE(Wire_write_uint8); - RESET_FAKE(Wire_write_buffer); - - // Arduino mocks - RESET_FAKE(pinMode); - RESET_FAKE(digitalWrite); - RESET_FAKE(digitalRead); - RESET_FAKE(millis); - RESET_FAKE(delay); - - // Hardware component mocks - RESET_FAKE(PCAL9535A_begin); - RESET_FAKE(PCAL9535A_pinMode); - RESET_FAKE(PCAL9535A_digitalWrite); - RESET_FAKE(PCAL9535A_digitalRead); - - RESET_FAKE(MCP79412_begin); - RESET_FAKE(MCP79412_getTime); - RESET_FAKE(MCP79412_setTime); - - RESET_FAKE(PCA9634_begin); - RESET_FAKE(PCA9634_setBrightness); - RESET_FAKE(PCA9634_setLEDOutputMode); - - RESET_FAKE(VEML3328_begin); - RESET_FAKE(VEML3328_readRed); - RESET_FAKE(VEML3328_readGreen); - RESET_FAKE(VEML3328_readBlue); - - RESET_FAKE(PAC1934_begin); - RESET_FAKE(PAC1934_readVoltage); - RESET_FAKE(PAC1934_readCurrent); - - RESET_FAKE(SHT4x_begin); - RESET_FAKE(SHT4x_readTemperature); - RESET_FAKE(SHT4x_readHumidity); - - RESET_FAKE(MXC6655_begin); - RESET_FAKE(MXC6655_readAcceleration); - - RESET_FAKE(BMA456_begin); - RESET_FAKE(BMA456_readAcceleration); - RESET_FAKE(BMA456_getStepCount); - - RESET_FAKE(GNSS_begin); - RESET_FAKE(GNSS_getLatitude); - RESET_FAKE(GNSS_getLongitude); - RESET_FAKE(GNSS_getAltitude); - } - - // Set default successful return values for hardware operations - inline void setDefaultMockBehavior() { - // Configure default successful behaviors - Wire_endTransmission_fake.return_val = 0; // Success - Wire_available_fake.return_val = 1; // Data available - - PCAL9535A_begin_fake.return_val = true; - PCAL9535A_digitalWrite_fake.return_val = true; - PCAL9535A_digitalRead_fake.return_val = 0; // Default to LOW - - MCP79412_begin_fake.return_val = true; - MCP79412_getTime_fake.return_val = 1616161616; - MCP79412_setTime_fake.return_val = true; - - PCA9634_begin_fake.return_val = true; - PCA9634_setBrightness_fake.return_val = true; - PCA9634_setLEDOutputMode_fake.return_val = true; - - VEML3328_begin_fake.return_val = true; - VEML3328_readRed_fake.return_val = 500; - VEML3328_readGreen_fake.return_val = 600; - VEML3328_readBlue_fake.return_val = 400; - - PAC1934_begin_fake.return_val = true; - PAC1934_readVoltage_fake.return_val = 3.3f; - PAC1934_readCurrent_fake.return_val = 0.1f; - - SHT4x_begin_fake.return_val = true; - - MXC6655_begin_fake.return_val = true; - BMA456_begin_fake.return_val = true; - - GNSS_begin_fake.return_val = true; - GNSS_isConnected_fake.return_val = true; - GNSS_getLatitude_fake.return_val = 449673925; - GNSS_getLongitude_fake.return_val = -932838386; - GNSS_getAltitude_fake.return_val = 25000; - GNSS_getSIV_fake.return_val = 8; - GNSS_getYear_fake.return_val = 2023; - GNSS_getMonth_fake.return_val = 5; - GNSS_getDay_fake.return_val = 15; - GNSS_getHour_fake.return_val = 10; - GNSS_getMinute_fake.return_val = 30; - GNSS_getSecond_fake.return_val = 0; - - millis_fake.return_val = 1000; // Start at 1 second - } -} \ No newline at end of file diff --git a/test/mocks/WProgram.h b/test/mocks/WProgram.h index f638a01..c9535cd 100644 --- a/test/mocks/WProgram.h +++ b/test/mocks/WProgram.h @@ -1,4 +1,7 @@ -#pragma once +#ifndef WPROGRAM_H_MOCK +#define WPROGRAM_H_MOCK -// This is for older Arduino code that used WProgram.h instead of Arduino.h -#include "Arduino.h" \ No newline at end of file +// This is for older Arduino libraries that use WProgram.h instead of Arduino.h +#include "Arduino.h" + +#endif // WPROGRAM_H_MOCK \ No newline at end of file diff --git a/test/mocks/Wire.h b/test/mocks/Wire.h index 993e1f4..e8abb9c 100644 --- a/test/mocks/Wire.h +++ b/test/mocks/Wire.h @@ -1,5 +1,9 @@ -#pragma once +#ifndef WIRE_H_MOCK +#define WIRE_H_MOCK -#include "MockWireDeclare.h" +#include "Arduino.h" // For TwoWire definition -// This is just a header to include our Wire mock \ No newline at end of file +// This is just a placeholder that includes the TwoWire class from Arduino.h +// Libraries that include Wire.h will get the TwoWire implementation from Arduino.h + +#endif // WIRE_H_MOCK \ No newline at end of file diff --git a/test/unit/Driver_-_Kestrel/Driver_-_KestrelFunctionTests.cpp b/test/unit/Driver_-_Kestrel/Driver_-_KestrelFunctionTests.cpp deleted file mode 100644 index 22d31c0..0000000 --- a/test/unit/Driver_-_Kestrel/Driver_-_KestrelFunctionTests.cpp +++ /dev/null @@ -1,151 +0,0 @@ -// FlightControl_Demo/test/unit/Driver-_-Kestrel/KestrelFunctionTests.cpp -// Tests for Kestrel's public functions - -#include "gtest/gtest.h" -#include "fff.h" -#include "TestKestrel.h" - -// Test fixture for Kestrel public function tests -class KestrelPublicTest : public ::testing::Test { -protected: - Kestrel* kestrel; - - void SetUp() override { - // Reset all mocks before each test - KestrelTest::resetAllMocks(); - - // Set default mock behaviors - KestrelTest::setDefaultMockBehavior(); - - // Create a Kestrel instance with properly configured mocks - kestrel = KestrelTest::createKestrel(); - } - - void TearDown() override { - // Clean up - delete kestrel; - } -}; - -// Test Kestrel initialization -TEST_F(KestrelPublicTest, BeginTest) { - // Set up additional mock behaviors if needed - bool criticalFault = false; - bool fault = false; - - // Call Kestrel's begin method - std::string result = kestrel->begin(1616161616, criticalFault, fault); - - // Verify begin was called on all dependent hardware components - EXPECT_EQ(PCAL9535A_begin_fake.call_count, 2); // 2 I/O expanders - EXPECT_EQ(MCP79412_begin_fake.call_count, 1); // RTC - EXPECT_EQ(PCA9634_begin_fake.call_count, 1); // LED controller - - // Verify no critical faults occurred - EXPECT_FALSE(criticalFault); - EXPECT_FALSE(fault); - - // Verify result contains expected success message - EXPECT_TRUE(result.find("Kestrel") != std::string::npos); -} - -// Test port power control -TEST_F(KestrelPublicTest, EnablePowerTest) { - // Set up mock behavior for I/O expanders - PCAL9535A_digitalWrite_fake.return_val = true; - - // Test enabling power on port 1 - bool result = kestrel->enablePower(1, true); - - // Verify the result and calls - EXPECT_TRUE(result); - EXPECT_EQ(PCAL9535A_digitalWrite_fake.call_count, 1); - - // Test disabling power - RESET_FAKE(PCAL9535A_digitalWrite); - PCAL9535A_digitalWrite_fake.return_val = true; - - result = kestrel->enablePower(1, false); - - EXPECT_TRUE(result); - EXPECT_EQ(PCAL9535A_digitalWrite_fake.call_count, 1); - - // Test with invalid port (should return false) - result = kestrel->enablePower(10, true); // Port 10 is out of range - EXPECT_FALSE(result); -} - -// Test data port control -TEST_F(KestrelPublicTest, EnableDataTest) { - // Set up mock behavior for I/O expanders - PCAL9535A_digitalWrite_fake.return_val = true; - - // Test enabling data on port 1 - bool result = kestrel->enableData(1, true); - - // Verify the result and calls - EXPECT_TRUE(result); - EXPECT_EQ(PCAL9535A_digitalWrite_fake.call_count, 1); - - // Test with invalid port (should return false) - result = kestrel->enableData(10, true); // Port 10 is out of range - EXPECT_FALSE(result); -} - -// Test time retrieval -TEST_F(KestrelPublicTest, GetTimeTest) { - // Set up mock behavior for time retrieval - MCP79412_getTime_fake.return_val = 1616161616; - - // Call the method under test - time_t time = kestrel->getTime(); - - // Verify the result matches what we expect - EXPECT_EQ(time, 1616161616); - EXPECT_EQ(MCP79412_getTime_fake.call_count, 1); -} - -// Test I2C bus control -TEST_F(KestrelPublicTest, I2CControlTest) { - // Set up mock behavior - PCAL9535A_digitalWrite_fake.return_val = true; - - // Test enabling I2C buses - EXPECT_TRUE(kestrel->enableI2C_Global(true)); - EXPECT_TRUE(kestrel->enableI2C_OB(true)); - EXPECT_TRUE(kestrel->enableI2C_External(true)); - - // Verify the calls occurred - // Note: Our simplified test implementation uses digitalWrite for Global, not PCAL9535A - EXPECT_EQ(digitalWrite_fake.call_count, 1); - EXPECT_EQ(PCAL9535A_digitalWrite_fake.call_count, 2); - - // Reset the fakes for the next series of tests - RESET_FAKE(PCAL9535A_digitalWrite); - RESET_FAKE(digitalWrite); - PCAL9535A_digitalWrite_fake.return_val = true; - - // Test disabling I2C buses - EXPECT_TRUE(kestrel->enableI2C_Global(false)); - EXPECT_TRUE(kestrel->enableI2C_OB(false)); - EXPECT_TRUE(kestrel->enableI2C_External(false)); - - // Verify the calls occurred - EXPECT_EQ(digitalWrite_fake.call_count, 1); - EXPECT_EQ(PCAL9535A_digitalWrite_fake.call_count, 2); -} - -// Test indicator light control -TEST_F(KestrelPublicTest, IndicatorLightTest) { - // Set up mock behavior - PCA9634_setBrightness_fake.return_val = true; - - // Test setting lights to different states - EXPECT_TRUE(kestrel->setIndicatorState(IndicatorLight::SENSORS, IndicatorMode::PASS)); - EXPECT_TRUE(kestrel->setIndicatorState(IndicatorLight::GPS, IndicatorMode::ERROR)); - - // Verify the right calls were made - EXPECT_EQ(PCA9634_setBrightness_fake.call_count, 2); -} - -// Add more tests for other public Kestrel functions as needed \ No newline at end of file diff --git a/test/unit/Driver_-_Kestrel/Driver_-_KestrelSetupTests.cpp b/test/unit/Driver_-_Kestrel/Driver_-_KestrelSetupTests.cpp deleted file mode 100644 index 87430fa..0000000 --- a/test/unit/Driver_-_Kestrel/Driver_-_KestrelSetupTests.cpp +++ /dev/null @@ -1,317 +0,0 @@ -// FlightControl_Demo/test/unit/Driver-_-Kestrel/Driver-_-KestrelTest.cpp -#include "gtest/gtest.h" -#include "fff.h" -#include "MockWireDeclare.h" -#include "MockArduino.h" -#include "MockPCAL9535A.h" -#include "MockMCP79412.h" -#include "MockPCA9634.h" -#include "MockVEML3328.h" -#include "MockPAC1934.h" -#include "MockSHT4x.h" -#include "MockMXC6655.h" -#include "MockBMA456.h" -#include "MockGNSS.h" -#include "MockSensor.h" - -// Test fixture for basic mock testing -class KestrelBasicTest : public ::testing::Test { -protected: - void SetUp() override { - // Reset all Wire fakes before each test - RESET_FAKE(Wire_begin); - RESET_FAKE(Wire_setClock); - RESET_FAKE(Wire_beginTransmission); - RESET_FAKE(Wire_endTransmission); - RESET_FAKE(Wire_requestFrom_3args); - RESET_FAKE(Wire_requestFrom_2args); - RESET_FAKE(Wire_available); - RESET_FAKE(Wire_read); - RESET_FAKE(Wire_write_uint8); - RESET_FAKE(Wire_write_buffer); - - // Reset Arduino fakes - RESET_FAKE(pinMode); - RESET_FAKE(digitalWrite); - RESET_FAKE(digitalRead); - RESET_FAKE(millis); - RESET_FAKE(delay); - - // Reset PCAL9535A fakes - RESET_FAKE(PCAL9535A_begin); - RESET_FAKE(PCAL9535A_pinMode); - RESET_FAKE(PCAL9535A_digitalWrite); - RESET_FAKE(PCAL9535A_digitalRead); - - // Reset MCP79412 fakes - RESET_FAKE(MCP79412_begin); - RESET_FAKE(MCP79412_getTime); - RESET_FAKE(MCP79412_setTime); - - // Reset PCA9634 fakes - RESET_FAKE(PCA9634_begin); - RESET_FAKE(PCA9634_setBrightness); - RESET_FAKE(PCA9634_setLEDOutputMode); - - // Reset VEML3328 fakes - RESET_FAKE(VEML3328_begin); - RESET_FAKE(VEML3328_readRed); - RESET_FAKE(VEML3328_readGreen); - RESET_FAKE(VEML3328_readBlue); - - // Reset PAC1934 fakes - RESET_FAKE(PAC1934_begin); - RESET_FAKE(PAC1934_readVoltage); - RESET_FAKE(PAC1934_readCurrent); - - // Reset SHT4x fakes - RESET_FAKE(SHT4x_begin); - RESET_FAKE(SHT4x_readTemperature); - RESET_FAKE(SHT4x_readHumidity); - - // Reset MXC6655 fakes - RESET_FAKE(MXC6655_begin); - RESET_FAKE(MXC6655_readAcceleration); - - // Reset BMA456 fakes - RESET_FAKE(BMA456_begin); - RESET_FAKE(BMA456_readAcceleration); - RESET_FAKE(BMA456_getStepCount); - - // Reset GNSS fakes - RESET_FAKE(GNSS_begin); - RESET_FAKE(GNSS_getLatitude); - RESET_FAKE(GNSS_getLongitude); - RESET_FAKE(GNSS_getAltitude); - - // Set default mock behavior - PCAL9535A_begin_fake.return_val = true; - MCP79412_begin_fake.return_val = true; - MCP79412_getTime_fake.return_val = 1616161616; // Fixed timestamp for testing - Wire_endTransmission_fake.return_val = 0; // Success - Wire_available_fake.return_val = 1; // Data available - - PCA9634_begin_fake.return_val = true; - VEML3328_begin_fake.return_val = true; - PAC1934_begin_fake.return_val = true; - SHT4x_begin_fake.return_val = true; - MXC6655_begin_fake.return_val = true; - BMA456_begin_fake.return_val = true; - GNSS_begin_fake.return_val = true; - - // Set up return values for common functions - VEML3328_readRed_fake.return_val = 500; - VEML3328_readGreen_fake.return_val = 600; - VEML3328_readBlue_fake.return_val = 400; - - PAC1934_readVoltage_fake.return_val = 3.3f; - PAC1934_readCurrent_fake.return_val = 0.1f; - - GNSS_getLatitude_fake.return_val = 449673925; // Minneapolis ~44.96°N - GNSS_getLongitude_fake.return_val = -932838386; // Minneapolis ~-93.28°E - GNSS_getAltitude_fake.return_val = 25000; // 250m above MSL - } -}; - -// A simple test to verify that Google Test is working -TEST_F(KestrelBasicTest, GoogleTestWorks) { - EXPECT_TRUE(true); -} - -// A test to verify that FFF is working with our mocks -TEST_F(KestrelBasicTest, FFFWorks) { - // Call a mocked function - Wire.begin(); - - // Verify it was called - EXPECT_EQ(Wire_begin_fake.call_count, 1); -} - -// A test to verify PCAL9535A mock -TEST_F(KestrelBasicTest, PCAL9535AMockWorks) { - // Create a mock object - PCAL9535A io; - - // Use the mock - bool result = io.begin(); - io.pinMode(0, OUTPUT); - io.digitalWrite(0, HIGH); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(PCAL9535A_begin_fake.call_count, 1); - EXPECT_EQ(PCAL9535A_pinMode_fake.call_count, 1); - EXPECT_EQ(PCAL9535A_digitalWrite_fake.call_count, 1); -} - -// A test to verify MCP79412 mock -TEST_F(KestrelBasicTest, MCP79412MockWorks) { - // Create a mock object - MCP79412 rtc; - - // Use the mock - bool beginResult = rtc.begin(); - time_t time = rtc.getTime(); - - // Verify calls - EXPECT_TRUE(beginResult); - EXPECT_EQ(MCP79412_begin_fake.call_count, 1); - EXPECT_EQ(MCP79412_getTime_fake.call_count, 1); - EXPECT_EQ(time, 1616161616); -} - -// Tests for the PCA9634 mock -TEST_F(KestrelBasicTest, PCA9634MockWorks) { - // Create a mock object - PCA9634 led; - - // Use the mock - bool result = led.begin(); - led.setLEDOutputMode(1, 0); - led.setBrightness(1, 100); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(PCA9634_begin_fake.call_count, 1); - EXPECT_EQ(PCA9634_setLEDOutputMode_fake.call_count, 1); - EXPECT_EQ(PCA9634_setBrightness_fake.call_count, 1); -} - -// Tests for the VEML3328 mock -TEST_F(KestrelBasicTest, VEML3328MockWorks) { - // Create a mock object - VEML3328 als; - - // Use the mock - bool result = als.begin(); - uint16_t red = als.readRed(); - uint16_t green = als.readGreen(); - uint16_t blue = als.readBlue(); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(VEML3328_begin_fake.call_count, 1); - EXPECT_EQ(VEML3328_readRed_fake.call_count, 1); - EXPECT_EQ(VEML3328_readGreen_fake.call_count, 1); - EXPECT_EQ(VEML3328_readBlue_fake.call_count, 1); - EXPECT_EQ(red, 500); - EXPECT_EQ(green, 600); - EXPECT_EQ(blue, 400); -} - -// Tests for the PAC1934 mock -TEST_F(KestrelBasicTest, PAC1934MockWorks) { - // Create a mock object - PAC1934 csa; - - // Use the mock - bool result = csa.begin(); - float voltage = csa.readVoltage(0); - float current = csa.readCurrent(0); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(PAC1934_begin_fake.call_count, 1); - EXPECT_EQ(PAC1934_readVoltage_fake.call_count, 1); - EXPECT_EQ(PAC1934_readCurrent_fake.call_count, 1); - EXPECT_FLOAT_EQ(voltage, 3.3f); - EXPECT_FLOAT_EQ(current, 0.1f); -} - -// Tests for the SHT4x mock -TEST_F(KestrelBasicTest, SHT4xMockWorks) { - // Create a mock object - Adafruit_SHT4x sht; - - // Set test values - sht.temperature = 22.5f; - sht.humidity = 45.0f; - - // Use the mock - bool result = sht.begin(); - float temp = sht.readTemperature(); - float humid = sht.readHumidity(); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(SHT4x_begin_fake.call_count, 1); - EXPECT_EQ(SHT4x_readTemperature_fake.call_count, 1); - EXPECT_EQ(SHT4x_readHumidity_fake.call_count, 1); - EXPECT_FLOAT_EQ(temp, 22.5f); - EXPECT_FLOAT_EQ(humid, 45.0f); -} - -// Tests for the MXC6655 mock -TEST_F(KestrelBasicTest, MXC6655MockWorks) { - // Create a mock object - MXC6655 accel; - - // Set test values in mock object - accel.x_acceleration = 0.1f; - accel.y_acceleration = -0.2f; - accel.z_acceleration = 0.98f; - - // Use the mock - bool result = accel.begin(); - float x, y, z; - bool readResult = accel.readAcceleration(&x, &y, &z); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(MXC6655_begin_fake.call_count, 1); - EXPECT_EQ(MXC6655_readAcceleration_fake.call_count, 1); - EXPECT_FLOAT_EQ(x, 0.1f); - EXPECT_FLOAT_EQ(y, -0.2f); - EXPECT_FLOAT_EQ(z, 0.98f); -} - -// Tests for the BMA456 mock -TEST_F(KestrelBasicTest, BMA456MockWorks) { - // Access the global mock object - BMA456 accel = accel_bma456; - - // Set test values - accel.x_acceleration = 0.05f; - accel.y_acceleration = 0.1f; - accel.z_acceleration = 0.95f; - accel.step_count = 1234; - BMA456_getStepCount_fake.return_val = 1234; // Set the return value for the fake function - - // Use the mock - bool result = accel.begin(); - float x, y, z; - bool readResult = accel.readAcceleration(&x, &y, &z); - uint32_t steps = accel.getStepCount(); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(BMA456_begin_fake.call_count, 1); - EXPECT_EQ(BMA456_readAcceleration_fake.call_count, 1); - EXPECT_EQ(BMA456_getStepCount_fake.call_count, 1); - EXPECT_FLOAT_EQ(x, 0.05f); - EXPECT_FLOAT_EQ(y, 0.1f); - EXPECT_FLOAT_EQ(z, 0.95f); - EXPECT_EQ(steps, 1234); -} - -// Tests for the GNSS mock -TEST_F(KestrelBasicTest, GNSSMockWorks) { - // Create a mock object - SFE_UBLOX_GNSS gps; - - // Use the mock - bool result = gps.begin(); - long lat = gps.getLatitude(); - long lon = gps.getLongitude(); - long alt = gps.getAltitude(); - - // Verify calls - EXPECT_TRUE(result); - EXPECT_EQ(GNSS_begin_fake.call_count, 1); - EXPECT_EQ(GNSS_getLatitude_fake.call_count, 1); - EXPECT_EQ(GNSS_getLongitude_fake.call_count, 1); - EXPECT_EQ(GNSS_getAltitude_fake.call_count, 1); - EXPECT_EQ(lat, 449673925); - EXPECT_EQ(lon, -932838386); - EXPECT_EQ(alt, 25000); -} \ No newline at end of file diff --git a/test/unit/Driver_-_Kestrel/KestrelTest.cpp b/test/unit/Driver_-_Kestrel/KestrelTest.cpp new file mode 100644 index 0000000..ee07a47 --- /dev/null +++ b/test/unit/Driver_-_Kestrel/KestrelTest.cpp @@ -0,0 +1,238 @@ +#include +#include + +// Include our mock headers first +#include "Particle.h" +#include "MockSensor.h" +//#include "PCAL9535A.h" // Uncomment if you need to use the actual PCAL9535A +//#include "MockPAC9634.h" // Uncomment if you need to use the actual PAC1934 +#include "MockMCP79412.h" +//#include "MockSparkFun_u_blox_GNSS.h" // Uncomment if you need to use the actual SFE_UBLOX_GNSS +//#include "MockPAC1934.h" // Uncomment if you need to use the actual PAC1934 +#include "MockVEML3328.h" +//#include "MockAdafruit_SHT4x.h" // Uncomment if you need to use the actual Adafruit_SHT4x +#include "MockMXC6655.h" +//#include "MockBMA456.h" // Uncomment if you need to use the actual BMA456 + + +// This must be included AFTER mocks to ensure the mocks are used +// instead of actual hardware implementations +#include "Kestrel.h" + +// Forward declarations for other mocked dependencies needed +class MockSensor; +//class MockPCAL9535A; +//class MockPAC1934; // Uncomment if you need to use the actual PAC1934 +class MockMCP79412; +//class MockSFE_UBLOX_GNSS; // Uncomment if you need to use the actual SFE_UBLOX_GNSS +//class MockPAC1934; // Uncomment if you need to use the actual PAC1934 +class MockVEML3328; +//class MockAdafruit_SHT4x; // Uncomment if you need to use the actual Adafruit_SHT4x +class MockMXC6655; +//class MockBMA456; // Uncomment if you need to use the actual BMA456 + + +// Test fixture class for Kestrel tests +class KestrelTest : public ::testing::Test { +protected: + // Set up mocks and test objects + void SetUp() override { + // Any test setup code goes here + } + + void TearDown() override { + // Any test cleanup code goes here + } +}; + +// Basic test to verify our testing framework works +TEST_F(KestrelTest, TestSensorMockWorks) { + MockSensor mockSensor; + time_t dummyTime = 0; + bool criticalFault = false; + bool fault = false; + + EXPECT_CALL(mockSensor, begin(dummyTime, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Return("Sensor Initialized")); + + String result = mockSensor.begin(dummyTime, criticalFault, fault); + EXPECT_EQ(result, "Sensor Initialized"); +} + +// Test that the MCP79412 RTC mock works +TEST_F(KestrelTest, TestMockRTC) { + MockMCP79412 rtc; + + // Set up expectations + EXPECT_CALL(rtc, begin(true)) + .Times(1) + .WillOnce(::testing::Return(1)); // Return success + + EXPECT_CALL(rtc, getTimeUnix()) + .Times(1) + .WillOnce(::testing::Return(1585699200)); // Return a fixed time (April 1, 2020) + + EXPECT_CALL(rtc, getUUIDString()) + .Times(1) + .WillOnce(::testing::Return("test-uuid-1234")); + + // Test that the mock behaves as expected + int result = rtc.begin(true); + EXPECT_EQ(result, 1); + + time_t time = rtc.getTimeUnix(); + EXPECT_EQ(time, 1585699200); + + String uuid = rtc.getUUIDString(); + EXPECT_EQ(uuid, "test-uuid-1234"); +} + +// Test that the VEML3328 mock works +TEST_F(KestrelTest, TestMockVEML3328) { + MockVEML3328 veml; + + // Set up expectations + EXPECT_CALL(veml, begin()) + .Times(1) + .WillOnce(::testing::Return(0)); // Return success + + EXPECT_CALL(veml, autoRange()) + .Times(1) + .WillOnce(::testing::Return(1)); // Return success + + EXPECT_CALL(veml, GetValue(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Return(123.45f)); // Return a fixed value + + // Test that the mock behaves as expected + int result = veml.begin(); + EXPECT_EQ(result, 0); + + result = veml.autoRange(); + EXPECT_EQ(result, 1); + + bool state = false; + float value = veml.GetValue(VEML3328::Channel::Red, state); + EXPECT_FLOAT_EQ(value, 123.45f); +} + + + + +// Kestrel begin tests + +// Verify Kestrel function calling works +TEST_F(KestrelTest, TestKestrelBegin) { + // Mock the components that Kestrel depends on + MockMXC6655 mockAccel; + + EXPECT_CALL(mockAccel, begin()) + .Times(1) + .WillOnce(::testing::Return(0)); // Return success + + EXPECT_CALL(mockAccel, getAccel(2, 0)) + .Times(1) + .WillOnce(::testing::Return(20.0f)); // Return success + + Kestrel kestrel(&mockAccel); + + bool result = kestrel.zeroAccel(false); // Reset accelerometer offsets + + // Verify the result and state + EXPECT_FALSE(result); +} + + // Verify Kestrel begin thows error with reset reason if init is not done + + // Verify Kestrel begin sets criticalFault tue if ioOb.begin() fails + + // Verify Kestrel begin sets criticalFault true if ioTalon.begin() fails + + // Verify Kestrel begin sets criticalFault true if csaAlpha.begin() fails TODO: NOT IMPLEMENTED + + // Verify Kestrel begin sets criticalFault true if csaBeta.begin() fails TODO: NOT IMPLEMENTED + + // Verify Kestrel begin sets criticalFault true if led.begin() fails TODO: NOT IMPLEMENTED + + // Verify Kestrel Begin sets led functions if init is not done + + // Verify Kestrel begin sets criticalFault true if rtc.begin() fails + + // Verify Kestel begin sets criticalFault true if gps.begin() fails + + // Verify Kestrel begin sets initDone true if init is done + +//Kestrel getErrors tests + +//Kestrel getData tests + +//Kestrel getMetadata tests + +//Kestrel selfDiagnostic tests + +//Kestrel updateLocation tests + +//Kestrel connectToCell tests + +//Kestrel enablePower tests + +//Kestrel enableData tests + +//Kestrel setDirection tests + +//Kestrel getFault tests + +//Kestrel enableI2C_OB tests + +//Kestrel enableI2C_Global tests + +//Kestrel enableI2C_External tests + +//Kestrel enableSD tests + +//Kestrel sdInserted tests + +//Kestrel enableAuxPower tests + +//Kestrel getTime tests + +//Kestrel syncTime tests + +//Kestrel startTimer tests + +//Kestrel waitUntilTimerDone tests + +//Kestrel getTimeString tests + +//kestrel totalErrors tests + +//Kestrel statLED tests + +//Kestrel setIndicatorState tests + +//Kestrel updateTime tests + +//Kestrel feedWDT tests + +//Kestrel releaseWDT tests + +//Kestrel getPosLat tests + +//Kestrel getPosLong tests + +//Kestrel getPosAlt tests + +//Kestrel getPosTime tests + +//Kestrel getPosTimeString tests + +//Kestrel configTalonSense tests + +//Kestrel getMessageID tests + +//Kestrel testForBat tests + +//Kestrel zeroAccel tests + + diff --git a/test/unit/FlightControl_Demo/FlightControl_DemoTest.cpp b/test/unit/FlightControl_Demo/FlightControl_DemoTest.cpp deleted file mode 100644 index 9c230f4..0000000 --- a/test/unit/FlightControl_Demo/FlightControl_DemoTest.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// FlightControl_Demo/test/unit/Driver-_-Kestrel/kestrel_basic_test.cpp -#include "gtest/gtest.h" -#include "fff.h" -#include "MockWireDeclare.h" -#include "FlightControl_DemoTestHarness.h" - -// Test fixture -class FlightControlBasicTest : public ::testing::Test { -protected: - void SetUp() override { - // Reset all fakes before each test - RESET_FAKE(Wire_begin); - RESET_FAKE(Wire_setClock); - RESET_FAKE(Wire_beginTransmission); - RESET_FAKE(Wire_endTransmission); - RESET_FAKE(Wire_requestFrom_3args); - RESET_FAKE(Wire_requestFrom_2args); - RESET_FAKE(Wire_available); - RESET_FAKE(Wire_read); - RESET_FAKE(Wire_write_uint8); - RESET_FAKE(Wire_write_buffer); - } -}; - -// A simple test to verify that Google Test is working -TEST_F(FlightControlBasicTest, GoogleTestWorks) { - EXPECT_TRUE(true); -} - -// A test to verify that FFF is working with our mocks -TEST_F(FlightControlBasicTest, FFFWorks) { - // Call a mocked function - Wire.begin(); - - // Verify it was called - EXPECT_EQ(Wire_begin_fake.call_count, 1); -} - -TEST_F(FlightControlBasicTest, GetDataStringFormat) { - // Setup any necessary state first - - // Call the function through the harness - String result = test_harness::callGetDataString(); - - // Assert on expected format - EXPECT_TRUE(result.startsWith("{\"Data\":{")); - // More assertions -} \ No newline at end of file diff --git a/test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.cpp b/test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.cpp deleted file mode 100644 index 3ac3246..0000000 --- a/test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "FlightControl_DemoTestHarness.h" - -// Declare the functions from the original file -extern String getDataString(); -extern String getErrorString(); -extern String getDiagnosticString(uint8_t level); -extern void logEvents(uint8_t type, uint8_t destination); - -namespace FlightControl_DemoTestHarness { - String callGetDataString() { - return getDataString(); - } - // Other functions -} \ No newline at end of file diff --git a/test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.h b/test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.h deleted file mode 100644 index beeefff..0000000 --- a/test/unit/FlightControl_Demo/FlightControl_DemoTestHarness.h +++ /dev/null @@ -1,12 +0,0 @@ -// test/unit/FlightControl_Demo/test_harness.h -#pragma once - -#include "../../mocks/MockParticle.h" - -namespace test_harness { - String callGetDataString(); - String callGetErrorString(); - String callGetDiagnosticString(uint8_t level); - void callLogEvents(uint8_t type, uint8_t destination); - // Add more as needed -} \ No newline at end of file From 640e64119736287cc0790ab0dd5c2edc399c9e05 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 27 Mar 2025 10:43:06 -0500 Subject: [PATCH 02/48] bunch of random testing --- test/mocks/Particle.h | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h index 0dda129..2206ae6 100644 --- a/test/mocks/Particle.h +++ b/test/mocks/Particle.h @@ -69,6 +69,20 @@ class String { return String(value_ + other); } + String operator+(int other) const { + return String(value_ + std::to_string(other)); + } + + String endsWith(const String& suffix) const { + if (suffix.length() > value_.length()) return false; + return value_.compare(value_.length() - suffix.length(), suffix.length(), suffix.value_) == 0; + } + + String remove(size_t pos, size_t len) { + if (pos >= value_.length()) return *this; + return String(value_.erase(pos, len)); + } + private: std::string value_; }; @@ -88,6 +102,7 @@ typedef bool boolean; #define OUTPUT 1 #define INPUT_PULLUP 2 #define SERIAL_8N1 0x00 +#define HEX 16 // Stream base class class Stream { @@ -158,12 +173,10 @@ class TwoWire { extern TwoWire Wire; // EEPROM -namespace EEPROM { - template - void get(int addr, T& val) { - val = T(); - } -} +class EEPROM { +public: + void get(int addr, float val) {} +}; // System class that appears in many Particle programs class SystemClass { @@ -174,6 +187,7 @@ class SystemClass { void reset() {} const char* version() { return "1.0.0"; } String deviceID() { return "TEST_DEVICE_ID"; } + bool freeMemory() { return true; } }; extern SystemClass System; From c1dad84bc426fa8e06ddeb93cf4eec3be15d5de7 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 27 Mar 2025 10:49:01 -0500 Subject: [PATCH 03/48] submodule changes --- lib/Driver_-_Kestrel | 2 +- lib/Driver_-_MCP79412 | 2 +- lib/Driver_-_Sensor | 2 +- lib/MXC6655 | 2 +- lib/PCAL9535A_Library | 2 +- lib/VEML3328 | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 3591e84..001e52a 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 3591e84f206a1f09ebe7635247c32d3427809086 +Subproject commit 001e52abf89d1d0e56fa8bdcae39095ed57f8a6d diff --git a/lib/Driver_-_MCP79412 b/lib/Driver_-_MCP79412 index 45abb14..4b0fe70 160000 --- a/lib/Driver_-_MCP79412 +++ b/lib/Driver_-_MCP79412 @@ -1 +1 @@ -Subproject commit 45abb14ae26e4d28e16e6117524507e2a6d83d6f +Subproject commit 4b0fe7075cb3a212cbc6322fabfa37a095f2c910 diff --git a/lib/Driver_-_Sensor b/lib/Driver_-_Sensor index d13c2ac..cd2873a 160000 --- a/lib/Driver_-_Sensor +++ b/lib/Driver_-_Sensor @@ -1 +1 @@ -Subproject commit d13c2ac5771b446bc8010937e9218b6a80fe0ff7 +Subproject commit cd2873af6756767b5d6b4ad41f54999757e8aeb8 diff --git a/lib/MXC6655 b/lib/MXC6655 index 9cbb9c4..ae031a4 160000 --- a/lib/MXC6655 +++ b/lib/MXC6655 @@ -1 +1 @@ -Subproject commit 9cbb9c4d15cade31f3b72a28a58dfe9590e9f21f +Subproject commit ae031a49f2a7862f2fe8406db82f54ecb7f10bfd diff --git a/lib/PCAL9535A_Library b/lib/PCAL9535A_Library index b922cdf..40f1d84 160000 --- a/lib/PCAL9535A_Library +++ b/lib/PCAL9535A_Library @@ -1 +1 @@ -Subproject commit b922cdf198f93788c9ac4454a98640972e74ff1e +Subproject commit 40f1d84b172b41790dec3f5553188de8bd19a7f4 diff --git a/lib/VEML3328 b/lib/VEML3328 index 39c1cc8..f066297 160000 --- a/lib/VEML3328 +++ b/lib/VEML3328 @@ -1 +1 @@ -Subproject commit 39c1cc8a65f30284c2c7978458213eeb06d68921 +Subproject commit f066297784a108b872d17637c8b9a95927aa0640 From 58bdf2441da8a55504089ad141582ae454f34c36 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 27 Mar 2025 16:54:43 -0500 Subject: [PATCH 04/48] wip still not working --- test/mocks/Particle.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h index 2206ae6..6d7a690 100644 --- a/test/mocks/Particle.h +++ b/test/mocks/Particle.h @@ -173,7 +173,7 @@ class TwoWire { extern TwoWire Wire; // EEPROM -class EEPROM { +class EEPROMClass { public: void get(int addr, float val) {} }; @@ -238,6 +238,8 @@ void pinMode(uint16_t pin, uint8_t mode); void digitalWrite(uint16_t pin, uint8_t value); int digitalRead(uint16_t pin); +extern EEPROMClass EEPROM; + // Define min/max functions if not already defined #ifndef min #define min(a,b) ((a)<(b)?(a):(b)) From 731be604432c56188d726b1c8ea551378c91fc9d Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 28 Mar 2025 12:42:33 -0500 Subject: [PATCH 05/48] refactored the paricle Time dependencies out of Kestrel, changed to constructor dependency injection pattern --- .gitmodules | 3 ++ lib/FlightControl-platform-dependencies | 1 + lib/PCAL9535A_Library | 2 +- src/FlightControl_Demo.cpp | 6 +++- src/platform/ParticleTimeProvider.cpp | 24 ++++++++++++++++ src/platform/ParticleTimeProvider.h | 38 +++++++++++++++++++++++++ test/mocks/MockTimeProvider.h | 34 ++++++++++++++++++++++ 7 files changed, 106 insertions(+), 2 deletions(-) create mode 160000 lib/FlightControl-platform-dependencies create mode 100644 src/platform/ParticleTimeProvider.cpp create mode 100644 src/platform/ParticleTimeProvider.h create mode 100644 test/mocks/MockTimeProvider.h diff --git a/.gitmodules b/.gitmodules index a0b5eea..f79521b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -128,3 +128,6 @@ [submodule "test/external/googletest"] path = test/external/googletest url = https://github.com/google/googletest.git +[submodule "lib/FlightControl-platform-dependencies"] + path = lib/FlightControl-platform-dependencies + url = https://github.com/gemsiot/FlightControl-platform-dependencies.git diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies new file mode 160000 index 0000000..ab1a7e7 --- /dev/null +++ b/lib/FlightControl-platform-dependencies @@ -0,0 +1 @@ +Subproject commit ab1a7e7800659de0cbec7fbbce432247ea6ec5ee diff --git a/lib/PCAL9535A_Library b/lib/PCAL9535A_Library index 40f1d84..b922cdf 160000 --- a/lib/PCAL9535A_Library +++ b/lib/PCAL9535A_Library @@ -1 +1 @@ -Subproject commit 40f1d84b172b41790dec3f5553188de8bd19a7f4 +Subproject commit b922cdf198f93788c9ac4454a98640972e74ff1e diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 377d772..2f91a30 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -59,6 +59,8 @@ int configurePowerSave(int desiredPowerSaveMode); #include #include +#include "platform/ParticleTimeProvider.h" + const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -67,7 +69,9 @@ const unsigned long indicatorTimeout = 60000; //Wait for up to 1 minute with ind const uint64_t balancedDiagnosticPeriod = 3600000; //Report diagnostics once an hour //DEBUG! int powerSaveMode = 0; //Default to 0, update when configure power save mode is called -Kestrel logger(true); +ParticleTimeProvider realTimeProvider; + +Kestrel logger(realTimeProvider, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 AuxTalon aux(0, 0x14); //Instantiate AUX talon with deaults - null port and hardware v1.4 diff --git a/src/platform/ParticleTimeProvider.cpp b/src/platform/ParticleTimeProvider.cpp new file mode 100644 index 0000000..e27a815 --- /dev/null +++ b/src/platform/ParticleTimeProvider.cpp @@ -0,0 +1,24 @@ +// src/platform/ParticleTimeProvider.cpp +#include "ParticleTimeProvider.h" + +// Use :: prefix for global scope functions like millis() and delay() +// to avoid potential name conflicts if methods had same name. + +int ParticleTimeProvider::year() { return Time.year(); } +int ParticleTimeProvider::year(time_t t) { return Time.year(t); } +int ParticleTimeProvider::month() { return Time.month(); } +int ParticleTimeProvider::month(time_t t) { return Time.month(t); } +int ParticleTimeProvider::day() { return Time.day(); } +int ParticleTimeProvider::day(time_t t) { return Time.day(t); } +int ParticleTimeProvider::hour() { return Time.hour(); } +int ParticleTimeProvider::hour(time_t t) { return Time.hour(t); } +int ParticleTimeProvider::minute() { return Time.minute(); } +int ParticleTimeProvider::minute(time_t t) { return Time.minute(t); } +int ParticleTimeProvider::second() { return Time.second(); } +int ParticleTimeProvider::second(time_t t) { return Time.second(t); } +time_t ParticleTimeProvider::now() { return Time.now(); } +void ParticleTimeProvider::setTime(time_t t) { Time.setTime(t); } +bool ParticleTimeProvider::isValid() { return Time.isValid(); } +void ParticleTimeProvider::zone(float GMT_Offset) { Time.zone(GMT_Offset); } +uint32_t ParticleTimeProvider::millis() { return ::millis(); } +void ParticleTimeProvider::delay(uint32_t ms) { ::delay(ms); } \ No newline at end of file diff --git a/src/platform/ParticleTimeProvider.h b/src/platform/ParticleTimeProvider.h new file mode 100644 index 0000000..3410802 --- /dev/null +++ b/src/platform/ParticleTimeProvider.h @@ -0,0 +1,38 @@ +// src/platform/ParticleTimeProvider.h +#ifndef PARTICLE_TIME_PROVIDER_H +#define PARTICLE_TIME_PROVIDER_H + +#include "ITimeProvider.h" // Include the interface definition +#include "Particle.h" // Include the actual Particle header HERE + +/** + * @brief Concrete implementation of ITimeProvider using Particle API. + */ +class ParticleTimeProvider : public ITimeProvider { +public: + // Constructor/Destructor (often default is fine) + ParticleTimeProvider() = default; + ~ParticleTimeProvider() override = default; + + // Implement methods from ITimeProvider + int year() override; + int year(time_t t) override; + int month() override; + int month(time_t t) override; + int day() override; + int day(time_t t) override; + int hour() override; + int hour(time_t t) override; + int minute() override; + int minute(time_t t) override; + int second() override; + int second(time_t t) override; + time_t now() override; + void setTime(time_t t) override; + bool isValid() override; + void zone(float GMT_Offset) override; + uint32_t millis() override; + void delay(uint32_t ms) override; +}; + +#endif // PARTICLE_TIME_PROVIDER_H \ No newline at end of file diff --git a/test/mocks/MockTimeProvider.h b/test/mocks/MockTimeProvider.h new file mode 100644 index 0000000..3d91242 --- /dev/null +++ b/test/mocks/MockTimeProvider.h @@ -0,0 +1,34 @@ +// tests/mocks/MockTimeProvider.h +#ifndef MOCK_TIME_PROVIDER_H +#define MOCK_TIME_PROVIDER_H + +#include "gmock/gmock.h" +#include "ITimeProvider.h" // Include the interface definition + +/** + * @brief Google Mock implementation of ITimeProvider for testing. + */ +class MockTimeProvider : public ITimeProvider { +public: + // Mock all methods defined in the interface + MOCK_METHOD(int, year, (), (override)); + MOCK_METHOD(int, year, (time_t t), (override)); + MOCK_METHOD(int, month, (), (override)); + MOCK_METHOD(int, month, (time_t t), (override)); + MOCK_METHOD(int, day, (), (override)); + MOCK_METHOD(int, day, (time_t t), (override)); + MOCK_METHOD(int, hour, (), (override)); + MOCK_METHOD(int, hour, (time_t t), (override)); + MOCK_METHOD(int, minute, (), (override)); + MOCK_METHOD(int, minute, (time_t t), (override)); + MOCK_METHOD(int, second, (), (override)); + MOCK_METHOD(int, second, (time_t t), (override)); + MOCK_METHOD(time_t, now, (), (override)); + MOCK_METHOD(void, setTime, (time_t t), (override)); + MOCK_METHOD(bool, isValid, (), (override)); + MOCK_METHOD(void, zone, (float GMT_Offset), (override)); + MOCK_METHOD(uint32_t, millis, (), (override)); + MOCK_METHOD(void, delay, (uint32_t ms), (override)); +}; + +#endif // MOCK_TIME_PROVIDER_H \ No newline at end of file From 33b1cbaa04ee521deb3561fe4c900fc9f4d2c6dd Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 28 Mar 2025 12:57:41 -0500 Subject: [PATCH 06/48] updated submods --- lib/Driver_-_Kestrel | 2 +- lib/Driver_-_MCP79412 | 2 +- lib/FlightControl-platform-dependencies | 2 +- lib/MXC6655 | 2 +- lib/VEML3328 | 2 +- src/FlightControl_Demo.cpp | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 001e52a..76d71d9 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 001e52abf89d1d0e56fa8bdcae39095ed57f8a6d +Subproject commit 76d71d9356b2af9f33aa2a50ed3eedbb0b2fa39f diff --git a/lib/Driver_-_MCP79412 b/lib/Driver_-_MCP79412 index 4b0fe70..2e3fe8b 160000 --- a/lib/Driver_-_MCP79412 +++ b/lib/Driver_-_MCP79412 @@ -1 +1 @@ -Subproject commit 4b0fe7075cb3a212cbc6322fabfa37a095f2c910 +Subproject commit 2e3fe8b0558e0de9168f6f186b157e06f9e26668 diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index ab1a7e7..0d0a150 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit ab1a7e7800659de0cbec7fbbce432247ea6ec5ee +Subproject commit 0d0a15077eec31040647eca019e2015157e94a56 diff --git a/lib/MXC6655 b/lib/MXC6655 index ae031a4..593307d 160000 --- a/lib/MXC6655 +++ b/lib/MXC6655 @@ -1 +1 @@ -Subproject commit ae031a49f2a7862f2fe8406db82f54ecb7f10bfd +Subproject commit 593307d852c3f1e32d37ef8d7b9c300197b21c0c diff --git a/lib/VEML3328 b/lib/VEML3328 index f066297..4bb84e7 160000 --- a/lib/VEML3328 +++ b/lib/VEML3328 @@ -1 +1 @@ -Subproject commit f066297784a108b872d17637c8b9a95927aa0640 +Subproject commit 4bb84e70e4e2fb2abf3df0b3a60a7b32284b6891 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 2f91a30..ed4c74d 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -121,7 +121,7 @@ Hedorah gas(0, 0, 0x10); //Instantiate CO2 sensor with default ports and v1.0 ha LI710 et(sdi12, 0, 0); //Instantiate ET sensor with default ports and unknown version, pass over SDI12 Talon interface BaroVue10 campPressure(sdi12, 0, 0x00); // Instantiate Barovue10 with default ports and v0.0 hardware -const uint8_t numSensors = 7; //Number must match the number of objects defined in `sensors` array +const uint8_t numSensors = 8; //Number must match the number of objects defined in `sensors` array Sensor* const sensors[numSensors] = { &fileSys, @@ -130,8 +130,8 @@ Sensor* const sensors[numSensors] = { &sdi12, &battery, &logger, //Add sensors after this line - &et - // &haar, + &et, + &haar, // &soil1, // &apogeeSolar, From 3582800dbcb3fbc5e59d24d19e9083817e442ef3 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 28 Mar 2025 14:04:36 -0500 Subject: [PATCH 07/48] missed a few Time.s --- test/mocks/Arduino.cpp | 72 ------------ test/mocks/Arduino.h | 93 --------------- test/mocks/Particle.cpp | 30 ----- test/mocks/Particle.h | 252 ---------------------------------------- test/mocks/SPI.h | 9 -- test/mocks/WProgram.h | 7 -- test/mocks/Wire.h | 9 -- 7 files changed, 472 deletions(-) delete mode 100644 test/mocks/Arduino.cpp delete mode 100644 test/mocks/Arduino.h delete mode 100644 test/mocks/Particle.cpp delete mode 100644 test/mocks/Particle.h delete mode 100644 test/mocks/SPI.h delete mode 100644 test/mocks/WProgram.h delete mode 100644 test/mocks/Wire.h diff --git a/test/mocks/Arduino.cpp b/test/mocks/Arduino.cpp deleted file mode 100644 index 2d5da28..0000000 --- a/test/mocks/Arduino.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "Arduino.h" - -// Global variables for time tracking -unsigned long _millis = 0; -unsigned long _micros = 0; - -// Implementation of Arduino functions -unsigned long millis() { - _millis += 100; // Advance time by 100ms each call for testing - return _millis; -} - -unsigned long micros() { - _micros += 1000; // Advance time by 1000us each call for testing - return _micros; -} - -void delay(unsigned long ms) { - // No-op in test -} - -void delayMicroseconds(unsigned int us) { - // No-op in test -} - -void pinMode(uint8_t pin, uint8_t mode) { - // No-op in test -} - -void digitalWrite(uint8_t pin, uint8_t val) { - // No-op in test -} - -int digitalRead(uint8_t pin) { - return LOW; // Default to LOW -} - -int analogRead(uint8_t pin) { - return 0; // Default to 0 -} - -void analogWrite(uint8_t pin, int val) { - // No-op in test -} - -int analogReadResolution(int bits) { - return bits; -} - -void analogWriteResolution(int bits) { - // No-op in test -} - -unsigned long pulseIn(uint8_t pin, uint8_t value, unsigned long timeout) { - return 0; // Default to 0 -} - -long random(long min, long max) { - return min; -} - -long random(long max) { - return 0; -} - -void randomSeed(unsigned long seed) { - // No-op in test -} - -// Global instances -TwoWire Wire; -SPIClass SPI; \ No newline at end of file diff --git a/test/mocks/Arduino.h b/test/mocks/Arduino.h deleted file mode 100644 index 99246ee..0000000 --- a/test/mocks/Arduino.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef ARDUINO_H_MOCK -#define ARDUINO_H_MOCK - -// Include Particle.h first to make sure shared types come from there -#include "Particle.h" - -// Basic Arduino types -typedef uint8_t byte; -typedef bool boolean; - -// Arduino constants - only defined if not already defined -#ifndef HIGH -#define HIGH 0x1 -#endif -#ifndef LOW -#define LOW 0x0 -#endif -#ifndef INPUT -#define INPUT 0x0 -#endif -#ifndef OUTPUT -#define OUTPUT 0x1 -#endif -#ifndef INPUT_PULLUP -#define INPUT_PULLUP 0x2 -#endif - -// SPI related constants -#define SPI_MODE0 0x00 -#define SPI_MODE1 0x04 -#define SPI_MODE2 0x08 -#define SPI_MODE3 0x0C -#define SPI_CLOCK_DIV2 0x04 -#define SPI_CLOCK_DIV4 0x00 -#define SPI_CLOCK_DIV8 0x05 -#define SPI_CLOCK_DIV16 0x01 -#define SPI_CLOCK_DIV32 0x06 -#define SPI_CLOCK_DIV64 0x02 -#define SPI_CLOCK_DIV128 0x03 - -// Wiring.h based functions -unsigned long millis(); -unsigned long micros(); -void delay(unsigned long ms); -void delayMicroseconds(unsigned int us); -void pinMode(uint8_t pin, uint8_t mode); -void digitalWrite(uint8_t pin, uint8_t val); -int digitalRead(uint8_t pin); -int analogRead(uint8_t pin); -void analogWrite(uint8_t pin, int val); -int analogReadResolution(int bits); -void analogWriteResolution(int bits); -unsigned long pulseIn(uint8_t pin, uint8_t value, unsigned long timeout = 1000000L); - -// Random functions -long random(long min, long max); -long random(long max); -void randomSeed(unsigned long seed); - -// External variables needed by Arduino libraries -extern unsigned long _millis; -extern unsigned long _micros; - -// Use MockSerialPort from Particle.h for Serial and Serial1 -// No need to redefine them here - -// Use TwoWire from Particle.h - no need to redefine -// Wire object instance is defined in Particle.cpp - -// SPI class -class SPIClass { -public: - static const uint8_t MOSI = 11; - static const uint8_t MISO = 12; - static const uint8_t SCK = 13; - static const uint8_t SS = 10; - - void begin() {} - void end() {} - void beginTransaction(uint8_t pin, uint8_t mode, uint32_t speed) {} - void endTransaction() {} - uint8_t transfer(uint8_t data) { return 0; } - uint16_t transfer16(uint16_t data) { return 0; } - void transfer(void *buf, size_t count) {} - void setBitOrder(uint8_t bitOrder) {} - void setDataMode(uint8_t dataMode) {} - void setClockDivider(uint8_t clockDiv) {} -}; - -// SPI object instance -extern SPIClass SPI; - -#endif // ARDUINO_H_MOCK \ No newline at end of file diff --git a/test/mocks/Particle.cpp b/test/mocks/Particle.cpp deleted file mode 100644 index 5348c6f..0000000 --- a/test/mocks/Particle.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "Particle.h" - -// Define global instances -SystemClass System; -TwoWire Wire; -MockSerialPort Serial; -MockSerialPort Serial1; - -// Basic implementations of Arduino functions -unsigned long millis() { - static unsigned long fake_millis = 0; - fake_millis += 100; // Increment by 100ms each call - return fake_millis; -} - -void delay(unsigned long ms) { - // No-op in test -} - -void pinMode(uint16_t pin, uint8_t mode) { - // No-op in test -} - -void digitalWrite(uint16_t pin, uint8_t value) { - // No-op in test -} - -int digitalRead(uint16_t pin) { - return 0; // Default to LOW -} \ No newline at end of file diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h deleted file mode 100644 index 6d7a690..0000000 --- a/test/mocks/Particle.h +++ /dev/null @@ -1,252 +0,0 @@ -#ifndef PARTICLE_H_MOCK -#define PARTICLE_H_MOCK - -#include -#include -#include -#include - -// Must come before any GoogleTest stuff to avoid conflicts -#ifndef SERIAL_8N1 -#define SERIAL_8N1 0x00 -#endif - -// Undefine any macros that might cause conflicts with library headers -#ifdef A -#undef A -#endif - -// Define basic Particle types -typedef int system_event_t; - -// Define common Particle event types -const int time_changed = 1; -const int out_of_memory = 2; - -// String class compatible with Particle's -class String { -public: - String() : value_("") {} - String(const char* str) : value_(str ? str : "") {} - String(const std::string& str) : value_(str) {} - String(int val) : value_(std::to_string(val)) {} - String(long val) : value_(std::to_string(val)) {} - String(float val, int decimalPlaces = 2) : value_(std::to_string(val)) {} - - bool equals(const String& other) const { - return value_ == other.value_; - } - - String substring(size_t beginIndex) const { - return value_.substr(beginIndex); - } - - String substring(size_t beginIndex, size_t endIndex) const { - return value_.substr(beginIndex, endIndex - beginIndex); - } - - size_t length() const { - return value_.length(); - } - - const char* c_str() const { - return value_.c_str(); - } - - bool operator==(const String& other) const { - return value_ == other.value_; - } - - bool operator==(const char* other) const { - return value_ == other; - } - - String operator+(const String& other) const { - return String(value_ + other.value_); - } - - String operator+(const char* other) const { - return String(value_ + other); - } - - String operator+(int other) const { - return String(value_ + std::to_string(other)); - } - - String endsWith(const String& suffix) const { - if (suffix.length() > value_.length()) return false; - return value_.compare(value_.length() - suffix.length(), suffix.length(), suffix.value_) == 0; - } - - String remove(size_t pos, size_t len) { - if (pos >= value_.length()) return *this; - return String(value_.erase(pos, len)); - } - -private: - std::string value_; -}; - -// For gtest to print Strings in a readable way -inline std::ostream& operator<<(std::ostream& os, const String& str) { - return os << str.c_str(); -} - -// Mock minimal Arduino functions that Particle depends on -typedef uint8_t byte; -typedef bool boolean; - -#define LOW 0 -#define HIGH 1 -#define INPUT 0 -#define OUTPUT 1 -#define INPUT_PULLUP 2 -#define SERIAL_8N1 0x00 -#define HEX 16 - -// Stream base class -class Stream { -public: - virtual int available() = 0; - virtual int read() = 0; - virtual int peek() = 0; - virtual void flush() = 0; - virtual size_t write(uint8_t) = 0; - virtual ~Stream() {} -}; - -// Print base class -class Print { -public: - virtual size_t write(uint8_t) = 0; - virtual size_t write(const uint8_t *buffer, size_t size) { - size_t n = 0; - for (size_t i = 0; i < size; i++) { - n += write(buffer[i]); - } - return n; - } - virtual ~Print() {} -}; - -// Mock Serial objects with gmock that inherit from Stream for compatibility -class MockSerialPort : public Stream, public Print { -public: - // For MockSerialPort - MOCK_METHOD(void, begin, (unsigned long, uint8_t)); - MOCK_METHOD(void, print, (const char*)); - MOCK_METHOD(void, print, (int)); - MOCK_METHOD(void, print, (const String&)); - MOCK_METHOD(void, println, (const char*)); - MOCK_METHOD(void, println, (int)); - MOCK_METHOD(void, println, (const String&)); - MOCK_METHOD(void, println, ()); - - // From Stream - MOCK_METHOD(int, available, (), (override)); - MOCK_METHOD(int, read, (), (override)); - MOCK_METHOD(int, peek, (), (override)); - MOCK_METHOD(void, flush, (), (override)); - - // From Print - MOCK_METHOD(size_t, write, (uint8_t), (override)); -}; - -extern MockSerialPort Serial; -extern MockSerialPort Serial1; - -// Wire class for I2C -class TwoWire { -public: - bool isEnabled() { return true; } - void begin() {} - void setClock(uint32_t clock) {} - void beginTransmission(uint8_t address) {} - uint8_t endTransmission(bool stopBit = true) { return 0; } - uint8_t requestFrom(uint8_t address, uint8_t quantity, bool stopBit = true) { return 0; } - int available() { return 0; } - int read() { return 0; } - size_t write(uint8_t data) { return 1; } - size_t write(const uint8_t* data, size_t len) { return len; } -}; - -extern TwoWire Wire; - -// EEPROM -class EEPROMClass { -public: - void get(int addr, float val) {} -}; - -// System class that appears in many Particle programs -class SystemClass { -public: - void on(int event, void (*handler)(system_event_t, int)) {} - uint32_t resetReason() { return 0; } - bool connected() { return true; } - void reset() {} - const char* version() { return "1.0.0"; } - String deviceID() { return "TEST_DEVICE_ID"; } - bool freeMemory() { return true; } -}; - -extern SystemClass System; - -// Define pin names -#define D0 0 -#define D1 1 -#define D2 2 -#define D3 3 -#define D4 4 -#define D5 5 -#define D6 6 -#define D7 7 -#define D8 8 -#define D9 9 -#define D10 10 -#define D11 11 -#define D12 12 -#define D13 13 -#define D14 14 -#define D15 15 -#define D16 16 -#define D17 17 -#define D18 18 -#define D19 19 -#define D20 20 -#define D21 21 -#define D22 22 -#define D23 23 -#define A0 24 -#define A1 25 -#define A2 26 -#define A3 27 -#define A4 28 -#define A5 29 -#define A6 30 -#define A7 31 - -// Platform IDs -#define PLATFORM_BSOM 23 -#define PLATFORM_B5SOM 32 -#define PLATFORM_ID PLATFORM_BSOM - -// Arduino-like functions -unsigned long millis(); -void delay(unsigned long ms); -void pinMode(uint16_t pin, uint8_t mode); -void digitalWrite(uint16_t pin, uint8_t value); -int digitalRead(uint16_t pin); - -extern EEPROMClass EEPROM; - -// Define min/max functions if not already defined -#ifndef min -#define min(a,b) ((a)<(b)?(a):(b)) -#endif - -#ifndef max -#define max(a,b) ((a)>(b)?(a):(b)) -#endif - -#endif // PARTICLE_H_MOCK \ No newline at end of file diff --git a/test/mocks/SPI.h b/test/mocks/SPI.h deleted file mode 100644 index 7632048..0000000 --- a/test/mocks/SPI.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef SPI_H_MOCK -#define SPI_H_MOCK - -#include "Arduino.h" // For SPIClass definition - -// This is just a placeholder that includes the SPIClass implementation from Arduino.h -// Libraries that include SPI.h will get the implementation from Arduino.h - -#endif // SPI_H_MOCK \ No newline at end of file diff --git a/test/mocks/WProgram.h b/test/mocks/WProgram.h deleted file mode 100644 index c9535cd..0000000 --- a/test/mocks/WProgram.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef WPROGRAM_H_MOCK -#define WPROGRAM_H_MOCK - -// This is for older Arduino libraries that use WProgram.h instead of Arduino.h -#include "Arduino.h" - -#endif // WPROGRAM_H_MOCK \ No newline at end of file diff --git a/test/mocks/Wire.h b/test/mocks/Wire.h deleted file mode 100644 index e8abb9c..0000000 --- a/test/mocks/Wire.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef WIRE_H_MOCK -#define WIRE_H_MOCK - -#include "Arduino.h" // For TwoWire definition - -// This is just a placeholder that includes the TwoWire class from Arduino.h -// Libraries that include Wire.h will get the TwoWire implementation from Arduino.h - -#endif // WIRE_H_MOCK \ No newline at end of file From 4259af8b11ad5f80abc370e0a976e3baa4b7e694 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 28 Mar 2025 14:15:31 -0500 Subject: [PATCH 08/48] fixed connection problem hopefully --- lib/Driver_-_Kestrel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 76d71d9..928527c 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 76d71d9356b2af9f33aa2a50ed3eedbb0b2fa39f +Subproject commit 928527c2eb07b08f6f75a02554f803b21da23057 From a7c7b30ecefb1fea4f7fda1562e743f381797eee Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 28 Mar 2025 14:53:43 -0500 Subject: [PATCH 09/48] removed comma --- src/FlightControl_Demo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index ed4c74d..d1834a1 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -131,7 +131,7 @@ Sensor* const sensors[numSensors] = { &battery, &logger, //Add sensors after this line &et, - &haar, + &haar // &soil1, // &apogeeSolar, From d5853140bfeebdd607f9a299ad27c4b76ae8ee30 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Mon, 31 Mar 2025 16:48:10 -0500 Subject: [PATCH 10/48] started getting rid of gpio dependency --- src/platform/ParticleGpio.cpp | 5 +++++ src/platform/ParticleGpio.h | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/platform/ParticleGpio.cpp create mode 100644 src/platform/ParticleGpio.h diff --git a/src/platform/ParticleGpio.cpp b/src/platform/ParticleGpio.cpp new file mode 100644 index 0000000..f3c4666 --- /dev/null +++ b/src/platform/ParticleGpio.cpp @@ -0,0 +1,5 @@ +#include "ParticleGpio.h" + +void ParticleGpio::pinMode(uint16_t pin, PinMode mode){::pinMode(uint16_t pin, PinMode mode);} +void ParticleGpio::digitalWrite(uint16_t pin, uint8_t value){::digitalWrite(uint16_t pin, uint8_t value);} +uint32_t ParticleGpio::digitalRead(uint16_t pin){::digitalRead(pin);} \ No newline at end of file diff --git a/src/platform/ParticleGpio.h b/src/platform/ParticleGpio.h new file mode 100644 index 0000000..2672236 --- /dev/null +++ b/src/platform/ParticleGpio.h @@ -0,0 +1,23 @@ +// src/platform/ParticleGpio.h +#ifndef PARTICLE_GPIO_H +#define PARTICLE_GPIO_H + +#include "IGpio.h" // Include the interface definition +#include "Particle.h" // Include the actual Particle header HERE + +/** + * @brief Concrete implementation of IGpio + */ +class ParticleGpio : public IGpio { +public: + // Constructor/Destructor (often default is fine) + ParticleGpio() = default; + ~ParticleGpio() override = default; + + // Implement methods from IGpio + void pinMode(uint16_t pin, PinMode mode) override; + void digitalWrite(uint16_t pin, uint8_t value) override; + uint32_t digitlaRead(uint16_t pin) override; +}; + +#endif // PARTICLE_GPIO_H \ No newline at end of file From d220fd6437100ec274661ad39ecc1977622bb57f Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 1 Apr 2025 12:04:27 -0500 Subject: [PATCH 11/48] gpio abstraction compiles --- src/FlightControl_Demo.cpp | 6 +++++- src/platform/ParticleGpio.cpp | 21 ++++++++++++++++++--- src/platform/ParticleGpio.h | 4 ++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index d1834a1..3f2baea 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -60,6 +60,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include #include "platform/ParticleTimeProvider.h" +#include "platform/ParticleGpio.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -70,8 +71,11 @@ const uint64_t balancedDiagnosticPeriod = 3600000; //Report diagnostics once an int powerSaveMode = 0; //Default to 0, update when configure power save mode is called ParticleTimeProvider realTimeProvider; +ParticleGpio realGpio; -Kestrel logger(realTimeProvider, true); +Kestrel logger(realTimeProvider, + realGpio, + true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 AuxTalon aux(0, 0x14); //Instantiate AUX talon with deaults - null port and hardware v1.4 diff --git a/src/platform/ParticleGpio.cpp b/src/platform/ParticleGpio.cpp index f3c4666..a6cd2bf 100644 --- a/src/platform/ParticleGpio.cpp +++ b/src/platform/ParticleGpio.cpp @@ -1,5 +1,20 @@ #include "ParticleGpio.h" -void ParticleGpio::pinMode(uint16_t pin, PinMode mode){::pinMode(uint16_t pin, PinMode mode);} -void ParticleGpio::digitalWrite(uint16_t pin, uint8_t value){::digitalWrite(uint16_t pin, uint8_t value);} -uint32_t ParticleGpio::digitalRead(uint16_t pin){::digitalRead(pin);} \ No newline at end of file +void ParticleGpio::pinMode(uint16_t pin, IPinMode mode){ + PinMode particlePinMode; + switch(mode) + { + case IPinMode::INPUT: + particlePinMode = ::INPUT; + break; + case IPinMode::OUTPUT: + particlePinMode = ::OUTPUT; + break; + default: + particlePinMode = ::INPUT; + break; + } + ::pinMode(pin, particlePinMode); +} +void ParticleGpio::digitalWrite(uint16_t pin, uint8_t value){::digitalWrite(pin, value);} +int32_t ParticleGpio::digitalRead(uint16_t pin){return ::digitalRead(pin);} \ No newline at end of file diff --git a/src/platform/ParticleGpio.h b/src/platform/ParticleGpio.h index 2672236..b10d121 100644 --- a/src/platform/ParticleGpio.h +++ b/src/platform/ParticleGpio.h @@ -15,9 +15,9 @@ class ParticleGpio : public IGpio { ~ParticleGpio() override = default; // Implement methods from IGpio - void pinMode(uint16_t pin, PinMode mode) override; + void pinMode(uint16_t pin, IPinMode mode) override; void digitalWrite(uint16_t pin, uint8_t value) override; - uint32_t digitlaRead(uint16_t pin) override; + int32_t digitalRead(uint16_t pin) override; }; #endif // PARTICLE_GPIO_H \ No newline at end of file From a3cee36be442d0a4d8a4dc890c8c48bf947cf205 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 1 Apr 2025 12:16:46 -0500 Subject: [PATCH 12/48] updated gpio submods --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-platform-dependencies | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 928527c..8dee45b 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 928527c2eb07b08f6f75a02554f803b21da23057 +Subproject commit 8dee45b12ba33d37d8f430e6628ce5d08f7bce60 diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index 0d0a150..f482572 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit 0d0a15077eec31040647eca019e2015157e94a56 +Subproject commit f4825722440b97766572c2d944b31b51fe149ade From ced8205301c0f7a191a8807c296fd7dc90b52bd2 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 1 Apr 2025 14:29:44 -0500 Subject: [PATCH 13/48] added ISystem --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-platform-dependencies | 2 +- src/FlightControl_Demo.cpp | 3 + src/platform/ParticleSystem.cpp | 98 +++++++++++++++++++++++++ src/platform/ParticleSystem.h | 21 ++++++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/platform/ParticleSystem.cpp create mode 100644 src/platform/ParticleSystem.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 8dee45b..ee3e2d1 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 8dee45b12ba33d37d8f430e6628ce5d08f7bce60 +Subproject commit ee3e2d15c75700cecbd1b95e0284468f3b5ca306 diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index f482572..91006fe 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit f4825722440b97766572c2d944b31b51fe149ade +Subproject commit 91006fe9c521638ae13f3847264c74d2b63afd0a diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 3f2baea..a2a944e 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -61,6 +61,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "platform/ParticleTimeProvider.h" #include "platform/ParticleGpio.h" +#include "platform/ParticleSystem.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -72,9 +73,11 @@ int powerSaveMode = 0; //Default to 0, update when configure power save mode is ParticleTimeProvider realTimeProvider; ParticleGpio realGpio; +ParticleSystem realSystem; Kestrel logger(realTimeProvider, realGpio, + realSystem, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/platform/ParticleSystem.cpp b/src/platform/ParticleSystem.cpp new file mode 100644 index 0000000..1320ee2 --- /dev/null +++ b/src/platform/ParticleSystem.cpp @@ -0,0 +1,98 @@ +#include "ParticleSystem.h" +// #include // Only needed if managing multiple 'on' handlers complexly + +// --- Method Implementations --- + +void ParticleSystem::on(IEventType eventType, SystemEventHandler handler) { + system_event_t particleEvent; + switch (eventType) { + case IEventType::TIME_CHANGED: particleEvent = time_changed; break; + case IEventType::OUT_OF_MEMORY: particleEvent = out_of_memory; break; + default: Log.error("Unsupported IEventType in ParticleSystem::on"); return; + } + + auto particleHandlerWrapper = [handler](system_event_t sys_event, int data, void*) { + IEventType interfaceEventType; + switch (sys_event) { + case time_changed: interfaceEventType = IEventType::TIME_CHANGED; break; + case out_of_memory: interfaceEventType = IEventType::OUT_OF_MEMORY; break; + default: Log.warn("Unexpected system_event_t in wrapper: %d", (int)sys_event); return; + } + if (handler) { handler(interfaceEventType, data); } + }; + + System.on(particleEvent, particleHandlerWrapper); +} + +IResetReason ParticleSystem::resetReason() { + int particleReason = System.resetReason(); + switch (particleReason) { + case RESET_REASON_NONE: return IResetReason::RESET_REASON_NONE; + case RESET_REASON_UNKNOWN: return IResetReason::RESET_REASON_UNKNOWN; + // Hardware + case RESET_REASON_PIN_RESET: return IResetReason::RESET_REASON_PIN_RESET; + case RESET_REASON_POWER_MANAGEMENT: return IResetReason::RESET_REASON_POWER_MANAGEMENT; + case RESET_REASON_POWER_DOWN: return IResetReason::RESET_REASON_POWER_DOWN; + case RESET_REASON_POWER_BROWNOUT: return IResetReason::RESET_REASON_POWER_BROWNOUT; + case RESET_REASON_WATCHDOG: return IResetReason::RESET_REASON_WATCHDOG; + // Software + case RESET_REASON_UPDATE: return IResetReason::RESET_REASON_UPDATE; + case RESET_REASON_UPDATE_ERROR: return IResetReason::RESET_REASON_UPDATE_ERROR; + case RESET_REASON_UPDATE_TIMEOUT: return IResetReason::RESET_REASON_UPDATE_TIMEOUT; + case RESET_REASON_FACTORY_RESET: return IResetReason::RESET_REASON_FACTORY_RESET; + case RESET_REASON_SAFE_MODE: return IResetReason::RESET_REASON_SAFE_MODE; + case RESET_REASON_DFU_MODE: return IResetReason::RESET_REASON_DFU_MODE; + case RESET_REASON_PANIC: return IResetReason::RESET_REASON_PANIC; + case RESET_REASON_USER: return IResetReason::RESET_REASON_USER; + case RESET_REASON_CONFIG_UPDATE: return IResetReason::RESET_REASON_CONFIG_UPDATE; + default: return IResetReason::RESET_REASON_UNKNOWN; + } +} +uint32_t ParticleSystem::freeMemory() { + return System.freeMemory(); +} + +// Implementation for the sleep function with all wakeup reasons +IWakeupReason ParticleSystem::sleep(const particle::SystemSleepConfiguration& config) { + // Call the actual Particle function + SystemSleepResult result = System.sleep(config); + + // Translate Particle's SystemSleepWakeupReason to our IWakeupReason + switch (result.wakeupReason()) { + case SystemSleepWakeupReason::UNKNOWN: + return IWakeupReason::UNKNOWN; + case SystemSleepWakeupReason::BY_GPIO: + return IWakeupReason::BY_GPIO; + case SystemSleepWakeupReason::BY_ADC: + return IWakeupReason::BY_ADC; + case SystemSleepWakeupReason::BY_DAC: + return IWakeupReason::BY_DAC; + case SystemSleepWakeupReason::BY_RTC: + return IWakeupReason::BY_RTC; + case SystemSleepWakeupReason::BY_LPCOMP: + return IWakeupReason::BY_LPCOMP; + case SystemSleepWakeupReason::BY_USART: + return IWakeupReason::BY_USART; + case SystemSleepWakeupReason::BY_I2C: + return IWakeupReason::BY_I2C; + case SystemSleepWakeupReason::BY_SPI: + return IWakeupReason::BY_SPI; + case SystemSleepWakeupReason::BY_TIMER: + return IWakeupReason::BY_TIMER; + case SystemSleepWakeupReason::BY_CAN: + return IWakeupReason::BY_CAN; + case SystemSleepWakeupReason::BY_USB: + return IWakeupReason::BY_USB; + case SystemSleepWakeupReason::BY_BLE: + return IWakeupReason::BY_BLE; + case SystemSleepWakeupReason::BY_NFC: + return IWakeupReason::BY_NFC; + case SystemSleepWakeupReason::BY_NETWORK: + return IWakeupReason::BY_NETWORK; + default: + // You might also want to check result.error() here if needed + return IWakeupReason::UNKNOWN; + } +} + +// --- Implement overrides for other methods if added to ISystem --- \ No newline at end of file diff --git a/src/platform/ParticleSystem.h b/src/platform/ParticleSystem.h new file mode 100644 index 0000000..a52c406 --- /dev/null +++ b/src/platform/ParticleSystem.h @@ -0,0 +1,21 @@ +#ifndef PARTICLE_SYSTEM_H +#define PARTICLE_SYSTEM_H + +#include "ISystem.h" // Include the interface definition +#include "Particle.h" // Include the actual Particle header + +class ParticleSystem : public ISystem { +public: + ParticleSystem() = default; + ~ParticleSystem() override = default; + + void on(IEventType event, SystemEventHandler handler) override; + IResetReason resetReason() override; + uint32_t freeMemory() override; + + // Update the override signature to return IWakeupReason + IWakeupReason sleep(const particle::SystemSleepConfiguration& config) override; + +}; + +#endif // PARTICLE_SYSTEM_H \ No newline at end of file From 1066b4e9ea79c7227f14b7bdab71666819818fa0 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 1 Apr 2025 17:07:11 -0500 Subject: [PATCH 14/48] modified sleep --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-platform-dependencies | 2 +- src/platform/ParticleSystem.cpp | 177 ++++++++++++++++-------- src/platform/ParticleSystem.h | 5 +- 4 files changed, 126 insertions(+), 60 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index ee3e2d1..49ffa30 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit ee3e2d15c75700cecbd1b95e0284468f3b5ca306 +Subproject commit 49ffa300a2787c23827bcd01c4c4ee0247724420 diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index 91006fe..31c0954 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit 91006fe9c521638ae13f3847264c74d2b63afd0a +Subproject commit 31c0954fe6df483f3f7487b4cf2ad93310abc006 diff --git a/src/platform/ParticleSystem.cpp b/src/platform/ParticleSystem.cpp index 1320ee2..45d0038 100644 --- a/src/platform/ParticleSystem.cpp +++ b/src/platform/ParticleSystem.cpp @@ -1,5 +1,5 @@ #include "ParticleSystem.h" -// #include // Only needed if managing multiple 'on' handlers complexly +#include "Particle.h" // Make sure Particle.h is included for Particle types/functions // --- Method Implementations --- @@ -11,6 +11,7 @@ void ParticleSystem::on(IEventType eventType, SystemEventHandler handler) { default: Log.error("Unsupported IEventType in ParticleSystem::on"); return; } + // Wrapper lambda captures handler by value auto particleHandlerWrapper = [handler](system_event_t sys_event, int data, void*) { IEventType interfaceEventType; switch (sys_event) { @@ -26,71 +27,137 @@ void ParticleSystem::on(IEventType eventType, SystemEventHandler handler) { IResetReason ParticleSystem::resetReason() { int particleReason = System.resetReason(); + // Assuming Particle's RESET_REASON_* constants match the values used in IResetReason enum switch (particleReason) { - case RESET_REASON_NONE: return IResetReason::RESET_REASON_NONE; - case RESET_REASON_UNKNOWN: return IResetReason::RESET_REASON_UNKNOWN; - // Hardware - case RESET_REASON_PIN_RESET: return IResetReason::RESET_REASON_PIN_RESET; - case RESET_REASON_POWER_MANAGEMENT: return IResetReason::RESET_REASON_POWER_MANAGEMENT; - case RESET_REASON_POWER_DOWN: return IResetReason::RESET_REASON_POWER_DOWN; - case RESET_REASON_POWER_BROWNOUT: return IResetReason::RESET_REASON_POWER_BROWNOUT; - case RESET_REASON_WATCHDOG: return IResetReason::RESET_REASON_WATCHDOG; - // Software - case RESET_REASON_UPDATE: return IResetReason::RESET_REASON_UPDATE; - case RESET_REASON_UPDATE_ERROR: return IResetReason::RESET_REASON_UPDATE_ERROR; - case RESET_REASON_UPDATE_TIMEOUT: return IResetReason::RESET_REASON_UPDATE_TIMEOUT; - case RESET_REASON_FACTORY_RESET: return IResetReason::RESET_REASON_FACTORY_RESET; - case RESET_REASON_SAFE_MODE: return IResetReason::RESET_REASON_SAFE_MODE; - case RESET_REASON_DFU_MODE: return IResetReason::RESET_REASON_DFU_MODE; - case RESET_REASON_PANIC: return IResetReason::RESET_REASON_PANIC; - case RESET_REASON_USER: return IResetReason::RESET_REASON_USER; - case RESET_REASON_CONFIG_UPDATE: return IResetReason::RESET_REASON_CONFIG_UPDATE; - default: return IResetReason::RESET_REASON_UNKNOWN; + // Keep only the cases defined by Particle's resetReason() return values + case ::RESET_REASON_PIN_RESET: return IResetReason::PIN_RESET; + case ::RESET_REASON_POWER_MANAGEMENT: return IResetReason::POWER_MANAGEMENT; + case ::RESET_REASON_POWER_DOWN: return IResetReason::POWER_DOWN; + case ::RESET_REASON_POWER_BROWNOUT: return IResetReason::POWER_BROWNOUT; + case ::RESET_REASON_WATCHDOG: return IResetReason::WATCHDOG; + case ::RESET_REASON_UPDATE: return IResetReason::UPDATE; + case ::RESET_REASON_UPDATE_ERROR: return IResetReason::UPDATE_ERROR; + case ::RESET_REASON_UPDATE_TIMEOUT: return IResetReason::UPDATE_TIMEOUT; + case ::RESET_REASON_FACTORY_RESET: return IResetReason::FACTORY_RESET; + case ::RESET_REASON_SAFE_MODE: return IResetReason::SAFE_MODE; + case ::RESET_REASON_DFU_MODE: return IResetReason::DFU_MODE; + case ::RESET_REASON_PANIC: return IResetReason::PANIC; + case ::RESET_REASON_USER: return IResetReason::USER; + // Note: RESET_REASON_CONFIG_UPDATE might not be a standard Particle reason code. Check documentation. + // If it's not standard, remove or map appropriately. + // case ::RESET_REASON_CONFIG_UPDATE: return IResetReason::CONFIG_UPDATE; // Remove if not standard + case ::RESET_REASON_UNKNOWN: // Particle's unknown/default + default: + // Map Particle's 0 (NONE) or others to UNKNOWN + return IResetReason::UNKNOWN; } } + uint32_t ParticleSystem::freeMemory() { return System.freeMemory(); } -// Implementation for the sleep function with all wakeup reasons -IWakeupReason ParticleSystem::sleep(const particle::SystemSleepConfiguration& config) { - // Call the actual Particle function - SystemSleepResult result = System.sleep(config); +// *** Updated sleep implementation *** +IWakeupReason ParticleSystem::sleep(const ISleepConfig& iConfig) { + // 1. Create the Particle-specific configuration object + particle::SystemSleepConfiguration particleConfig; + + // 2. Translate from ISleepConfig (interface) to particleConfig (Particle) + // Translate Mode + switch(iConfig.mode) { + case ISleepMode::ULTRA_LOW_POWER: + particleConfig.mode(SystemSleepMode::ULTRA_LOW_POWER); + break; + case ISleepMode::STOP: + particleConfig.mode(SystemSleepMode::STOP); + break; + case ISleepMode::HIBERNATE: + particleConfig.mode(SystemSleepMode::HIBERNATE); + break; + // Add other ISleepMode cases -> SystemSleepMode mapping if needed + default: + Log.warn("Unsupported ISleepMode passed, defaulting to ULP"); + particleConfig.mode(SystemSleepMode::ULTRA_LOW_POWER); // Default? + break; + } + + // Translate Duration + if (iConfig.duration.count() > 0) { + particleConfig.duration(iConfig.duration); + } + + // Translate Wake Pin + // Assuming 0xFFFF was used as the "invalid/not set" marker in ISleepConfig + if (iConfig.wakePin != 0xFFFF) { + // Translate IInterruptMode (interface) to Particle's InterruptMode + InterruptMode particleInterruptMode; + switch(iConfig.wakePinMode) { + case IInterruptMode::FALLING: particleInterruptMode = FALLING; break; + case IInterruptMode::RISING: particleInterruptMode = RISING; break; + case IInterruptMode::CHANGE: particleInterruptMode = CHANGE; break; + case IInterruptMode::NONE: + default: + Log.error("Unsupported IInterruptMode for wake pin %u, defaulting to CHANGE.", iConfig.wakePin); + particleInterruptMode = CHANGE; // Default? + break; + } + particleConfig.gpio(iConfig.wakePin, particleInterruptMode); + } + + // Translate Network Interface setting + switch(iConfig.network) { + case INetworkInterfaceIndex::CELLULAR: + particleConfig.network(NETWORK_INTERFACE_CELLULAR); // Basic keep alive + // particleConfig.network(NETWORK_INTERFACE_CELLULAR, SystemSleepNetworkFlag::INACTIVE_STANDBY); // Alternative for lower power standby + break; + case INetworkInterfaceIndex::WIFI: + particleConfig.network(NETWORK_INTERFACE_WIFI_AP); + break; + case INetworkInterfaceIndex::ETHERNET: + particleConfig.network(NETWORK_INTERFACE_ETHERNET); + break; + // case INetworkInterfaceIndex::ALL: // Ambiguous, map carefully if needed + // particleConfig.network(NETWORK_INTERFACE_CELLULAR); + // particleConfig.network(NETWORK_INTERFACE_WIFI); // Example + // break; + case INetworkInterfaceIndex::NONE: + case INetworkInterfaceIndex::LOOPBACK: // Loopback doesn't make sense for sleep network config + default: + // Default is often network off, so potentially do nothing here, + // or explicitly ensure it's off if Particle API requires it. + break; + } - // Translate Particle's SystemSleepWakeupReason to our IWakeupReason + // Translate other flags from ISleepConfig if added... + // e.g., if (iConfig.waitForCloud) { particleConfig.flag(SystemSleepFlag::WAIT_CLOUD); } + + + // 3. Call the actual Particle function with the configured Particle object + SystemSleepResult result = System.sleep(particleConfig); + + // 4. Translate Particle's SystemSleepWakeupReason back to our IWakeupReason switch (result.wakeupReason()) { + // Map all defined reasons + case SystemSleepWakeupReason::BY_GPIO: return IWakeupReason::BY_GPIO; + case SystemSleepWakeupReason::BY_ADC: return IWakeupReason::BY_ADC; + case SystemSleepWakeupReason::BY_DAC: return IWakeupReason::BY_DAC; + case SystemSleepWakeupReason::BY_RTC: return IWakeupReason::BY_RTC; + case SystemSleepWakeupReason::BY_LPCOMP: return IWakeupReason::BY_LPCOMP; + case SystemSleepWakeupReason::BY_USART: return IWakeupReason::BY_USART; + case SystemSleepWakeupReason::BY_I2C: return IWakeupReason::BY_I2C; + case SystemSleepWakeupReason::BY_SPI: return IWakeupReason::BY_SPI; + case SystemSleepWakeupReason::BY_TIMER: return IWakeupReason::BY_TIMER; + case SystemSleepWakeupReason::BY_CAN: return IWakeupReason::BY_CAN; + case SystemSleepWakeupReason::BY_USB: return IWakeupReason::BY_USB; + case SystemSleepWakeupReason::BY_BLE: return IWakeupReason::BY_BLE; + case SystemSleepWakeupReason::BY_NFC: return IWakeupReason::BY_NFC; + case SystemSleepWakeupReason::BY_NETWORK: return IWakeupReason::BY_NETWORK; case SystemSleepWakeupReason::UNKNOWN: - return IWakeupReason::UNKNOWN; - case SystemSleepWakeupReason::BY_GPIO: - return IWakeupReason::BY_GPIO; - case SystemSleepWakeupReason::BY_ADC: - return IWakeupReason::BY_ADC; - case SystemSleepWakeupReason::BY_DAC: - return IWakeupReason::BY_DAC; - case SystemSleepWakeupReason::BY_RTC: - return IWakeupReason::BY_RTC; - case SystemSleepWakeupReason::BY_LPCOMP: - return IWakeupReason::BY_LPCOMP; - case SystemSleepWakeupReason::BY_USART: - return IWakeupReason::BY_USART; - case SystemSleepWakeupReason::BY_I2C: - return IWakeupReason::BY_I2C; - case SystemSleepWakeupReason::BY_SPI: - return IWakeupReason::BY_SPI; - case SystemSleepWakeupReason::BY_TIMER: - return IWakeupReason::BY_TIMER; - case SystemSleepWakeupReason::BY_CAN: - return IWakeupReason::BY_CAN; - case SystemSleepWakeupReason::BY_USB: - return IWakeupReason::BY_USB; - case SystemSleepWakeupReason::BY_BLE: - return IWakeupReason::BY_BLE; - case SystemSleepWakeupReason::BY_NFC: - return IWakeupReason::BY_NFC; - case SystemSleepWakeupReason::BY_NETWORK: - return IWakeupReason::BY_NETWORK; default: - // You might also want to check result.error() here if needed + // Check error code for more info? + if (result.error() != SYSTEM_ERROR_NONE) { + Log.error("Sleep returned error code: %d", result.error()); + } return IWakeupReason::UNKNOWN; } } diff --git a/src/platform/ParticleSystem.h b/src/platform/ParticleSystem.h index a52c406..c60e222 100644 --- a/src/platform/ParticleSystem.h +++ b/src/platform/ParticleSystem.h @@ -13,9 +13,8 @@ class ParticleSystem : public ISystem { IResetReason resetReason() override; uint32_t freeMemory() override; - // Update the override signature to return IWakeupReason - IWakeupReason sleep(const particle::SystemSleepConfiguration& config) override; - + // Updated sleep signature to use ISleepConfig from the interface + IWakeupReason sleep(const ISleepConfig& config) override; }; #endif // PARTICLE_SYSTEM_H \ No newline at end of file From 772db28917b48092defe5c20a8dc926e2c875fcb Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 2 Apr 2025 16:26:07 -0500 Subject: [PATCH 15/48] added Iwire refactoring --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-platform-dependencies | 2 +- src/FlightControl_Demo.cpp | 3 +++ src/platform/ParticleWire.cpp | 10 +++++++++ src/platform/ParticleWire.h | 28 +++++++++++++++++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/platform/ParticleWire.cpp create mode 100644 src/platform/ParticleWire.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 49ffa30..0ccfddc 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 49ffa300a2787c23827bcd01c4c4ee0247724420 +Subproject commit 0ccfddce396330e948ec6d7bd1edafb5e1f449ab diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index 31c0954..f6f4f3c 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit 31c0954fe6df483f3f7487b4cf2ad93310abc006 +Subproject commit f6f4f3ca991d45dbcd4403a54e3ab934b305d4fa diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index a2a944e..dd66604 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -62,6 +62,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "platform/ParticleTimeProvider.h" #include "platform/ParticleGpio.h" #include "platform/ParticleSystem.h" +#include "platform/ParticleWire.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -74,10 +75,12 @@ int powerSaveMode = 0; //Default to 0, update when configure power save mode is ParticleTimeProvider realTimeProvider; ParticleGpio realGpio; ParticleSystem realSystem; +ParticleWire realWire; Kestrel logger(realTimeProvider, realGpio, realSystem, + realWire, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/platform/ParticleWire.cpp b/src/platform/ParticleWire.cpp new file mode 100644 index 0000000..b23928a --- /dev/null +++ b/src/platform/ParticleWire.cpp @@ -0,0 +1,10 @@ +#include "ParticleWire.h" + +void ParticleWire::begin(){Wire.begin();} +void ParticleWire::setClock(uint32_t speed){Wire.setClock(speed);} +bool ParticleWire::isEnabled(){return Wire.isEnabled();} +void ParticleWire::beginTransmission(int value){Wire.beginTransmission(value);} +uint8_t ParticleWire::endTransmission(){return Wire.endTransmission();} +size_t ParticleWire::write(uint8_t value){return Wire.write(value);} +int ParticleWire::reset(){return Wire.reset();} + diff --git a/src/platform/ParticleWire.h b/src/platform/ParticleWire.h new file mode 100644 index 0000000..9bb8399 --- /dev/null +++ b/src/platform/ParticleWire.h @@ -0,0 +1,28 @@ +// src/platform/ParticleWire.h +#ifndef PARTICLE_WIRE_H +#define PARTICLE_WIRE_H + +#include "IWire.h" // Include the interface definition +#include "Particle.h" // Include the actual Particle header HERE + +/** + * @brief Concrete implementation of IWire using Particle API. + */ +class ParticleWire: public IWire { +public: + // Constructor/Destructor (often default is fine) + ParticleWire() = default; + ~ParticleWire() override = default; + + // Implement methods from IWire + void begin() override; + void setClock(uint32_t speed) override; + bool isEnabled() override; + void beginTransmission(int) override; + uint8_t endTransmission() override; + size_t write(uint8_t) override; + int reset() override; + +}; + +#endif // PARTICLE_WIRE_H \ No newline at end of file From 9ba64ac726e8e2d6367af702aee8bda58088c85b Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 3 Apr 2025 11:14:09 -0500 Subject: [PATCH 16/48] added cloud interface --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-platform-dependencies | 2 +- src/FlightControl_Demo.cpp | 3 +++ src/platform/ParticleCloud.cpp | 11 ++++++++++ src/platform/ParticleCloud.h | 27 +++++++++++++++++++++++++ src/platform/ParticleSystem.cpp | 6 +++++- src/platform/ParticleSystem.h | 1 + 7 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/platform/ParticleCloud.cpp create mode 100644 src/platform/ParticleCloud.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 0ccfddc..88c2413 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 0ccfddce396330e948ec6d7bd1edafb5e1f449ab +Subproject commit 88c24132c896de8d3a40ac0fd66eb10790c8cf27 diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index f6f4f3c..0aac441 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit f6f4f3ca991d45dbcd4403a54e3ab934b305d4fa +Subproject commit 0aac4412a16674da8151fecb32f552ada0fe54f4 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index dd66604..f0ffcb8 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -63,6 +63,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "platform/ParticleGpio.h" #include "platform/ParticleSystem.h" #include "platform/ParticleWire.h" +#include "platform/ParticleCloud.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -76,11 +77,13 @@ ParticleTimeProvider realTimeProvider; ParticleGpio realGpio; ParticleSystem realSystem; ParticleWire realWire; +ParticleCloud realCloud; Kestrel logger(realTimeProvider, realGpio, realSystem, realWire, + realCloud, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/platform/ParticleCloud.cpp b/src/platform/ParticleCloud.cpp new file mode 100644 index 0000000..989fb92 --- /dev/null +++ b/src/platform/ParticleCloud.cpp @@ -0,0 +1,11 @@ +#include "ParticleCloud.h" + +void ParticleCloud::connect() {Particle.connect();} +void ParticleCloud::disconnect(const ICloudDisconnectOptions& options) { + Particle.disconnect(CloudDisconnectOptions().graceful(options.graceful).timeout(options.timeout)); +} +bool ParticleCloud::connected() {return Particle.connected();} +bool ParticleCloud::syncTime() {return Particle.syncTime();} +bool ParticleCloud::syncTimePending() {return Particle.syncTimePending();} +bool ParticleCloud::syncTimeDone() {return Particle.syncTimeDone();} +bool ParticleCloud::process() {return Particle.process();} \ No newline at end of file diff --git a/src/platform/ParticleCloud.h b/src/platform/ParticleCloud.h new file mode 100644 index 0000000..d9575e9 --- /dev/null +++ b/src/platform/ParticleCloud.h @@ -0,0 +1,27 @@ +// src/platform/ParticleCloud.h +#ifndef PARTICLE_CLOUD_H +#define PARTICLE_CLOUD_H + +#include "ICloud.h" // Include the interface definition +#include "Particle.h" // Include the actual Particle header HERE + +/** + * @brief Concrete implementation of ICloud for Particle platform + */ +class ParticleCloud : public ICloud { +public: + // Constructor/Destructor + ParticleCloud() = default; + ~ParticleCloud() override = default; + + // Implement methods from ICloud + void connect() override; + void disconnect(const ICloudDisconnectOptions& options) override; + bool connected() override; + bool syncTime() override; + bool syncTimePending() override; + bool syncTimeDone() override; + bool process() override; +}; + +#endif // PARTICLE_CLOUD_H \ No newline at end of file diff --git a/src/platform/ParticleSystem.cpp b/src/platform/ParticleSystem.cpp index 45d0038..a726900 100644 --- a/src/platform/ParticleSystem.cpp +++ b/src/platform/ParticleSystem.cpp @@ -57,6 +57,11 @@ uint32_t ParticleSystem::freeMemory() { return System.freeMemory(); } +bool ParticleSystem::waitForCondition(std::function condition, std::chrono::milliseconds timeout) { + // Use Particle's System.waitCondition() instead of the waitFor macro + return System.waitCondition([&condition]{ return condition(); }, timeout.count()); +} + // *** Updated sleep implementation *** IWakeupReason ParticleSystem::sleep(const ISleepConfig& iConfig) { // 1. Create the Particle-specific configuration object @@ -95,7 +100,6 @@ IWakeupReason ParticleSystem::sleep(const ISleepConfig& iConfig) { case IInterruptMode::FALLING: particleInterruptMode = FALLING; break; case IInterruptMode::RISING: particleInterruptMode = RISING; break; case IInterruptMode::CHANGE: particleInterruptMode = CHANGE; break; - case IInterruptMode::NONE: default: Log.error("Unsupported IInterruptMode for wake pin %u, defaulting to CHANGE.", iConfig.wakePin); particleInterruptMode = CHANGE; // Default? diff --git a/src/platform/ParticleSystem.h b/src/platform/ParticleSystem.h index c60e222..2407d0b 100644 --- a/src/platform/ParticleSystem.h +++ b/src/platform/ParticleSystem.h @@ -12,6 +12,7 @@ class ParticleSystem : public ISystem { void on(IEventType event, SystemEventHandler handler) override; IResetReason resetReason() override; uint32_t freeMemory() override; + bool waitForCondition(std::function condition, std::chrono::milliseconds timeout) override; // Updated sleep signature to use ISleepConfig from the interface IWakeupReason sleep(const ISleepConfig& config) override; From 62bd27ce5593ba700d229ed1cf958d409517bfe5 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 3 Apr 2025 13:51:06 -0500 Subject: [PATCH 17/48] added Serial abstraction refactoring --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-platform-dependencies | 2 +- src/FlightControl_Demo.cpp | 5 + src/platform/ParticleSerial.cpp | 131 ++++++++++++++++++++++++ src/platform/ParticleSerial.h | 63 ++++++++++++ 5 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/platform/ParticleSerial.cpp create mode 100644 src/platform/ParticleSerial.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 88c2413..065cbd7 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 88c24132c896de8d3a40ac0fd66eb10790c8cf27 +Subproject commit 065cbd727e29d52beae44c0db025fcbf7f1e4e3a diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index 0aac441..183167f 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit 0aac4412a16674da8151fecb32f552ada0fe54f4 +Subproject commit 183167f55afbe499f67b8ad22292a9c12602bdc6 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index f0ffcb8..3727887 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -64,6 +64,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "platform/ParticleSystem.h" #include "platform/ParticleWire.h" #include "platform/ParticleCloud.h" +#include "platform/ParticleSerial.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -78,12 +79,16 @@ ParticleGpio realGpio; ParticleSystem realSystem; ParticleWire realWire; ParticleCloud realCloud; +ParticleUSBSerial realSerialDebug; +ParticleHardwareSerial realSerialSdi12; Kestrel logger(realTimeProvider, realGpio, realSystem, realWire, realCloud, + realSerialDebug, + realSerialSdi12, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/platform/ParticleSerial.cpp b/src/platform/ParticleSerial.cpp new file mode 100644 index 0000000..a4bf6b2 --- /dev/null +++ b/src/platform/ParticleSerial.cpp @@ -0,0 +1,131 @@ +/** + * @file ParticleSerial.cpp + * @brief Particle implementation of ISerial interface + */ + +#include "ParticleSerial.h" +#include "Particle.h" + +// ParticleUSBSerial implementation (Serial) + +void ParticleUSBSerial::begin(long speed) { + Serial.begin(speed); +} + +size_t ParticleUSBSerial::print(const char* str) { + return Serial.print(str); +} + +size_t ParticleUSBSerial::print(int value) { + return Serial.print(value); +} + +size_t ParticleUSBSerial::print(uint32_t value) { + return Serial.print(value); +} + +size_t ParticleUSBSerial::print(time_t value) { + return Serial.print(value); +} + +size_t ParticleUSBSerial::print(unsigned int value, int base) { + return Serial.print(value, base); +} + +size_t ParticleUSBSerial::print(float value) { + return Serial.print(value); +} + +size_t ParticleUSBSerial::print(double value) { + return Serial.print(value); +} + +size_t ParticleUSBSerial::println() { + return Serial.println(); +} + +size_t ParticleUSBSerial::println(const char* str) { + return Serial.println(str); +} + +size_t ParticleUSBSerial::println(int value) { + return Serial.println(value); +} + +size_t ParticleUSBSerial::println(uint32_t value) { + return Serial.println(value); +} + +size_t ParticleUSBSerial::println(time_t value) { + return Serial.println(value); +} + +size_t ParticleUSBSerial::println(unsigned int value, int base) { + return Serial.println(value, base); +} + +void ParticleUSBSerial::flush() { + Serial.flush(); +} + +// ParticleHardwareSerial implementation (Serial1) + +void ParticleHardwareSerial::begin(unsigned long speed, uint32_t config) { + Serial1.begin(speed, config); +} + +size_t ParticleHardwareSerial::print(const char* str) { + return Serial1.print(str); +} + +size_t ParticleHardwareSerial::print(int value) { + return Serial1.print(value); +} + +size_t ParticleHardwareSerial::print(uint32_t value) { + return Serial1.print(value); +} + +size_t ParticleHardwareSerial::print(time_t value) { + return Serial1.print(value); +} + +size_t ParticleHardwareSerial::print(unsigned int value, int base) { + return Serial1.print(value, base); +} + +size_t ParticleHardwareSerial::print(float value) { + return Serial1.print(value); +} + +size_t ParticleHardwareSerial::print(double value) { + return Serial1.print(value); +} + +size_t ParticleHardwareSerial::println() { + return Serial1.println(); +} + +size_t ParticleHardwareSerial::println(const char* str) { + return Serial1.println(str); +} + +size_t ParticleHardwareSerial::println(int value) { + return Serial1.println(value); +} + +size_t ParticleHardwareSerial::println(uint32_t value) { + return Serial1.println(value); +} + +size_t ParticleHardwareSerial::println(time_t value) { + return Serial1.println(value); +} + +size_t ParticleHardwareSerial::println(unsigned int value, int base) { + return Serial1.println(value, base); +} + +void ParticleHardwareSerial::flush() { + Serial1.flush(); +} \ No newline at end of file diff --git a/src/platform/ParticleSerial.h b/src/platform/ParticleSerial.h new file mode 100644 index 0000000..4cb7a6c --- /dev/null +++ b/src/platform/ParticleSerial.h @@ -0,0 +1,63 @@ +/** + * @file ParticleSerial.h + * @brief Particle implementation of ISerial interface + */ + +#ifndef __PARTICLE_SERIAL_H +#define __PARTICLE_SERIAL_H + +#include "../../lib/FlightControl-platform-dependencies/src/ISerial.h" + +/** + * @brief Particle Serial implementation for Serial (USB) + */ +class ParticleUSBSerial : public ISerial { +public: + ParticleUSBSerial() = default; + virtual ~ParticleUSBSerial() = default; + + void begin(long speed) override; + void begin(unsigned long speed, uint32_t config) override { begin((long)speed); } + size_t print(const char* str) override; + size_t print(int value) override; + size_t print(uint32_t value) override; + size_t print(time_t value) override; + size_t print(unsigned int value, int base = 10) override; + size_t print(float value) override; + size_t print(double value) override; + size_t println() override; + size_t println(const char* str) override; + size_t println(int value) override; + size_t println(uint32_t value) override; + size_t println(time_t value) override; + size_t println(unsigned int value, int base = 10) override; + void flush() override; +}; + +/** + * @brief Particle Serial implementation for Serial1 (Hardware UART) + */ +class ParticleHardwareSerial : public ISerial { +public: + ParticleHardwareSerial() = default; + virtual ~ParticleHardwareSerial() = default; + + void begin(unsigned long speed, uint32_t config) override; + void begin(long speed) override { begin((unsigned long)speed, 0); } + size_t print(const char* str) override; + size_t print(int value) override; + size_t print(uint32_t value) override; + size_t print(time_t value) override; + size_t print(unsigned int value, int base = 10) override; + size_t print(float value) override; + size_t print(double value) override; + size_t println() override; + size_t println(const char* str) override; + size_t println(int value) override; + size_t println(uint32_t value) override; + size_t println(time_t value) override; + size_t println(unsigned int value, int base = 10) override; + void flush() override; +}; + +#endif // __PARTICLE_SERIAL_H \ No newline at end of file From b4e05b1528b236a5311f75f2231a7d33f32170af Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 3 Apr 2025 15:26:35 -0500 Subject: [PATCH 18/48] added mocks for hardware dependencies --- lib/Driver_-_Kestrel | 2 +- test/mocks/HARDWARE_INTERFACES.md | 215 ++++++++++++++++ test/mocks/MockCloud.h | 22 ++ test/mocks/MockGpio.h | 18 ++ test/mocks/MockParticle.h | 105 -------- test/mocks/MockSerial.h | 31 +++ test/mocks/MockSystem.h | 20 ++ test/mocks/MockWire.h | 22 ++ test/unit/Driver_-_Kestrel/KestrelTest.cpp | 280 +++++++++++---------- 9 files changed, 477 insertions(+), 238 deletions(-) create mode 100644 test/mocks/HARDWARE_INTERFACES.md create mode 100644 test/mocks/MockCloud.h create mode 100644 test/mocks/MockGpio.h delete mode 100644 test/mocks/MockParticle.h create mode 100644 test/mocks/MockSerial.h create mode 100644 test/mocks/MockSystem.h create mode 100644 test/mocks/MockWire.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 065cbd7..f6e2cdd 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 065cbd727e29d52beae44c0db025fcbf7f1e4e3a +Subproject commit f6e2cdde204c8f97aa78761511cf71348d16032d diff --git a/test/mocks/HARDWARE_INTERFACES.md b/test/mocks/HARDWARE_INTERFACES.md new file mode 100644 index 0000000..7bb5a27 --- /dev/null +++ b/test/mocks/HARDWARE_INTERFACES.md @@ -0,0 +1,215 @@ +# Hardware Interfaces and Mocks for Kestrel Testing + +## Current State + +The Kestrel class uses direct instantiation of hardware components, making them difficult to mock for unit testing. While we've successfully abstracted platform dependencies (TimeProvider, Gpio, System, etc.), hardware components remain tightly coupled. + +## Recommended Hardware Interface Abstractions + +To enable comprehensive unit testing, we should create interfaces for major hardware components. Here's a blueprint for implementing these interfaces: + +### 1. IO Expander Interface + +```cpp +// IPCAL9535A.h +class IPCAL9535A { +public: + virtual ~IPCAL9535A() = default; + virtual int begin() = 0; + virtual int pinMode(int Pin, uint8_t State) = 0; + virtual int digitalWrite(int Pin, bool State) = 0; + virtual int digitalRead(int Pin) = 0; + virtual void safeMode(int state) = 0; + // Add other required methods +}; +``` + +### 2. LED Driver Interface + +```cpp +// IPCA9634.h +class IPCA9634 { +public: + virtual ~IPCA9634() = default; + virtual int begin() = 0; + virtual int setOutputMode(PCA9634::OutputMode State) = 0; + virtual int setGroupMode(PCA9634::GroupMode State) = 0; + virtual int setOutputArray(PCA9634::PortState Val) = 0; + virtual int setBrightnessArray(float Brightness) = 0; + virtual int setGroupBlinkPeriod(uint16_t Period) = 0; + virtual int setGroupOnTime(uint16_t Period) = 0; + // Add other required methods +}; +``` + +### 3. RTC Interface + +```cpp +// IMCP79412.h +class IMCP79412 { +public: + virtual ~IMCP79412() = default; + virtual int begin(bool exOsc) = 0; + virtual time_t getTimeUnix() = 0; + virtual String getUUIDString() = 0; + virtual int setMode(MCP79412::Mode mode) = 0; + virtual int enableAlarm(bool enable, uint8_t alarmNum) = 0; + // Add other required methods +}; +``` + +### 4. Current Sensor Interface + +```cpp +// IPAC1934.h +class IPAC1934 { +public: + virtual ~IPAC1934() = default; + virtual bool begin() = 0; + virtual void setAddress(uint8_t addr) = 0; + virtual void setFrequency(PAC1934::Frequency freq) = 0; + // Add other required methods +}; +``` + +### 5. GPS Interface + +```cpp +// ISFE_UBLOX_GNSS.h +class ISFE_UBLOX_GNSS { +public: + virtual ~ISFE_UBLOX_GNSS() = default; + virtual bool begin() = 0; + virtual void setI2COutput(uint8_t comType) = 0; + virtual bool setNavigationFrequency(uint8_t navFreq) = 0; + virtual void setAutoPVT(bool autoPVT) = 0; + virtual uint8_t getNavigationFrequency() = 0; + virtual uint8_t getMeasurementRate() = 0; + virtual uint8_t getNavigationRate() = 0; + virtual int16_t getATTroll() = 0; + virtual int16_t getATTpitch() = 0; + virtual int16_t getATTheading() = 0; + // Add other required methods +}; +``` + +## Implementation Strategy + +### Step 1: Create Interface Files + +Create interface files for each hardware component in a central location: +- `/lib/FlightControl-hardware-dependencies/src/` + +### Step 2: Modify Hardware Components + +Make each hardware class implement its corresponding interface: + +```cpp +// Existing class modifications +class PCAL9535A : public IPCAL9535A { + // Implementation remains unchanged +}; +``` + +### Step 3: Refactor Kestrel + +Update Kestrel's constructor to use interfaces instead of concrete implementations: + +```cpp +class Kestrel: public Sensor { +private: + IPCAL9535A& ioOB; + IPCAL9535A& ioTalon; + IPCA9634& led; + IPAC1934& csaAlpha; + IPAC1934& csaBeta; + IMCP79412& rtc; + ISFE_UBLOX_GNSS& gps; + // etc. + +public: + Kestrel(ITimeProvider& timeProvider, + IGpio& gpio, + ISystem& system, + IWire& wire, + ICloud& cloud, + ISerial& serialDebug, + ISerial& serialSdi12, + IPCAL9535A& ioOB, + IPCAL9535A& ioTalon, + IPCA9634& led, + IPAC1934& csaAlpha, + IPAC1934& csaBeta, + IMCP79412& rtc, + ISFE_UBLOX_GNSS& gps, + // etc. + bool useSensors = false); +}; +``` + +### Step 4: Create Factory Function + +To keep backward compatibility, provide a factory function: + +```cpp +// In Kestrel.cpp +Kestrel* createDefaultKestrel(ITimeProvider& timeProvider, + IGpio& gpio, + ISystem& system, + IWire& wire, + ICloud& cloud, + ISerial& serialDebug, + ISerial& serialSdi12, + bool useSensors = false) { + static PCAL9535A ioOB(0x20); + static PCAL9535A ioTalon(0x21); + static PCA9634 led(0x52); + // etc. + + return new Kestrel(timeProvider, gpio, system, wire, cloud, + serialDebug, serialSdi12, + ioOB, ioTalon, led, csaAlpha, csaBeta, rtc, gps, + useSensors); +} +``` + +### Step 5: Update Tests + +Modify the test file to use the interfaces: + +```cpp +TEST_F(KestrelTest, TestKestrelBegin) { + // Create Kestrel with all mocked dependencies + Kestrel kestrel(mockTimeProvider, mockGpio, mockSystem, mockWire, mockCloud, + mockSerialDebug, mockSerialSdi12, + mockIoOB, mockIoTalon, mockLed, mockCsaAlpha, mockCsaBeta, + mockRtc, mockGps, false); + + // Now you can test the full begin() method + bool criticalFault = false; + bool fault = false; + kestrel.begin(0, criticalFault, fault); + + // Assert expectations + EXPECT_FALSE(criticalFault); +} +``` + +## Benefits of This Approach + +1. **Full Unit Testing**: Enables testing the entire Kestrel class with mocked dependencies +2. **Isolation**: Tests become deterministic, not dependent on hardware +3. **Test Coverage**: Increases test coverage to include hardware interactions +4. **Maintainability**: Makes code more modular and easier to extend + +## Incremental Implementation + +This refactoring can be implemented incrementally: + +1. Start with the most critical components (PCAL9535A, MCP79412) +2. Add interfaces one at a time to limit disruption +3. Update tests progressively as interfaces are added + +## Conclusion + +By implementing interfaces for hardware components, we can achieve comprehensive unit testing for Kestrel. The platform dependency interfaces already demonstrate the value of this approach, and extending it to hardware components is a logical next step for maintaining a robust, testable codebase. \ No newline at end of file diff --git a/test/mocks/MockCloud.h b/test/mocks/MockCloud.h new file mode 100644 index 0000000..a1c6d94 --- /dev/null +++ b/test/mocks/MockCloud.h @@ -0,0 +1,22 @@ +#ifndef MOCK_CLOUD_H +#define MOCK_CLOUD_H + +#include +#include "ICloud.h" // Include the interface definition + +/** + * @brief Google Mock implementation of ICloud for testing. + */ +class MockCloud : public ICloud { +public: + // Mock all methods defined in the interface + MOCK_METHOD(void, connect, (), (override)); + MOCK_METHOD(void, disconnect, (const ICloudDisconnectOptions& options), (override)); + MOCK_METHOD(bool, connected, (), (override)); + MOCK_METHOD(bool, syncTime, (), (override)); + MOCK_METHOD(bool, syncTimePending, (), (override)); + MOCK_METHOD(bool, syncTimeDone, (), (override)); + MOCK_METHOD(bool, process, (), (override)); +}; + +#endif // MOCK_CLOUD_H \ No newline at end of file diff --git a/test/mocks/MockGpio.h b/test/mocks/MockGpio.h new file mode 100644 index 0000000..61f0c04 --- /dev/null +++ b/test/mocks/MockGpio.h @@ -0,0 +1,18 @@ +#ifndef MOCK_GPIO_H +#define MOCK_GPIO_H + +#include +#include "IGpio.h" // Include the interface definition + +/** + * @brief Google Mock implementation of IGpio for testing. + */ +class MockGpio : public IGpio { +public: + // Mock all methods defined in the interface + MOCK_METHOD(void, pinMode, (uint16_t pin, IPinMode mode), (override)); + MOCK_METHOD(void, digitalWrite, (uint16_t pin, uint8_t value), (override)); + MOCK_METHOD(int32_t, digitalRead, (uint16_t pin), (override)); +}; + +#endif // MOCK_GPIO_H \ No newline at end of file diff --git a/test/mocks/MockParticle.h b/test/mocks/MockParticle.h deleted file mode 100644 index d28dcf0..0000000 --- a/test/mocks/MockParticle.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef MOCK_PARTICLE_H -#define MOCK_PARTICLE_H - -#include -#include - -// Define system event type similar to Particle's definition -typedef int system_event_t; - -// Event type values -const int time_changed = 1; -const int out_of_memory = 2; - -// Mock String class for tests -class String { -public: - String() : value_("") {} - String(const char* str) : value_(str ? str : "") {} - String(const std::string& str) : value_(str) {} - String(int val) : value_(std::to_string(val)) {} - String(long val) : value_(std::to_string(val)) {} - String(float val, int decimalPlaces = 2) : value_(std::to_string(val)) {} - - bool equals(const String& other) const { - return value_ == other.value_; - } - - String substring(size_t beginIndex) const { - return value_.substr(beginIndex); - } - - String substring(size_t beginIndex, size_t endIndex) const { - return value_.substr(beginIndex, endIndex - beginIndex); - } - - size_t length() const { - return value_.length(); - } - - const char* c_str() const { - return value_.c_str(); - } - - bool operator==(const String& other) const { - return value_ == other.value_; - } - - bool operator==(const char* other) const { - return value_ == other; - } - - String operator+(const String& other) const { - return String(value_ + other.value_); - } - - String operator+(const char* other) const { - return String(value_ + other); - } - -private: - std::string value_; -}; - -// For gtest to print Strings in a readable way -inline std::ostream& operator<<(std::ostream& os, const String& str) { - return os << str.c_str(); -} - -// Mock for Particle System functionality -class MockSystemClass { -public: - MOCK_METHOD(void, on, (int event, void (*handler)(system_event_t, int))); - MOCK_METHOD(uint32_t, resetReason, ()); - MOCK_METHOD(bool, connected, ()); -}; - -// Mock for Wire/I2C functionality -class MockTwoWire { -public: - MOCK_METHOD(bool, isEnabled, ()); - MOCK_METHOD(void, begin, ()); - MOCK_METHOD(void, setClock, (uint32_t)); - MOCK_METHOD(void, beginTransmission, (uint8_t)); - MOCK_METHOD(uint8_t, endTransmission, (bool)); - MOCK_METHOD(uint8_t, requestFrom, (uint8_t, uint8_t, bool)); - MOCK_METHOD(int, available, ()); - MOCK_METHOD(int, read, ()); - MOCK_METHOD(size_t, write, (uint8_t)); - MOCK_METHOD(size_t, write, (const uint8_t*, size_t)); -}; - -// Mock EEPROM namespace -namespace EEPROM { - template - void get(int addr, T& val) { - val = T(); // Default construct the value - } -} - -// Define platform IDs -#define PLATFORM_BSOM 23 -#define PLATFORM_B5SOM 32 -#define PLATFORM_ID PLATFORM_BSOM - -#endif // MOCK_PARTICLE_H \ No newline at end of file diff --git a/test/mocks/MockSerial.h b/test/mocks/MockSerial.h new file mode 100644 index 0000000..5261dd3 --- /dev/null +++ b/test/mocks/MockSerial.h @@ -0,0 +1,31 @@ +#ifndef MOCK_SERIAL_H +#define MOCK_SERIAL_H + +#include +#include "ISerial.h" // Include the interface definition + +/** + * @brief Google Mock implementation of ISerial for testing. + */ +class MockSerial : public ISerial { +public: + // Mock all methods defined in the interface + MOCK_METHOD(void, begin, (long speed), (override)); + MOCK_METHOD(void, begin, (unsigned long speed, uint32_t config), (override)); + MOCK_METHOD(size_t, print, (const char* str), (override)); + MOCK_METHOD(size_t, print, (int value), (override)); + MOCK_METHOD(size_t, print, (uint32_t value), (override)); + MOCK_METHOD(size_t, print, (time_t value), (override)); + MOCK_METHOD(size_t, print, (unsigned int value, int base), (override)); + MOCK_METHOD(size_t, print, (float value), (override)); + MOCK_METHOD(size_t, print, (double value), (override)); + MOCK_METHOD(size_t, println, (), (override)); + MOCK_METHOD(size_t, println, (const char* str), (override)); + MOCK_METHOD(size_t, println, (int value), (override)); + MOCK_METHOD(size_t, println, (uint32_t value), (override)); + MOCK_METHOD(size_t, println, (time_t value), (override)); + MOCK_METHOD(size_t, println, (unsigned int value, int base), (override)); + MOCK_METHOD(void, flush, (), (override)); +}; + +#endif // MOCK_SERIAL_H \ No newline at end of file diff --git a/test/mocks/MockSystem.h b/test/mocks/MockSystem.h new file mode 100644 index 0000000..9831d94 --- /dev/null +++ b/test/mocks/MockSystem.h @@ -0,0 +1,20 @@ +#ifndef MOCK_SYSTEM_H +#define MOCK_SYSTEM_H + +#include +#include "ISystem.h" // Include the interface definition + +/** + * @brief Google Mock implementation of ISystem for testing. + */ +class MockSystem : public ISystem { +public: + // Mock all methods defined in the interface + MOCK_METHOD(void, on, (IEventType event, SystemEventHandler handler), (override)); + MOCK_METHOD(IResetReason, resetReason, (), (override)); + MOCK_METHOD(uint32_t, freeMemory, (), (override)); + MOCK_METHOD(bool, waitForCondition, (std::function condition, std::chrono::milliseconds timeout), (override)); + MOCK_METHOD(IWakeupReason, sleep, (const ISleepConfig& config), (override)); +}; + +#endif // MOCK_SYSTEM_H \ No newline at end of file diff --git a/test/mocks/MockWire.h b/test/mocks/MockWire.h new file mode 100644 index 0000000..7462f9a --- /dev/null +++ b/test/mocks/MockWire.h @@ -0,0 +1,22 @@ +#ifndef MOCK_WIRE_H +#define MOCK_WIRE_H + +#include +#include "IWire.h" // Include the interface definition + +/** + * @brief Google Mock implementation of IWire for testing. + */ +class MockWire : public IWire { +public: + // Mock all methods defined in the interface + MOCK_METHOD(void, begin, (), (override)); + MOCK_METHOD(void, setClock, (uint32_t speed), (override)); + MOCK_METHOD(bool, isEnabled, (), (override)); + MOCK_METHOD(void, beginTransmission, (int), (override)); + MOCK_METHOD(uint8_t, endTransmission, (), (override)); + MOCK_METHOD(size_t, write, (uint8_t), (override)); + MOCK_METHOD(int, reset, (), (override)); +}; + +#endif // MOCK_WIRE_H \ No newline at end of file diff --git a/test/unit/Driver_-_Kestrel/KestrelTest.cpp b/test/unit/Driver_-_Kestrel/KestrelTest.cpp index ee07a47..5b17790 100644 --- a/test/unit/Driver_-_Kestrel/KestrelTest.cpp +++ b/test/unit/Driver_-_Kestrel/KestrelTest.cpp @@ -4,45 +4,73 @@ // Include our mock headers first #include "Particle.h" #include "MockSensor.h" -//#include "PCAL9535A.h" // Uncomment if you need to use the actual PCAL9535A -//#include "MockPAC9634.h" // Uncomment if you need to use the actual PAC1934 +#include "MockPCAL9535A.h" +#include "MockPCA9634.h" #include "MockMCP79412.h" -//#include "MockSparkFun_u_blox_GNSS.h" // Uncomment if you need to use the actual SFE_UBLOX_GNSS -//#include "MockPAC1934.h" // Uncomment if you need to use the actual PAC1934 +#include "MockSFE_UBLOX_GNSS.h" #include "MockVEML3328.h" -//#include "MockAdafruit_SHT4x.h" // Uncomment if you need to use the actual Adafruit_SHT4x #include "MockMXC6655.h" -//#include "MockBMA456.h" // Uncomment if you need to use the actual BMA456 +// Include platform interface mocks +#include "MockTimeProvider.h" +#include "MockGpio.h" +#include "MockSystem.h" +#include "MockWire.h" +#include "MockCloud.h" +#include "MockSerial.h" // This must be included AFTER mocks to ensure the mocks are used // instead of actual hardware implementations #include "Kestrel.h" -// Forward declarations for other mocked dependencies needed -class MockSensor; -//class MockPCAL9535A; -//class MockPAC1934; // Uncomment if you need to use the actual PAC1934 -class MockMCP79412; -//class MockSFE_UBLOX_GNSS; // Uncomment if you need to use the actual SFE_UBLOX_GNSS -//class MockPAC1934; // Uncomment if you need to use the actual PAC1934 -class MockVEML3328; -//class MockAdafruit_SHT4x; // Uncomment if you need to use the actual Adafruit_SHT4x -class MockMXC6655; -//class MockBMA456; // Uncomment if you need to use the actual BMA456 - - // Test fixture class for Kestrel tests class KestrelTest : public ::testing::Test { protected: + // Our platform interface mocks + MockTimeProvider mockTimeProvider; + MockGpio mockGpio; + MockSystem mockSystem; + MockWire mockWire; + MockCloud mockCloud; + MockSerial mockSerialDebug; + MockSerial mockSerialSdi12; + + // Additional hardware component mocks that would need injection + MockPCAL9535A mockIoOB; + MockPCAL9535A mockIoTalon; + MockPCA9634 mockLed; + MockMCP79412 mockRtc; + MockSFE_UBLOX_GNSS mockGps; + MockVEML3328 mockAls; + MockMXC6655 mockAccel; + // Set up mocks and test objects void SetUp() override { - // Any test setup code goes here + // Set up default mock behavior here + EXPECT_CALL(mockWire, isEnabled()) + .WillRepeatedly(::testing::Return(false)); + + // Add more default behaviors as needed } void TearDown() override { // Any test cleanup code goes here } + + // Helper method to create a Kestrel instance with mocks + // Note: This only passes the platform interface mocks, not hardware component mocks + Kestrel createKestrel() { + return Kestrel( + mockTimeProvider, + mockGpio, + mockSystem, + mockWire, + mockCloud, + mockSerialDebug, + mockSerialSdi12, + false // useSensors + ); + } }; // Basic test to verify our testing framework works @@ -117,122 +145,110 @@ TEST_F(KestrelTest, TestMockVEML3328) { EXPECT_FLOAT_EQ(value, 123.45f); } - - - -// Kestrel begin tests - -// Verify Kestrel function calling works -TEST_F(KestrelTest, TestKestrelBegin) { - // Mock the components that Kestrel depends on - MockMXC6655 mockAccel; - - EXPECT_CALL(mockAccel, begin()) - .Times(1) - .WillOnce(::testing::Return(0)); // Return success - - EXPECT_CALL(mockAccel, getAccel(2, 0)) - .Times(1) - .WillOnce(::testing::Return(20.0f)); // Return success - - Kestrel kestrel(&mockAccel); - - bool result = kestrel.zeroAccel(false); // Reset accelerometer offsets +// Test platform interfaces in Kestrel +TEST_F(KestrelTest, TestKestrelPlatformInteractions) { + // Set up expectations for the Kestrel constructor and begin method + EXPECT_CALL(mockWire, begin()).Times(1); + EXPECT_CALL(mockWire, setClock(400000)).Times(1); + EXPECT_CALL(mockSystem, on(IEventType::TIME_CHANGED, ::testing::_)).Times(1); + EXPECT_CALL(mockSystem, on(IEventType::OUT_OF_MEMORY, ::testing::_)).Times(1); + EXPECT_CALL(mockSystem, resetReason()).WillOnce(::testing::Return(IResetReason::POWER_DOWN)); + + // Additional expectations for hardware components would go here + // but we can't easily mock them without refactoring Kestrel + + // Create a Kestrel instance with our mocked platform interfaces + Kestrel kestrel = createKestrel(); - // Verify the result and state - EXPECT_FALSE(result); + // This would initialize Kestrel, but we can't fully test it without hardware component mocks + // bool criticalFault = false; + // bool fault = false; + // String result = kestrel.begin(0, criticalFault, fault); + + // Instead, we can test isolated methods that primarily use platform interfaces + // For example, methods that use timeProvider, gpio, etc. } - // Verify Kestrel begin thows error with reset reason if init is not done - - // Verify Kestrel begin sets criticalFault tue if ioOb.begin() fails - - // Verify Kestrel begin sets criticalFault true if ioTalon.begin() fails - - // Verify Kestrel begin sets criticalFault true if csaAlpha.begin() fails TODO: NOT IMPLEMENTED - - // Verify Kestrel begin sets criticalFault true if csaBeta.begin() fails TODO: NOT IMPLEMENTED - - // Verify Kestrel begin sets criticalFault true if led.begin() fails TODO: NOT IMPLEMENTED - - // Verify Kestrel Begin sets led functions if init is not done - - // Verify Kestrel begin sets criticalFault true if rtc.begin() fails - - // Verify Kestel begin sets criticalFault true if gps.begin() fails - - // Verify Kestrel begin sets initDone true if init is done - -//Kestrel getErrors tests - -//Kestrel getData tests - -//Kestrel getMetadata tests - -//Kestrel selfDiagnostic tests - -//Kestrel updateLocation tests - -//Kestrel connectToCell tests - -//Kestrel enablePower tests - -//Kestrel enableData tests - -//Kestrel setDirection tests - -//Kestrel getFault tests - -//Kestrel enableI2C_OB tests - -//Kestrel enableI2C_Global tests - -//Kestrel enableI2C_External tests - -//Kestrel enableSD tests - -//Kestrel sdInserted tests - -//Kestrel enableAuxPower tests - -//Kestrel getTime tests - -//Kestrel syncTime tests - -//Kestrel startTimer tests - -//Kestrel waitUntilTimerDone tests - -//Kestrel getTimeString tests - -//kestrel totalErrors tests - -//Kestrel statLED tests - -//Kestrel setIndicatorState tests - -//Kestrel updateTime tests - -//Kestrel feedWDT tests - -//Kestrel releaseWDT tests - -//Kestrel getPosLat tests - -//Kestrel getPosLong tests - -//Kestrel getPosAlt tests - -//Kestrel getPosTime tests - -//Kestrel getPosTimeString tests - -//Kestrel configTalonSense tests - -//Kestrel getMessageID tests +// Test platform ITimeProvider interactions +TEST_F(KestrelTest, TestKestrelTimeOperations) { + // Set up expectations + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(1585699200)); // April 1, 2020 + + // Create Kestrel instance (note: this doesn't test begin()) + Kestrel kestrel = createKestrel(); + + // Test getTime() which should use mockTimeProvider + time_t time = kestrel.getTime(); + EXPECT_EQ(time, 1585699200); +} -//Kestrel testForBat tests +// Test GPIO interactions +TEST_F(KestrelTest, TestKestrelGpioOperations) { + // Set up expectations + EXPECT_CALL(mockGpio, pinMode(Pins::WD_HOLD, IPinMode::OUTPUT)).Times(1); + EXPECT_CALL(mockGpio, digitalWrite(Pins::WD_HOLD, 1)).Times(1); + + // Create Kestrel instance + Kestrel kestrel = createKestrel(); + + // Test feedWDT which should use mockGpio + bool result = kestrel.feedWDT(); + EXPECT_TRUE(result); +} -//Kestrel zeroAccel tests +// More tests would be added to cover other Kestrel methods +// that interact with the platform interfaces we've mocked +/* +// Remaining test outline (commented out since we can't fully implement these yet) +// Kestrel begin tests +// Verify Kestrel begin throws error with reset reason if init is not done +// Verify Kestrel begin sets criticalFault true if ioOb.begin() fails +// Verify Kestrel begin sets criticalFault true if ioTalon.begin() fails +// Verify Kestrel begin sets criticalFault true if csaAlpha.begin() fails +// Verify Kestrel begin sets criticalFault true if csaBeta.begin() fails +// Verify Kestrel begin sets criticalFault true if led.begin() fails +// Verify Kestrel Begin sets led functions if init is not done +// Verify Kestrel begin sets criticalFault true if rtc.begin() fails +// Verify Kestel begin sets criticalFault true if gps.begin() fails +// Verify Kestrel begin sets initDone true if init is done + +// Kestrel getErrors tests +// Kestrel getData tests +// Kestrel getMetadata tests +// Kestrel selfDiagnostic tests +// Kestrel updateLocation tests +// Kestrel connectToCell tests +// Kestrel enablePower tests +// Kestrel enableData tests +// Kestrel setDirection tests +// Kestrel getFault tests +// Kestrel enableI2C_OB tests +// Kestrel enableI2C_Global tests +// Kestrel enableI2C_External tests +// Kestrel enableSD tests +// Kestrel sdInserted tests +// Kestrel enableAuxPower tests +// Kestrel getTime tests +// Kestrel syncTime tests +// Kestrel startTimer tests +// Kestrel waitUntilTimerDone tests +// Kestrel getTimeString tests +// Kestrel totalErrors tests +// Kestrel statLED tests +// Kestrel setIndicatorState tests +// Kestrel updateTime tests +// Kestrel feedWDT tests +// Kestrel releaseWDT tests +// Kestrel getPosLat tests +// Kestrel getPosLong tests +// Kestrel getPosAlt tests +// Kestrel getPosTime tests +// Kestrel getPosTimeString tests +// Kestrel configTalonSense tests +// Kestrel getMessageID tests +// Kestrel testForBat tests +// Kestrel zeroAccel tests +*/ \ No newline at end of file From 1f4bc89dc6dd359593bdeb8fb7bedbb58b7df599 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 3 Apr 2025 17:49:37 -0500 Subject: [PATCH 19/48] wip on refactoring --- .gitmodules | 3 +++ lib/Driver_-_Sensor | 2 +- lib/FlightControl-hardware-dependencies | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 160000 lib/FlightControl-hardware-dependencies diff --git a/.gitmodules b/.gitmodules index f79521b..ed03299 100644 --- a/.gitmodules +++ b/.gitmodules @@ -131,3 +131,6 @@ [submodule "lib/FlightControl-platform-dependencies"] path = lib/FlightControl-platform-dependencies url = https://github.com/gemsiot/FlightControl-platform-dependencies.git +[submodule "lib/FlightControl-hardware-dependencies"] + path = lib/FlightControl-hardware-dependencies + url = https://github.com/gemsiot/FlightControl-hardware-dependencies diff --git a/lib/Driver_-_Sensor b/lib/Driver_-_Sensor index cd2873a..4d1113c 160000 --- a/lib/Driver_-_Sensor +++ b/lib/Driver_-_Sensor @@ -1 +1 @@ -Subproject commit cd2873af6756767b5d6b4ad41f54999757e8aeb8 +Subproject commit 4d1113c70c3e63fe537354c4862af44fe03e2d96 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies new file mode 160000 index 0000000..6c4917b --- /dev/null +++ b/lib/FlightControl-hardware-dependencies @@ -0,0 +1 @@ +Subproject commit 6c4917b5345b674325a1695e654ba51ab1deaa8a From fde3f2788c2d30982949832e259640932852fa9f Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 8 Apr 2025 10:35:05 -0500 Subject: [PATCH 20/48] adedd PCAL refactoring --- lib/Driver_-_Kestrel | 2 +- lib/Driver_-_Sensor | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 11 +- src/hardware/IOExpanderPCAL9535A.cpp | 165 ++++++++++++++++++++++++ src/hardware/IOExpanderPCAL9535A.h | 64 +++++++++ 6 files changed, 241 insertions(+), 5 deletions(-) create mode 100644 src/hardware/IOExpanderPCAL9535A.cpp create mode 100644 src/hardware/IOExpanderPCAL9535A.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index f6e2cdd..af0eb76 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit f6e2cdde204c8f97aa78761511cf71348d16032d +Subproject commit af0eb76a3c8e95737c91c7650451b2332f54178b diff --git a/lib/Driver_-_Sensor b/lib/Driver_-_Sensor index 4d1113c..f768bd1 160000 --- a/lib/Driver_-_Sensor +++ b/lib/Driver_-_Sensor @@ -1 +1 @@ -Subproject commit 4d1113c70c3e63fe537354c4862af44fe03e2d96 +Subproject commit f768bd1078e44152e3c921ba1f69018fd6356d86 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 6c4917b..49ff324 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 6c4917b5345b674325a1695e654ba51ab1deaa8a +Subproject commit 49ff32445e715b23ac21340696487bd24c3f1745 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 3727887..aabc40e 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -66,6 +66,8 @@ int configurePowerSave(int desiredPowerSaveMode); #include "platform/ParticleCloud.h" #include "platform/ParticleSerial.h" +#include "hardware/IOExpanderPCAL9535A.h" + const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -82,6 +84,9 @@ ParticleCloud realCloud; ParticleUSBSerial realSerialDebug; ParticleHardwareSerial realSerialSdi12; +IOExpanderPCAL9535A realIoOB(0x20); //0x20 is the PCAL Base address +IOExpanderPCAL9535A realIoTalon(0x20); + Kestrel logger(realTimeProvider, realGpio, realSystem, @@ -89,14 +94,16 @@ Kestrel logger(realTimeProvider, realCloud, realSerialDebug, realSerialSdi12, + realIoOB, + realIoTalon, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 AuxTalon aux(0, 0x14); //Instantiate AUX talon with deaults - null port and hardware v1.4 I2CTalon i2c(0, 0x21); //Instantiate I2C talon with alt - null port and hardware v2.1 SDI12Talon sdi12(0, 0x14); //Instantiate SDI12 talon with alt - null port and hardware v1.4 -PCAL9535A ioAlpha(0x20); -PCAL9535A ioBeta(0x21); +IOExpanderPCAL9535A ioAlpha(0x20); +IOExpanderPCAL9535A ioBeta(0x21); String globalNodeID = ""; //Store current node ID diff --git a/src/hardware/IOExpanderPCAL9535A.cpp b/src/hardware/IOExpanderPCAL9535A.cpp new file mode 100644 index 0000000..d184f46 --- /dev/null +++ b/src/hardware/IOExpanderPCAL9535A.cpp @@ -0,0 +1,165 @@ +/****************************************************************************** + * IOExpanderPCAL9535A.cpp + * Concrete implementation of IIOExpander for the PCAL9535A chip. + * Delegates calls to the PCAL9535A_Library driver. + * + * + * Distributed as-is; no warranty is given. + ******************************************************************************/ + + #include "IOExpanderPCAL9535A.h" + + // Include standard types/constants if not guaranteed by includes above + // (e.g., if IIOExpander.h doesn't define INPUT, OUTPUT etc.) + // #include // Or + + // --- Constructor --- + IOExpanderPCAL9535A::IOExpanderPCAL9535A(int address) + : pcal9535a(address) // Initialize the driver instance with the address + { + // Constructor body (if needed) + } + + // --- IIOExpander Interface Method Implementations --- + + int IOExpanderPCAL9535A::begin() { + // Address is typically set in the constructor for I2C devices. + // The driver's begin() usually initializes Wire and checks presence. + return pcal9535a.begin(); + } + + // --- Core IO --- + int IOExpanderPCAL9535A::pinMode(int Pin, uint8_t State, bool Port) { + return pcal9535a.pinMode(Pin, State, Port); + } + + int IOExpanderPCAL9535A::pinMode(int Pin, uint8_t State) { + return pcal9535a.pinMode(Pin, State); + } + + int IOExpanderPCAL9535A::digitalWrite(int Pin, bool State, bool Port) { + return pcal9535a.digitalWrite(Pin, State, Port); + } + + int IOExpanderPCAL9535A::digitalWrite(int Pin, bool State) { + return pcal9535a.digitalWrite(Pin, State); + } + + int IOExpanderPCAL9535A::digitalRead(int Pin, bool Port) { + return pcal9535a.digitalRead(Pin, Port); + } + + int IOExpanderPCAL9535A::digitalRead(int Pin) { + return pcal9535a.digitalRead(Pin); + } + + // --- Specific Features --- + + + int IOExpanderPCAL9535A::pinSetDriveStrength(int Pin, DriveStrength State, bool Port) { + return pcal9535a.pinSetDriveStrength(Pin, State, Port); + } + + int IOExpanderPCAL9535A::pinSetDriveStrength(int Pin, DriveStrength State) { + return pcal9535a.pinSetDriveStrength(Pin, State); + } + + + int IOExpanderPCAL9535A::setInterrupt(int Pin, bool State, bool Port) { + return pcal9535a.setInterrupt(Pin, State, Port); + } + + int IOExpanderPCAL9535A::setInterrupt(int Pin, bool State) { + return pcal9535a.setInterrupt(Pin, State); + } + + int IOExpanderPCAL9535A::getInterrupt(int Pin) { + return pcal9535a.getInterrupt(Pin); + } + + uint16_t IOExpanderPCAL9535A::getAllInterrupts(uint8_t Option) { + // Delegate using the mapped age/status value + return pcal9535a.getAllInterrupts(Option); + } + + uint16_t IOExpanderPCAL9535A::getInterruptMask() { + return pcal9535a.getInterruptMask(); + } + + unsigned int IOExpanderPCAL9535A::clearInterrupt(uint8_t age) { + // Delegate using the mapped age/status value + return pcal9535a.clearInterrupt(age); + } + + bool IOExpanderPCAL9535A::isInterrupt(uint8_t age) { + // Driver doesn't have direct isInterrupt, implement via getAllInterrupts + return (pcal9535a.getAllInterrupts(age) != 0); + } + + int IOExpanderPCAL9535A::setLatch(int Pin, bool State, bool Port) { + return pcal9535a.setLatch(Pin, State, Port); + } + + int IOExpanderPCAL9535A::setLatch(int Pin, bool State) { + return pcal9535a.setLatch(Pin, State); + } + + uint16_t IOExpanderPCAL9535A::getLatch() { + return pcal9535a.getLatch(); + } + + int IOExpanderPCAL9535A::setInputPolarity(int Pin, bool State, bool Port) { + return pcal9535a.setInputPolarity(Pin, State, Port); + } + + int IOExpanderPCAL9535A::setInputPolarity(int Pin, bool State) { + return pcal9535a.setInputPolarity(Pin, State); + } + + bool IOExpanderPCAL9535A::getInputPolarity(int Pin, bool Port) { + // Driver returns int (-1 error, 0/1 state), interface expects bool + int result = pcal9535a.getInputPolarity(Pin, Port); + // Consider result >= 0 for true if 0 is valid, or just result == 1? + // Assuming 1 means "polarity inverted enabled", 0 means "disabled" + return (result == 1); // Adjust if 0 means true for your interface + } + + bool IOExpanderPCAL9535A::getInputPolarity(int Pin) { + int result = pcal9535a.getInputPolarity(Pin); + return (result == 1); // Adjust as needed + } + + int IOExpanderPCAL9535A::setBusOutput(uint8_t mode, bool Port) { + // Assumes interface OutputType constants match driver expectations + return pcal9535a.setBusOutput(mode, Port); + } + + uint8_t IOExpanderPCAL9535A::getBusOutput() { + return 0; //not implemented in driver + } + + uint16_t IOExpanderPCAL9535A::readBus() { + return pcal9535a.readBus(); + } + + // --- Error handling --- + uint16_t IOExpanderPCAL9535A::getError() { + return pcal9535a.getError(); + } + + uint16_t IOExpanderPCAL9535A::clearError() { + return pcal9535a.clearError(); + } + + void IOExpanderPCAL9535A::safeMode(int state) { + // Assumes interface constants match driver's SAFE, SAFE1, etc. + pcal9535a.safeMode(state); + } + + uint16_t IOExpanderPCAL9535A::readWord(int Pos, int &Error) { + return pcal9535a.readWord(Pos, Error); + } + + int IOExpanderPCAL9535A::setIntPinConfig(int Pin, bool Latch) { + return pcal9535a.setIntPinConfig(Pin, Latch); +} \ No newline at end of file diff --git a/src/hardware/IOExpanderPCAL9535A.h b/src/hardware/IOExpanderPCAL9535A.h new file mode 100644 index 0000000..4f29e9d --- /dev/null +++ b/src/hardware/IOExpanderPCAL9535A.h @@ -0,0 +1,64 @@ +// src/hardware/IOExpanderPCAL9535A.h + +#ifndef IOEXPANDERPCAL9535A_H +#define IOEXPANDERPCAL9535A_H + +#include "../../lib/PCAL9535A_Library/src/PCAL9535A.h" //include the driver of PCAL9535A here +#include "IIOExpander.h" //include the interface of IIOExpander here + +/** + * * @breif Concrete implementation of IIOExpander for PCAL9535A + */ +class IOExpanderPCAL9535A : public IIOExpander { + public: + IOExpanderPCAL9535A(int _ADR = PCAL9535A_BASE_ADR); + ~IOExpanderPCAL9535A() = default; + + int begin() override; // Address argument ignored here, set in constructor + + // Core IO + int pinMode(int Pin, uint8_t State, bool Port) override; + int pinMode(int Pin, uint8_t State) override; + int digitalWrite(int Pin, bool State, bool Port) override; + int digitalWrite(int Pin, bool State) override; + int digitalRead(int Pin, bool Port) override; + int digitalRead(int Pin) override; + + // Specific Features + int pinSetDriveStrength(int Pin, DriveStrength State, bool Port) override; + int pinSetDriveStrength(int Pin, DriveStrength State) override; + + int setInterrupt(int Pin, bool State, bool Port) override; + int setInterrupt(int Pin, bool State) override; + int getInterrupt(int Pin) override; + uint16_t getAllInterrupts(uint8_t Option) override; + uint16_t getInterruptMask() override; + unsigned int clearInterrupt(uint8_t age) override; + bool isInterrupt(uint8_t age) override; + + int setLatch(int Pin, bool State, bool Port) override; + int setLatch(int Pin, bool State) override; + uint16_t getLatch() override; + + int setInputPolarity(int Pin, bool State, bool Port) override; + int setInputPolarity(int Pin, bool State) override; + bool getInputPolarity(int Pin, bool Port) override; + bool getInputPolarity(int Pin) override; + int setIntPinConfig(int Pin, bool Latch) override; + + int setBusOutput(uint8_t mode, bool Port) override; + uint8_t getBusOutput() override; // Driver returns uint8_t, maybe add to interface later + + uint16_t readBus() override; + + // Error handling + uint16_t getError() override; + uint16_t clearError() override; + void safeMode(int state) override; + + uint16_t readWord(int Pos, int &Error) override; + private: + PCAL9535A pcal9535a; +}; + +#endif // IOEXPANDERPCAL9535A_H \ No newline at end of file From 45f6c6f24d4a8c0da66ac48a6337ba6266187160 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 9 Apr 2025 14:20:41 -0500 Subject: [PATCH 21/48] refactored li710 for unit testing missed a 0x21 instead of 0x20 updated Li710 tests for the direct measuremetn refactor removed haar for testing put haar back in removed haar corectly this time replaced the adr in the diag str fixed observed li710 issues --- lib/Driver_-_Li710 | 2 +- lib/Driver_-_Sensor | 2 +- lib/Driver_-_Talon-SDI12 | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 14 +- src/hardware/IOExpanderPCAL9535A.cpp | 6 +- src/hardware/SDI12TalonAdapter.h | 54 +++ test/CMakeLists.txt | 12 +- test/mocks/MockSDI12Talon.h | 39 +++ test/mocks/Particle.h | 389 ++++++++++++++++++++++ test/unit/Driver_-_Li710/Li710Test.cpp | 423 ++++++++++++++++++++++++ 11 files changed, 927 insertions(+), 18 deletions(-) create mode 100644 src/hardware/SDI12TalonAdapter.h create mode 100644 test/mocks/MockSDI12Talon.h create mode 100644 test/mocks/Particle.h create mode 100644 test/unit/Driver_-_Li710/Li710Test.cpp diff --git a/lib/Driver_-_Li710 b/lib/Driver_-_Li710 index c43b276..d5616c6 160000 --- a/lib/Driver_-_Li710 +++ b/lib/Driver_-_Li710 @@ -1 +1 @@ -Subproject commit c43b276337d09504a9f53c28270dee2190e31ea1 +Subproject commit d5616c60261d93517faf6d8b580ec932b9185ef2 diff --git a/lib/Driver_-_Sensor b/lib/Driver_-_Sensor index f768bd1..afef963 160000 --- a/lib/Driver_-_Sensor +++ b/lib/Driver_-_Sensor @@ -1 +1 @@ -Subproject commit f768bd1078e44152e3c921ba1f69018fd6356d86 +Subproject commit afef96338232e397656a907ce12338a4c9cad288 diff --git a/lib/Driver_-_Talon-SDI12 b/lib/Driver_-_Talon-SDI12 index c97deae..6439b28 160000 --- a/lib/Driver_-_Talon-SDI12 +++ b/lib/Driver_-_Talon-SDI12 @@ -1 +1 @@ -Subproject commit c97deaeacbc7db7784d9c5dde4189687f114364a +Subproject commit 6439b287dd1513858b8f8449e5250ac166215456 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 49ff324..1fec360 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 49ff32445e715b23ac21340696487bd24c3f1745 +Subproject commit 1fec36002d6e53980f6091d81b1e466944b55345 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index aabc40e..1d886ed 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -67,6 +67,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "platform/ParticleSerial.h" #include "hardware/IOExpanderPCAL9535A.h" +#include "hardware/SDI12TalonAdapter.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -85,7 +86,7 @@ ParticleUSBSerial realSerialDebug; ParticleHardwareSerial realSerialSdi12; IOExpanderPCAL9535A realIoOB(0x20); //0x20 is the PCAL Base address -IOExpanderPCAL9535A realIoTalon(0x20); +IOExpanderPCAL9535A realIoTalon(0x21); Kestrel logger(realTimeProvider, realGpio, @@ -102,6 +103,7 @@ Gonk battery(5); //Instantiate with defaults, manually set to port 5 AuxTalon aux(0, 0x14); //Instantiate AUX talon with deaults - null port and hardware v1.4 I2CTalon i2c(0, 0x21); //Instantiate I2C talon with alt - null port and hardware v2.1 SDI12Talon sdi12(0, 0x14); //Instantiate SDI12 talon with alt - null port and hardware v1.4 +SDI12TalonAdapter realSdi12(sdi12); IOExpanderPCAL9535A ioAlpha(0x20); IOExpanderPCAL9535A ioBeta(0x21); @@ -143,10 +145,10 @@ TDR315H soil2(sdi12, 0, 0); //Instantiate soil sensor with default ports and unk TDR315H soil3(sdi12, 0, 0); //Instantiate soil sensor with default ports and unknown version, pass over SDI12 Talon interface Hedorah gas(0, 0, 0x10); //Instantiate CO2 sensor with default ports and v1.0 hardware // T9602 humidity(0, 0, 0x00); //Instantiate Telair T9602 with default ports and version v0.0 -LI710 et(sdi12, 0, 0); //Instantiate ET sensor with default ports and unknown version, pass over SDI12 Talon interface +LI710 et(realTimeProvider, realSdi12, 0, 0); //Instantiate ET sensor with default ports and unknown version, pass over SDI12 Talon interface BaroVue10 campPressure(sdi12, 0, 0x00); // Instantiate Barovue10 with default ports and v0.0 hardware -const uint8_t numSensors = 8; //Number must match the number of objects defined in `sensors` array +const uint8_t numSensors = 7; //Number must match the number of objects defined in `sensors` array Sensor* const sensors[numSensors] = { &fileSys, @@ -155,8 +157,8 @@ Sensor* const sensors[numSensors] = { &sdi12, &battery, &logger, //Add sensors after this line - &et, - &haar + &et + // &haar // &soil1, // &apogeeSolar, @@ -1106,7 +1108,7 @@ int wakeSensors() for(int s = 0; s < numSensors; s++) { if(sensors[s]->getTalonPort() != 0) { logger.enableData(sensors[s]->getTalonPort(), true); //Turn on data for given port - sensors[s]->wake(); //Wake each sensor + sensors[s]->wake(realTimeProvider); //Wake each sensor logger.enableData(sensors[s]->getTalonPort(), false); //Turn data back off for given port } } diff --git a/src/hardware/IOExpanderPCAL9535A.cpp b/src/hardware/IOExpanderPCAL9535A.cpp index d184f46..383fa43 100644 --- a/src/hardware/IOExpanderPCAL9535A.cpp +++ b/src/hardware/IOExpanderPCAL9535A.cpp @@ -14,11 +14,7 @@ // #include // Or // --- Constructor --- - IOExpanderPCAL9535A::IOExpanderPCAL9535A(int address) - : pcal9535a(address) // Initialize the driver instance with the address - { - // Constructor body (if needed) - } + IOExpanderPCAL9535A::IOExpanderPCAL9535A(int address): pcal9535a(address) {} // --- IIOExpander Interface Method Implementations --- diff --git a/src/hardware/SDI12TalonAdapter.h b/src/hardware/SDI12TalonAdapter.h new file mode 100644 index 0000000..70bc2ee --- /dev/null +++ b/src/hardware/SDI12TalonAdapter.h @@ -0,0 +1,54 @@ +// src/hardware/SDI12TalonAdapter.h + +#ifndef SDI12_TALON_ADAPTER_H +#define SDI12_TALON_ADAPTER_H + +#include "ISDI12Talon.h" +#include "SDI12Talon.h" + +/** + * @brief Adapter implementing ISDI12Talon interface + * + * Adapts the concrete SDI12Talon class to the ISDI12Talon interface + * for dependency injection and testing purposes. + */ +class SDI12TalonAdapter : public ISDI12Talon { +public: + /** + * @brief Constructor that takes a reference to a concrete SDI12Talon + * @param talon The concrete SDI12Talon implementation to delegate to + */ + SDI12TalonAdapter(SDI12Talon& talon) : talon(talon) {} + ~SDI12TalonAdapter() override = default; + + // SDI12 communication methods + int getAddress() override { return talon.getAddress(); } + String sendCommand(String command) override { return talon.sendCommand(command); } + String command(String commandStr, int address) override { return talon.command(commandStr, address); } + int startMeasurment(int Address) override { return talon.startMeasurment(Address); } + int startMeasurmentIndex(int index, int Address) override { return talon.startMeasurmentIndex(index, Address); } + String continuousMeasurmentCRC(int Measure, int Address) override { return talon.continuousMeasurmentCRC(Measure, Address); } + bool testCRC(String message) override { return talon.testCRC(message); } + + // Port management methods + int enableData(uint8_t port, bool state) override { return talon.enableData(port, state); } + int enablePower(uint8_t port, bool state) override { return talon.enablePower(port, state); } + void disableDataAll() override { talon.disableDataAll(); } + uint8_t getNumPorts() override { return talon.getNumPorts(); } + + // Sensor interrogation + bool isPresent() override { return talon.isPresent(); } + + // Error handling and state reporting + //int throwError(uint32_t error) override { return talon.throwError(error); } + String getSensorPortString() override { return talon.getSensorPortString(); } + String getTalonPortString() override { return talon.getTalonPortString(); } + uint8_t getSensorPort() override { return talon.getSensorPort(); } + uint8_t getTalonPort() override { return talon.getTalonPort(); } + int restart() override { return talon.restart(); } + +private: + SDI12Talon& talon; // Reference to the concrete implementation +}; + +#endif // SDI12_TALON_ADAPTER_H \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7480e2b..e052d37 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,7 @@ # FlightControl_Demo/test/CMakeLists.txt +#allow debugging +set(CMAKE_BUILD_TYPE Debug) + # Add GoogleTest add_subdirectory(external/googletest) @@ -42,9 +45,12 @@ add_executable(unit_tests main.cpp # Kestrel tests - unit/Driver_-_Kestrel/KestrelTest.cpp - ${CMAKE_SOURCE_DIR}/lib/Driver_-_Kestrel/src/Kestrel.cpp - + #unit/Driver_-_Kestrel/KestrelTest.cpp + #${CMAKE_SOURCE_DIR}/lib/Driver_-_Kestrel/src/Kestrel.cpp + + # Li710 tests + unit/Driver_-_Li710/Li710Test.cpp + ${CMAKE_SOURCE_DIR}/lib/Driver_-_Li710/src/Li710.cpp ) # Link against mocks and GoogleTest diff --git a/test/mocks/MockSDI12Talon.h b/test/mocks/MockSDI12Talon.h new file mode 100644 index 0000000..e4d332a --- /dev/null +++ b/test/mocks/MockSDI12Talon.h @@ -0,0 +1,39 @@ +#ifndef MOCK_SDI12_TALON_H +#define MOCK_SDI12_TALON_H + +#include +#include "ISDI12Talon.h" + +/** + * @brief Mock implementation of the ISDI12Talon interface for testing + */ +class MockSDI12Talon : public ISDI12Talon { +public: + // SDI12 communication methods + MOCK_METHOD(int, getAddress, (), (override)); + MOCK_METHOD(String, sendCommand, (String command), (override)); + MOCK_METHOD(String, command, (String commandStr, int address), (override)); + MOCK_METHOD(int, startMeasurment, (int Address), (override)); + MOCK_METHOD(int, startMeasurmentIndex, (int index, int Address), (override)); + MOCK_METHOD(String, continuousMeasurmentCRC, (int Measure, int Address), (override)); + MOCK_METHOD(bool, testCRC, (String message), (override)); + + // Port management methods + MOCK_METHOD(int, enableData, (uint8_t port, bool state), (override)); + MOCK_METHOD(int, enablePower, (uint8_t port, bool state), (override)); + MOCK_METHOD(void, disableDataAll, (), (override)); + MOCK_METHOD(uint8_t, getNumPorts, (), (override)); + + // Sensor interrogation + MOCK_METHOD(bool, isPresent, (), (override)); + + // Error handling and state reporting + //MOCK_METHOD(int, throwError, (uint32_t error), (override)); + MOCK_METHOD(String, getSensorPortString, (), (override)); + MOCK_METHOD(String, getTalonPortString, (), (override)); + MOCK_METHOD(uint8_t, getSensorPort, (), (override)); + MOCK_METHOD(uint8_t, getTalonPort, (), (override)); + MOCK_METHOD(int, restart, (), (override)); +}; + +#endif // MOCK_SDI12_TALON_H \ No newline at end of file diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h new file mode 100644 index 0000000..2ed9217 --- /dev/null +++ b/test/mocks/Particle.h @@ -0,0 +1,389 @@ +#ifndef MOCK_PARTICLE_H +#define MOCK_PARTICLE_H + +// This file provides a minimal mock implementation of Particle's String class +// for testing purposes, without the need for actual Particle.h + +#include +#include +#include +#include +#include // For std::min and std::max + +// Forward declaration +class StringSumHelper; + +/** + * Mock implementation of Particle's String class for testing + * + * This class implements the minimum functionality needed for testing Li710 + * and can be expanded as needed for other components. + */ +class String { +public: + // Constructors + String(const char *cstr = "") { + if (cstr) { + _buffer = strdup(cstr); + if (_buffer) { + _length = strlen(cstr); + _capacity = _length; + } + } + } + + String(const String &str) : String(str._buffer) {} + + explicit String(char c) { + _buffer = (char*)malloc(2); + if (_buffer) { + _buffer[0] = c; + _buffer[1] = 0; + _length = 1; + _capacity = 1; + } + } + + explicit String(int value, unsigned char base = 10) { + char buf[33]; + snprintf(buf, sizeof(buf), base == 10 ? "%d" : "%x", value); + *this = String(buf); + } + + explicit String(float value, int decimalPlaces = 2) { + char buf[33]; + snprintf(buf, sizeof(buf), "%.*f", decimalPlaces, value); + *this = String(buf); + } + + ~String() { + if (_buffer) free(_buffer); + } + + // Operators + String& operator = (const String &rhs) { + if (this == &rhs) return *this; + if (_buffer) free(_buffer); + if (rhs._buffer) { + _buffer = strdup(rhs._buffer); + _length = rhs._length; + _capacity = rhs._capacity; + } else { + _buffer = nullptr; + _length = 0; + _capacity = 0; + } + return *this; + } + + String& operator = (const char *cstr) { + if (_buffer) free(_buffer); + if (cstr) { + _buffer = strdup(cstr); + _length = strlen(cstr); + _capacity = _length; + } else { + _buffer = nullptr; + _length = 0; + _capacity = 0; + } + return *this; + } + + // Concatenation + unsigned char concat(const String &str) { + return concat(str._buffer, str._length); + } + + unsigned char concat(const char *cstr) { + if (!cstr) return 0; + return concat(cstr, strlen(cstr)); + } + + unsigned char concat(const char *cstr, unsigned int length) { + if (!cstr || !length) return 0; + + unsigned int newLen = _length + length; + char *newBuffer = (char*)realloc(_buffer, newLen + 1); + if (!newBuffer) return 0; + + _buffer = newBuffer; + memcpy(_buffer + _length, cstr, length); + _buffer[newLen] = 0; + _length = newLen; + _capacity = newLen; + return 1; + } + + unsigned char concat(char c) { + char buf[2]; + buf[0] = c; + buf[1] = 0; + return concat(buf, 1); + } + + unsigned char concat(int num) { + char buf[12]; + snprintf(buf, sizeof(buf), "%d", num); + return concat(buf, strlen(buf)); + } + + unsigned char concat(float num, int decimalPlaces = 2) { + char buf[20]; + snprintf(buf, sizeof(buf), "%.*f", decimalPlaces, num); + return concat(buf, strlen(buf)); + } + + String& operator += (const String &rhs) { + concat(rhs); + return *this; + } + + String& operator += (const char *cstr) { + concat(cstr); + return *this; + } + + String& operator += (char c) { + concat(c); + return *this; + } + + String& operator += (int num) { + concat(num); + return *this; + } + + // Comparison + unsigned char equals(const String &s) const { + if (_length != s._length) return 0; + if (!_buffer || !s._buffer) return (_buffer == s._buffer); + return (strcmp(_buffer, s._buffer) == 0); + } + + unsigned char equals(const char *cstr) const { + if (!_buffer || !cstr) return (_buffer == cstr); + return (strcmp(_buffer, cstr) == 0); + } + + unsigned char operator == (const String &rhs) const { + return equals(rhs); + } + + unsigned char operator == (const char *cstr) const { + return equals(cstr); + } + + unsigned char operator != (const String &rhs) const { + return !equals(rhs); + } + + unsigned char operator != (const char *cstr) const { + return !equals(cstr); + } + + // Access + char charAt(unsigned int index) const { + if (index >= _length || !_buffer) return 0; + return _buffer[index]; + } + + void setCharAt(unsigned int index, char c) { + if (index >= _length || !_buffer) return; + _buffer[index] = c; + } + + char operator [] (unsigned int index) const { + return charAt(index); + } + + char& operator [] (unsigned int index) { + static char dummy_writable_char; + if (index >= _length || !_buffer) { + dummy_writable_char = 0; + return dummy_writable_char; + } + return _buffer[index]; + } + + const char* c_str() const { + return _buffer ? _buffer : ""; + } + + operator const char*() const { + return c_str(); + } + + unsigned int length() const { + return _length; + } + + // Search + int indexOf(char ch) const { + return indexOf(ch, 0); + } + + int indexOf(char ch, unsigned int fromIndex) const { + if (!_buffer || fromIndex >= _length) return -1; + const char* temp = strchr(_buffer + fromIndex, ch); + if (temp == nullptr) return -1; + return temp - _buffer; + } + + int indexOf(const String &str) const { + return indexOf(str, 0); + } + + int indexOf(const String &str, unsigned int fromIndex) const { + if (!_buffer || !str._buffer || fromIndex >= _length) return -1; + const char* found = strstr(_buffer + fromIndex, str._buffer); + if (found == nullptr) return -1; + return found - _buffer; + } + + void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { + if (buf == nullptr || bufsize == 0) { + return; // Nothing to do + } + + // Always null-terminate the start of the buffer + buf[0] = '\0'; + + // Check if the source buffer is valid and index is within bounds + if (_buffer == nullptr || index >= _length) { + return; // Nothing to copy + } + + // How many characters are available in the source string from index? + size_t source_available = _length - index; + + // How many characters can we actually copy? (Need bufsize - 1 for the null terminator) + size_t copy_len = std::min(source_available, (size_t)(bufsize - 1)); + + // Perform the copy from the internal buffer + if (copy_len > 0) { // Only copy if there's something to copy + memcpy(buf, _buffer + index, copy_len); + } + + // Add the null terminator at the end of the copied content + buf[copy_len] = '\0'; + } + + // Modification + String substring(unsigned int beginIndex) const { + if (!_buffer || beginIndex >= _length) return String(); + return String(_buffer + beginIndex); + } + + String substring(unsigned int beginIndex, unsigned int endIndex) const { + if (!_buffer || beginIndex >= _length || endIndex <= beginIndex) return String(); + if (endIndex > _length) endIndex = _length; + char temp[endIndex - beginIndex + 1]; + strncpy(temp, _buffer + beginIndex, endIndex - beginIndex); + temp[endIndex - beginIndex] = 0; + return String(temp); + } + + String& remove(unsigned int index) { + if (!_buffer || index >= _length) return *this; + memmove(_buffer + index, _buffer + index + 1, _length - index); + _length--; + _buffer[_length] = 0; + return *this; + } + + String& remove(unsigned int index, unsigned int count) { + if (!_buffer || index >= _length) return *this; + if (count > _length - index) count = _length - index; + memmove(_buffer + index, _buffer + index + count, _length - index - count + 1); + _length -= count; + return *this; + } + + String& replace(char find, char replace) { + if (!_buffer) return *this; + for (unsigned int i = 0; i < _length; i++) { + if (_buffer[i] == find) _buffer[i] = replace; + } + return *this; + } + + String& trim() { + if (!_buffer || !_length) return *this; + + // Find start of non-whitespace + char *begin = _buffer; + while (isspace(*begin)) begin++; + + // Find end of non-whitespace + char *end = _buffer + _length - 1; + while (end > begin && isspace(*end)) end--; + + // Calculate new length + unsigned int newlen = end - begin + 1; + + // Move string if needed + if (begin > _buffer) { + memmove(_buffer, begin, newlen); + } + + // Add null terminator + _buffer[newlen] = 0; + _length = newlen; + + return *this; + } + + // Parsing + long toInt() const { + if (!_buffer) return 0; + return atol(_buffer); + } + + float toFloat() const { + if (!_buffer) return 0; + return atof(_buffer); + } + +private: + char* _buffer = nullptr; + unsigned int _length = 0; + unsigned int _capacity = 0; +}; + +// String helper class for concatenation operations +class StringSumHelper : public String { +public: + StringSumHelper(const String &s) : String(s) {} + StringSumHelper(const char *p) : String(p) {} + StringSumHelper(char c) : String(c) {} + StringSumHelper(int num) : String(num) {} + StringSumHelper(float num) : String(num) {} +}; + +// Concatenation operators +inline StringSumHelper operator + (const String &lhs, const String &rhs) { + StringSumHelper result(lhs); + result += rhs; + return result; +} + +inline StringSumHelper operator + (const String &lhs, const char *cstr) { + StringSumHelper result(lhs); + result += cstr; + return result; +} + +inline StringSumHelper operator + (const String &lhs, char c) { + StringSumHelper result(lhs); + result += c; + return result; +} + +inline StringSumHelper operator + (const String &lhs, int num) { + StringSumHelper result(lhs); + result += num; + return result; +} + +#endif // MOCK_PARTICLE_H \ No newline at end of file diff --git a/test/unit/Driver_-_Li710/Li710Test.cpp b/test/unit/Driver_-_Li710/Li710Test.cpp new file mode 100644 index 0000000..d6b95d7 --- /dev/null +++ b/test/unit/Driver_-_Li710/Li710Test.cpp @@ -0,0 +1,423 @@ +#include +#include + +#include "Particle.h" //this is a mock of Particle.h that only includes its string implementation + +// Include other mocks +#include "MockSDI12Talon.h" +#include "MockTimeProvider.h" + +// Include the actual implementation +#include "Li710.h" + +class Li710Test : public ::testing::Test { +protected: + // Our mock objects + MockSDI12Talon mockTalon; + MockTimeProvider mockTimeProvider; + + void SetUp() override { + // Set up common mock behavior + ON_CALL(mockTalon, getSensorPortString()) + .WillByDefault(::testing::Return("1")); + ON_CALL(mockTalon, getTalonPortString()) + .WillByDefault(::testing::Return("1")); + ON_CALL(mockTalon, getSensorPort()) + .WillByDefault(::testing::Return(1)); + ON_CALL(mockTalon, getTalonPort()) + .WillByDefault(::testing::Return(1)); + } +}; + +// verify sensor interface set to SDI12 +TEST_F(Li710Test, TestSensorInterfaceSdi12) { + // Create a Li710 instance with our mock + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + // Verify that the sensor interface is set to SDI12 + EXPECT_EQ(li710.sensorInterface, BusType::SDI12); +} + +//verify talon port set to 255 if not in range +TEST_F(Li710Test, TestTalonPortOutOfRange) { + // Create a Li710 instance with our mock + LI710 li710(mockTimeProvider, mockTalon, 0, 1); // Set an out-of-range talon port + + // Verify that getTalonPort returns 0 when the port is 255 + EXPECT_EQ(li710.getTalonPort(), 0); +} + +//verify sensor port set to 255 if not in range +TEST_F(Li710Test, TestSensorPortOutOfRange) { + // Create a Li710 instance with our mock + LI710 li710(mockTimeProvider, mockTalon, 1, 0); // Set an out-of-range sensor port + + // Verify that getTalonPort returns 0 when the port is 255 + EXPECT_EQ(li710.getSensorPort(), 0); +} + +//verify talonPort is talonPort_ - 1 +TEST_F(Li710Test, TestTalonPortSet) { + // Create a Li710 instance with our mock + LI710 li710(mockTimeProvider, mockTalon, 42, 1); // Set a valid talon port + + // Verify that the talon port is set correctly + EXPECT_EQ(li710.getTalonPort(), 42); +} + +//verify sensorPort is sensorPort_ - 1 +TEST_F(Li710Test, TestSensorPortSet) { + // Create a Li710 instance with our mock + LI710 li710(mockTimeProvider, mockTalon, 1, 42); // Set a valid talon port + + // Verify that the sensor port is set correctly + EXPECT_EQ(li710.getSensorPort(), 42); +} + +// verify begin returns empty string +TEST_F(Li710Test, TestBegin) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + bool criticalFault = false; + bool fault = false; + + String result = li710.begin(0, criticalFault, fault); + + EXPECT_EQ(result, ""); +} + +// Test isPresent method when sensor is present +TEST_F(Li710Test, TestIsPresent_SensorFound) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .WillOnce(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .WillOnce(::testing::Return("013LI-COR LI-7101.01234567")); + + bool result = li710.isPresent(); + + EXPECT_TRUE(result); +} + +// Test isPresent method when sensor is not found +TEST_F(Li710Test, TestIsPresent_SensorNotFound) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .WillOnce(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .WillOnce(::testing::Return("013UNKNOWN SENSOR1.01234567")); + + bool result = li710.isPresent(); + + EXPECT_FALSE(result); +} + +//verify isPresent is called three times when it only returns false +TEST_F(Li710Test, TestGetData_SensorNotFound) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .Times(3) + .WillRepeatedly(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .Times(3) + .WillRepeatedly(::testing::Return("013UNKNOWN SENSOR1.01234567")); + + li710.getData(0); +} + +//verify talon.getAddress is called three times when it only return -1 +TEST_F(Li710Test, TestGetData_TalonAddressFail) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .Times(3) + .WillRepeatedly(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .Times(3) + .WillRepeatedly(::testing::Return("013LI-COR LI-7101.01234567")); + + EXPECT_CALL(mockTalon, getAddress()) + .Times(3) + .WillRepeatedly(::testing::Return(-1)); + + li710.getData(0); +} + +//verify ouptut is null when sensorPort is 0 +TEST_F(Li710Test, TestGetData_SensorPortZero) { + LI710 li710(mockTimeProvider, mockTalon, 1, 0); // Set an out-of-range sensor port + + String result = li710.getData(0); + + EXPECT_TRUE(result.indexOf("LiCor ET\":{") > 0); + EXPECT_TRUE(result.indexOf("\"ET\":null") > 0); + EXPECT_TRUE(result.indexOf("}") > 0); +} + +//verify output is null when retry count is reached +TEST_F(Li710Test, TestGetData_RetryCountReached) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .Times(3) + .WillRepeatedly(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .Times(3) + .WillRepeatedly(::testing::Return("013UNKNOWN SENSOR1.01234567")); + + String result = li710.getData(0); + + EXPECT_TRUE(result.indexOf("LiCor ET\":{") > 0); + EXPECT_TRUE(result.indexOf("\"ET\":null") > 0); + EXPECT_TRUE(result.indexOf("}") > 0); +} + +// Test getData method with valid data +TEST_F(Li710Test, DISABLED_TestGetData_ValidData) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .WillOnce(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .WillOnce(::testing::Return("013LI-COR LI-7101.01234567")); + + EXPECT_CALL(mockTalon, getAddress()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockTalon, command(String("XT"), 0)) + .WillOnce(::testing::Return("")); + + // Mock valid responses for data + EXPECT_CALL(mockTalon, continuousMeasurmentCRC(0, 0)) + .WillOnce(::testing::Return("0+1.23+4.56+7.89+10.11+12.13+14.15+16.17+18.19+20.21")); + + EXPECT_CALL(mockTalon, continuousMeasurmentCRC(1, 0)) + .WillOnce(::testing::Return("0+22.23+24.25+26.27+28.29+30.31+32.33+34.35+36.37+38.39")); + + EXPECT_CALL(mockTalon, continuousMeasurmentCRC(2, 0)) + .WillOnce(::testing::Return("0+40.41+42.43+44.45+46.47+48.49+50.51+52.53+54.55")); + + EXPECT_CALL(mockTalon, testCRC(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + String result = li710.getData(0); + + EXPECT_TRUE(result.indexOf("LiCor ET\":{") > 0); + EXPECT_TRUE(result.indexOf("\"ET\":1.230") > 0); + EXPECT_TRUE(result.indexOf("\"LE\":4.6") > 0); + EXPECT_TRUE(result.indexOf("\"H\":7.9") > 0); + EXPECT_TRUE(result.indexOf("\"VPD\":10.11") > 0); + EXPECT_TRUE(result.indexOf("\"PA\":12.13") > 0); + EXPECT_TRUE(result.indexOf("\"TA\":14.15") > 0); + EXPECT_TRUE(result.indexOf("\"RH\":16.17") > 0); + EXPECT_TRUE(result.indexOf("\"SAMP_CNT\":36") > 0); + EXPECT_TRUE(result.indexOf("\"AH\":40.41") > 0); + EXPECT_TRUE(result.indexOf("\"SVP\":44.45") > 0); + EXPECT_TRUE(result.indexOf("\"TD\":54.55") > 0); + EXPECT_TRUE(result.indexOf("\"Pos\":[1,1]") > 0); +} + +// Test getData method with valid data and start measurment +//after new read method +TEST_F(Li710Test, TestGetData_ValidDataStartMeasurment) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .WillOnce(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .WillOnce(::testing::Return("013LI-COR LI-7101.01234567")); + + EXPECT_CALL(mockTalon, getAddress()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockTalon, command(String("XT"), 0)) + .WillOnce(::testing::Return("")); + + // Mock valid responses for data + EXPECT_CALL(mockTalon, startMeasurmentIndex(0 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, startMeasurmentIndex(1 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, startMeasurmentIndex(2 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, startMeasurmentIndex(3 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, command(String("D0"), 0)) + .WillOnce(::testing::Return("0+1.23+4.56+7.89")) + .WillOnce(::testing::Return("0+22.23+24.25+26.27")) + .WillOnce(::testing::Return("0+40.41+42.43+44.45")) + .WillOnce(::testing::Return("0+40.41+42.43+44.45")); //value tbd + + EXPECT_CALL(mockTalon, command(String("D1"), 0)) + .WillOnce(::testing::Return("0+10.11+12.13+14.15")) + .WillOnce(::testing::Return("0+28.29+30.31+32.33")) + .WillOnce(::testing::Return("0+46.47+48.49+50.51")) + .WillOnce(::testing::Return("0+46.47+48.49+50.51")); //value tbd + + EXPECT_CALL(mockTalon, command(String("D2"), 0)) + .WillOnce(::testing::Return("0+16.17+18.19+20.21")) + .WillOnce(::testing::Return("0+34.35+36.37+38.39")) + .WillOnce(::testing::Return("0+52.53+54.55")) + .WillOnce(::testing::Return("0+52.53+54.55")); //value tbd + + String result = li710.getData(0); + + EXPECT_TRUE(result.indexOf("LiCor ET\":{") > 0); + EXPECT_TRUE(result.indexOf("\"ET\":1.230") > 0); + EXPECT_TRUE(result.indexOf("\"LE\":4.6") > 0); + EXPECT_TRUE(result.indexOf("\"H\":7.9") > 0); + EXPECT_TRUE(result.indexOf("\"VPD\":10.11") > 0); + EXPECT_TRUE(result.indexOf("\"PA\":12.13") > 0); + EXPECT_TRUE(result.indexOf("\"TA\":14.15") > 0); + EXPECT_TRUE(result.indexOf("\"RH\":16.17") > 0); + EXPECT_TRUE(result.indexOf("\"SAMP_CNT\":36") > 0); + EXPECT_TRUE(result.indexOf("\"AH\":40.41") > 0); + EXPECT_TRUE(result.indexOf("\"SVP\":44.45") > 0); + EXPECT_TRUE(result.indexOf("\"TD\":54.55") > 0); + EXPECT_TRUE(result.indexOf("\"Pos\":[1,1]") > 0); +} + +// Test getData method with CRC failure +//disabled after new read method +TEST_F(Li710Test, DISABLED_TestGetData_CRCFailure) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .Times(3) + .WillRepeatedly(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .Times(3) + .WillRepeatedly(::testing::Return("013LI-COR LI-7101.01234567")); + + EXPECT_CALL(mockTalon, getAddress()) + .WillRepeatedly(::testing::Return(0)); + + EXPECT_CALL(mockTalon, command(String("XT"), 0)) + .WillRepeatedly(::testing::Return("")); + + // Mock valid responses for data + EXPECT_CALL(mockTalon, continuousMeasurmentCRC(0, 0)) + .WillRepeatedly(::testing::Return("0+1.23+4.56+7.89+10.11+12.13+14.15+16.17+18.19+20.21")); + + EXPECT_CALL(mockTalon, continuousMeasurmentCRC(1, 0)) + .WillRepeatedly(::testing::Return("0+22.23+24.25+26.27+28.29+30.31+32.33+34.35+36.37+38.39")); + + EXPECT_CALL(mockTalon, continuousMeasurmentCRC(2, 0)) + .WillRepeatedly(::testing::Return("0+40.41+42.43+44.45+46.47+48.49+50.51+52.53+54.55")); + + // Force CRC failure + EXPECT_CALL(mockTalon, testCRC(::testing::_)) + .WillRepeatedly(::testing::Return(false)); + + // Expect error to be reported + //EXPECT_CALL(mockTalon, throwError(::testing::_)) + //.Times(::testing::AtLeast(1)); + + String result = li710.getData(0); + + EXPECT_TRUE(result.indexOf("LiCor ET\":{") > 0); + EXPECT_TRUE(result.indexOf("\"ET\":null") > 0); + EXPECT_TRUE(result.indexOf("\"LE\":null") > 0); + EXPECT_TRUE(result.indexOf("\"Pos\":[1,1]") > 0); +} + +// Test getMetadata method +// refactor this after refactoring Li710 +TEST_F(Li710Test, TestGetMetadata) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .WillOnce(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .WillOnce(::testing::Return("013LI-COR LI-7101.01234567")); + + String result = li710.getMetadata(); + + EXPECT_TRUE(result.indexOf("LiCor ET\":{") > 0); + EXPECT_TRUE(result.indexOf("\"Hardware\":\"1.0\"") > 0); + EXPECT_TRUE(result.indexOf("\"SDI12_Ver\":\"1.3\"") > 0); + EXPECT_TRUE(result.indexOf("\"Mfg\":\"LI-COR\"") > 0); + EXPECT_TRUE(result.indexOf("\"Model\":\"LI-710\"") > 0); + EXPECT_TRUE(result.indexOf("\"SN\":\"1234567\"") > 0); + EXPECT_TRUE(result.indexOf("\"Pos\":[1,1]") > 0); + EXPECT_TRUE(result.indexOf("}") > 0); +} + +// Test selfDiagnostic method +TEST_F(Li710Test, TestSelfDiagnostic) { + LI710 li710(mockTimeProvider, mockTalon, 1, 1); + + EXPECT_CALL(mockTalon, sendCommand(String("?!"))) + .WillRepeatedly(::testing::Return("0")); + + EXPECT_CALL(mockTalon, command(String("I"), 0)) + .WillRepeatedly(::testing::Return("013LI-COR LI-7101.01234567")); + + EXPECT_CALL(mockTalon, getAddress()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockTalon, command(String("XT"), 0)) + .WillOnce(::testing::Return("")); + + // Mock valid responses for data + EXPECT_CALL(mockTalon, startMeasurmentIndex(0 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, startMeasurmentIndex(1 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, startMeasurmentIndex(2 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, startMeasurmentIndex(3 , 0)) + .WillOnce(::testing::Return(10)); + + EXPECT_CALL(mockTalon, command(String("D0"), 0)) + .WillOnce(::testing::Return("0+1.23+4.56+7.89")) + .WillOnce(::testing::Return("0+22.23+24.25+26.27")) + .WillOnce(::testing::Return("0+40.41+42.43+44.45")) + .WillOnce(::testing::Return("0+56.57+58.59+60.61")); //value tbd + + EXPECT_CALL(mockTalon, command(String("D1"), 0)) + .WillOnce(::testing::Return("0+10.11+12.13+14.15")) + .WillOnce(::testing::Return("0+28.29+30.31+32.33")) + .WillOnce(::testing::Return("0+46.47+48.49+50.51")) + .WillOnce(::testing::Return("0+62.63+64.65+66.67")); //value tbd + + EXPECT_CALL(mockTalon, command(String("D2"), 0)) + .WillOnce(::testing::Return("0+16.17+18.19+20.21")) + .WillOnce(::testing::Return("0+34.35+36.37+38.39")) + .WillOnce(::testing::Return("0+52.53+54.55")) + .WillOnce(::testing::Return("0+68.69+70.71")); //value tbd + + li710.getData(0); + + String result = li710.selfDiagnostic(5, 0); + + EXPECT_TRUE(result.indexOf("\"LiCor ET\":{") >= 0); + EXPECT_TRUE(result.indexOf("\"Adr\":0") > 0); + EXPECT_TRUE(result.indexOf("\"PUMP_V\":56.57") > 0); + EXPECT_TRUE(result.indexOf("\"PA_CELL\":58.59") > 0); + EXPECT_TRUE(result.indexOf("\"RH_CELL\":60.61") > 0); + EXPECT_TRUE(result.indexOf("\"TA_CELL\":62.63") > 0); + //I think this is not 70.71 because the precision value is 0 + //instead of 2. I think the fake string implementation gets rid + //of the digits before the decimal place instead of after + EXPECT_TRUE(result.indexOf("\"DATA_QC\":71") > 0); + EXPECT_TRUE(result.indexOf("\"Pos\":[1,1]") > 0); +} \ No newline at end of file From ad0ac038112f13c482250b1b98b7d664f1e41016 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 23 Apr 2025 07:57:13 -0500 Subject: [PATCH 22/48] added PAC1934 absraction --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 6 ++ src/hardware/CurrentSenseAmplifierPAC1934.cpp | 101 ++++++++++++++++++ src/hardware/CurrentSenseAmplifierPAC1934.h | 59 ++++++++++ test/mocks/MockPAC1934.h | 47 ++++++++ 6 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/hardware/CurrentSenseAmplifierPAC1934.cpp create mode 100644 src/hardware/CurrentSenseAmplifierPAC1934.h create mode 100644 test/mocks/MockPAC1934.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index af0eb76..c50437e 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit af0eb76a3c8e95737c91c7650451b2332f54178b +Subproject commit c50437ef154e1606babcdf32cbc4b4734bf91ffc diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 1fec360..4b256b3 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 1fec36002d6e53980f6091d81b1e466944b55345 +Subproject commit 4b256b3268d4d1abd5a3913c6af7a856e5fc1904 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 1d886ed..97eb865 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -68,6 +68,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/IOExpanderPCAL9535A.h" #include "hardware/SDI12TalonAdapter.h" +#include "hardware/CurrentSenseAmplifierPAC1934.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -88,6 +89,9 @@ ParticleHardwareSerial realSerialSdi12; IOExpanderPCAL9535A realIoOB(0x20); //0x20 is the PCAL Base address IOExpanderPCAL9535A realIoTalon(0x21); +CurrentSenseAmplifierPAC1934 realCsaAlpha(2,2,2,2,0x18); +CurrentSenseAmplifierPAC1934 realCsaBeta(2,10,10,10,0x14); + Kestrel logger(realTimeProvider, realGpio, realSystem, @@ -97,6 +101,8 @@ Kestrel logger(realTimeProvider, realSerialSdi12, realIoOB, realIoTalon, + realCsaAlpha, + realCsaBeta, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/hardware/CurrentSenseAmplifierPAC1934.cpp b/src/hardware/CurrentSenseAmplifierPAC1934.cpp new file mode 100644 index 0000000..943f887 --- /dev/null +++ b/src/hardware/CurrentSenseAmplifierPAC1934.cpp @@ -0,0 +1,101 @@ +// src/hardware/CurrentSenseAmplifierPAC1934.cpp + +#include "CurrentSenseAmplifierPAC1934.h" + +CurrentSenseAmplifierPAC1934::CurrentSenseAmplifierPAC1934(float r1, float r2, float r3, float r4, uint8_t addr) + : pac1934(r1, r2, r3, r4, addr) { + // Constructor delegates to PAC1934 constructor +} + +bool CurrentSenseAmplifierPAC1934::begin() { + return pac1934.begin(); +} + +bool CurrentSenseAmplifierPAC1934::setAddress(uint8_t addr) { + return pac1934.setAddress(addr); +} + +bool CurrentSenseAmplifierPAC1934::enableChannel(uint8_t channel, bool state) { + return pac1934.enableChannel(channel, state); +} + +bool CurrentSenseAmplifierPAC1934::setFrequency(uint16_t frequency) { + // Map the uint16_t frequency to the PAC1934 Frequency enum + Frequency freq; + switch (frequency) { + case 8: + freq = SPS_8; + break; + case 64: + freq = SPS_64; + break; + case 256: + freq = SPS_256; + break; + case 1024: + default: + freq = SPS_1024; + break; + } + return pac1934.setFrequency(freq); +} + +int CurrentSenseAmplifierPAC1934::getFrequency() { + return pac1934.getFrequency(); +} + +void CurrentSenseAmplifierPAC1934::setVoltageDirection(uint8_t channel, bool bidirectional) { + pac1934.setVoltageDirection(channel, bidirectional); +} + +void CurrentSenseAmplifierPAC1934::setCurrentDirection(uint8_t channel, bool bidirectional) { + pac1934.setCurrentDirection(channel, bidirectional); +} + +bool CurrentSenseAmplifierPAC1934::getVoltageDirection(uint8_t channel) { + return pac1934.getVoltageDirection(channel); +} + +bool CurrentSenseAmplifierPAC1934::getCurrentDirection(uint8_t channel) { + return pac1934.getCurrentDirection(channel); +} + +float CurrentSenseAmplifierPAC1934::getBusVoltage(uint8_t channel, bool average, bool& status) { + return pac1934.getBusVoltage(channel, average, status); +} + +float CurrentSenseAmplifierPAC1934::getBusVoltage(uint8_t channel, bool average) { + return pac1934.getBusVoltage(channel, average); +} + +float CurrentSenseAmplifierPAC1934::getSenseVoltage(uint8_t channel, bool average, bool& status) { + return pac1934.getSenseVoltage(channel, average, status); +} + +float CurrentSenseAmplifierPAC1934::getSenseVoltage(uint8_t channel, bool average) { + return pac1934.getSenseVoltage(channel, average); +} + +float CurrentSenseAmplifierPAC1934::getCurrent(uint8_t channel, bool average, bool& status) { + return pac1934.getCurrent(channel, average, status); +} + +float CurrentSenseAmplifierPAC1934::getCurrent(uint8_t channel, bool average) { + return pac1934.getCurrent(channel, average); +} + +float CurrentSenseAmplifierPAC1934::getPowerAvg(uint8_t channel, bool& status) { + return pac1934.getPowerAvg(channel, status); +} + +float CurrentSenseAmplifierPAC1934::getPowerAvg(uint8_t channel) { + return pac1934.getPowerAvg(channel); +} + +uint8_t CurrentSenseAmplifierPAC1934::update(uint8_t clear) { + return pac1934.update(clear); +} + +bool CurrentSenseAmplifierPAC1934::testOverflow() { + return pac1934.testOverflow(); +} \ No newline at end of file diff --git a/src/hardware/CurrentSenseAmplifierPAC1934.h b/src/hardware/CurrentSenseAmplifierPAC1934.h new file mode 100644 index 0000000..d4cbb65 --- /dev/null +++ b/src/hardware/CurrentSenseAmplifierPAC1934.h @@ -0,0 +1,59 @@ +// src/hardware/CurrentSenseAmplifierPAC1934.h + +#ifndef CURRENT_SENSE_AMPLIFIER_PAC1934_H +#define CURRENT_SENSE_AMPLIFIER_PAC1934_H + +#include "ICurrentSenseAmplifier.h" +#include "PAC1934.h" // Include the driver header + +/** + * @brief Concrete implementation of ICurrentSenseAmplifier using PAC1934 + * + * Adapts the PAC1934 current sense amplifier driver to the ICurrentSenseAmplifier interface + * for dependency injection and testing. + */ +class CurrentSenseAmplifierPAC1934 : public ICurrentSenseAmplifier { +public: + /** + * @brief Constructor + * @param r1 Channel 1 sense resistor value in milliohms + * @param r2 Channel 2 sense resistor value in milliohms + * @param r3 Channel 3 sense resistor value in milliohms + * @param r4 Channel 4 sense resistor value in milliohms + * @param addr I2C address of the PAC1934 chip + */ + CurrentSenseAmplifierPAC1934(float r1 = 0, float r2 = 0, float r3 = 0, float r4 = 0, uint8_t addr = 0x18); + ~CurrentSenseAmplifierPAC1934() override = default; + + // Configuration methods + bool begin() override; + bool setAddress(uint8_t addr) override; + bool enableChannel(uint8_t channel, bool state) override; + bool setFrequency(uint16_t frequency) override; + int getFrequency() override; + + // Measurement direction + void setVoltageDirection(uint8_t channel, bool bidirectional) override; + void setCurrentDirection(uint8_t channel, bool bidirectional) override; + bool getVoltageDirection(uint8_t channel) override; + bool getCurrentDirection(uint8_t channel) override; + + // Measurement methods + float getBusVoltage(uint8_t channel, bool average, bool& status) override; + float getBusVoltage(uint8_t channel, bool average = false) override; + float getSenseVoltage(uint8_t channel, bool average, bool& status) override; + float getSenseVoltage(uint8_t channel, bool average = false) override; + float getCurrent(uint8_t channel, bool average, bool& status) override; + float getCurrent(uint8_t channel, bool average = false) override; + float getPowerAvg(uint8_t channel, bool& status) override; + float getPowerAvg(uint8_t channel) override; + + // Status and control + uint8_t update(uint8_t clear = 0) override; + bool testOverflow() override; + +private: + PAC1934 pac1934; // The underlying PAC1934 driver instance +}; + +#endif // CURRENT_SENSE_AMPLIFIER_PAC1934_H \ No newline at end of file diff --git a/test/mocks/MockPAC1934.h b/test/mocks/MockPAC1934.h new file mode 100644 index 0000000..e4ed15b --- /dev/null +++ b/test/mocks/MockPAC1934.h @@ -0,0 +1,47 @@ +// test/mocks/MockPAC1934.h + +#ifndef MOCK_PAC1934_H +#define MOCK_PAC1934_H + +#include +#include "ICurrentSenseAmplifier.h" + +/** + * @brief Google Mock implementation of ICurrentSenseAmplifier for testing. + * + * This mock allows for unit testing of code that depends on ICurrentSenseAmplifier + * without requiring actual hardware. + */ +class MockPAC1934 : public ICurrentSenseAmplifier { +public: + // Mock all methods defined in the interface + + // Configuration methods + MOCK_METHOD(bool, begin, (), (override)); + MOCK_METHOD(bool, setAddress, (uint8_t addr), (override)); + MOCK_METHOD(bool, enableChannel, (uint8_t channel, bool state), (override)); + MOCK_METHOD(bool, setFrequency, (uint8_t frequency), (override)); + MOCK_METHOD(int, getFrequency, (), (override)); + + // Measurement direction + MOCK_METHOD(void, setVoltageDirection, (uint8_t channel, bool bidirectional), (override)); + MOCK_METHOD(void, setCurrentDirection, (uint8_t channel, bool bidirectional), (override)); + MOCK_METHOD(bool, getVoltageDirection, (uint8_t channel), (override)); + MOCK_METHOD(bool, getCurrentDirection, (uint8_t channel), (override)); + + // Measurement methods - overload versions + MOCK_METHOD(float, getBusVoltage, (uint8_t channel, bool average, bool& status), (override)); + MOCK_METHOD(float, getBusVoltage, (uint8_t channel, bool average), (override)); + MOCK_METHOD(float, getSenseVoltage, (uint8_t channel, bool average, bool& status), (override)); + MOCK_METHOD(float, getSenseVoltage, (uint8_t channel, bool average), (override)); + MOCK_METHOD(float, getCurrent, (uint8_t channel, bool average, bool& status), (override)); + MOCK_METHOD(float, getCurrent, (uint8_t channel, bool average), (override)); + MOCK_METHOD(float, getPowerAvg, (uint8_t channel, bool& status), (override)); + MOCK_METHOD(float, getPowerAvg, (uint8_t channel), (override)); + + // Status and control + MOCK_METHOD(uint8_t, update, (uint8_t clear), (override)); + MOCK_METHOD(bool, testOverflow, (), (override)); +}; + +#endif // MOCK_PAC1934_H \ No newline at end of file From 127dd95cc4cfab313da1f5a127f9311aa52e5f0c Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 23 Apr 2025 09:43:20 -0500 Subject: [PATCH 23/48] abstracted LED --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 4 + src/hardware/LedPCA9634.cpp | 118 ++++++++++++++++++++++++ src/hardware/LedPCA9634.h | 41 ++++++++ test/mocks/MockPCA9634.h | 27 ++++-- 6 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 src/hardware/LedPCA9634.cpp create mode 100644 src/hardware/LedPCA9634.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index c50437e..8498d1e 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit c50437ef154e1606babcdf32cbc4b4734bf91ffc +Subproject commit 8498d1e689ce2009de40658447df46e32dab3bf5 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 4b256b3..1f57444 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 4b256b3268d4d1abd5a3913c6af7a856e5fc1904 +Subproject commit 1f57444733344b9ad08f987ddb00cf239eb0a0f0 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 97eb865..a30f565 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -69,6 +69,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/IOExpanderPCAL9535A.h" #include "hardware/SDI12TalonAdapter.h" #include "hardware/CurrentSenseAmplifierPAC1934.h" +#include "hardware/LedPCA9634.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -92,6 +93,8 @@ IOExpanderPCAL9535A realIoTalon(0x21); CurrentSenseAmplifierPAC1934 realCsaAlpha(2,2,2,2,0x18); CurrentSenseAmplifierPAC1934 realCsaBeta(2,10,10,10,0x14); +LedPCA9634 realLed(0x52); + Kestrel logger(realTimeProvider, realGpio, realSystem, @@ -103,6 +106,7 @@ Kestrel logger(realTimeProvider, realIoTalon, realCsaAlpha, realCsaBeta, + realLed, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/hardware/LedPCA9634.cpp b/src/hardware/LedPCA9634.cpp new file mode 100644 index 0000000..8c0d5e6 --- /dev/null +++ b/src/hardware/LedPCA9634.cpp @@ -0,0 +1,118 @@ +// src/hardware/LedPCA9634.cpp +#include "LedPCA9634.h" + +// Constructor +LedPCA9634::LedPCA9634(int address) + : driver(address) // Initialize the driver with address +{ + // Constructor body is empty as the initialization is done in the initializer list +} + +// Core methods +int LedPCA9634::begin() { + return driver.begin(); +} + +int LedPCA9634::sleep(bool State) { + return driver.sleep(State); +} + +// Configuration methods +int LedPCA9634::setOutputMode(IOutputMode State) { + // Convert from interface enum to driver enum + OutputMode driverState; + switch (State) { + case IOutputMode::OpenDrain: + driverState = OpenDrain; + break; + case IOutputMode::TotemPole: + driverState = TotemPole; + break; + default: + driverState = OpenDrain; // Default case + break; + } + return driver.setOutputMode(driverState); +} + +int LedPCA9634::setGroupMode(IGroupMode State) { + // Convert from interface enum to driver enum + GroupMode driverState; + switch (State) { + case IGroupMode::Dim: + driverState = Dim; + break; + case IGroupMode::Blink: + driverState = Blink; + break; + default: + driverState = Dim; // Default case + break; + } + return driver.setGroupMode(driverState); +} + +// Group control +int LedPCA9634::setGroupBlinkPeriod(uint16_t Period) { + return driver.setGroupBlinkPeriod(Period); +} + +int LedPCA9634::setGroupOnTime(uint16_t Period) { + return driver.setGroupOnTime(Period); +} + +// Brightness control +int LedPCA9634::setBrightness(uint8_t Pos, float Brightness) { + return driver.setBrightness(Pos, Brightness); +} + +int LedPCA9634::setBrightnessArray(float Brightness) { + return driver.setBrightnessArray(Brightness); +} + +// Output state control +int LedPCA9634::setOutput(uint8_t Pos, IPortState State) { + // Convert from interface enum to driver enum + PortState driverState; + switch (State) { + case IPortState::Off: + driverState = Off; + break; + case IPortState::On: + driverState = On; + break; + case IPortState::PWM: + driverState = PWM; + break; + case IPortState::Group: + driverState = Group; + break; + default: + driverState = Off; // Default case + break; + } + return driver.setOutput(Pos, driverState); +} + +int LedPCA9634::setOutputArray(IPortState Val) { + // Convert from interface enum to driver enum + PortState driverState; + switch (Val) { + case IPortState::Off: + driverState = Off; + break; + case IPortState::On: + driverState = On; + break; + case IPortState::PWM: + driverState = PWM; + break; + case IPortState::Group: + driverState = Group; + break; + default: + driverState = Off; // Default case + break; + } + return driver.setOutputArray(driverState); +} \ No newline at end of file diff --git a/src/hardware/LedPCA9634.h b/src/hardware/LedPCA9634.h new file mode 100644 index 0000000..0ee8466 --- /dev/null +++ b/src/hardware/LedPCA9634.h @@ -0,0 +1,41 @@ +// src/hardware/LedPCA9634.h +#ifndef LED_PCA9634_H +#define LED_PCA9634_H + +#include "ILed.h" // Include the interface definition +#include "../../PCA9634/src/PCA9634.h" // Include the actual driver + +/** + * @brief Concrete implementation of ILed using PCA9634 driver + */ +class LedPCA9634 : public ILed { +public: + // Constructor/Destructor + explicit LedPCA9634(int address); + ~LedPCA9634() override = default; + + // Core methods + int begin() override; + int sleep(bool State) override; + + // Configuration methods + int setOutputMode(IOutputMode State) override; + int setGroupMode(IGroupMode State) override; + + // Group control + int setGroupBlinkPeriod(uint16_t Period) override; + int setGroupOnTime(uint16_t Period) override; + + // Brightness control + int setBrightness(uint8_t Pos, float Brightness) override; + int setBrightnessArray(float Brightness) override; + + // Output state control + int setOutput(uint8_t Pos, IPortState State) override; + int setOutputArray(IPortState Val) override; + +private: + PCA9634 driver; // The concrete driver instance +}; + +#endif // LED_PCA9634_H \ No newline at end of file diff --git a/test/mocks/MockPCA9634.h b/test/mocks/MockPCA9634.h index ac23404..1f28988 100644 --- a/test/mocks/MockPCA9634.h +++ b/test/mocks/MockPCA9634.h @@ -1,19 +1,26 @@ +// test/mocks/MockPCA9634.h #ifndef MOCK_PCA9634_H #define MOCK_PCA9634_H #include -#include "PCA9634.h" +#include "ILed.h" // Include the interface definition -class MockPCA9634 : public PCA9634 { +/** + * @brief Google Mock implementation of ILed for testing + */ +class MockPCA9634 : public ILed { public: - MockPCA9634(uint8_t address = 0) : PCA9634(address) {} - - MOCK_METHOD(int, begin, ()); - MOCK_METHOD(int, setOutputMode, (OutputMode State)); - MOCK_METHOD(int, setGroupMode, (GroupMode State)); - MOCK_METHOD(int, setOutputArray, (PortState Val)); - MOCK_METHOD(int, setBrightnessArray, (float Brightness)); - MOCK_METHOD(int, setGroupOnTime, (uint16_t Period)); + // Mock all methods defined in the interface + MOCK_METHOD(int, begin, (), (override)); + MOCK_METHOD(int, sleep, (bool State), (override)); + MOCK_METHOD(int, setOutputMode, (OutputMode State), (override)); + MOCK_METHOD(int, setGroupMode, (GroupMode State), (override)); + MOCK_METHOD(int, setGroupBlinkPeriod, (uint16_t Period), (override)); + MOCK_METHOD(int, setGroupOnTime, (uint16_t Period), (override)); + MOCK_METHOD(int, setBrightness, (uint8_t Pos, float Brightness), (override)); + MOCK_METHOD(int, setBrightnessArray, (float Brightness), (override)); + MOCK_METHOD(int, setOutput, (uint8_t Pos, PortState State), (override)); + MOCK_METHOD(int, setOutputArray, (PortState Val), (override)); }; #endif // MOCK_PCA9634_H \ No newline at end of file From 4bfda3127798275456b744585b59fc53818e47ac Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 24 Apr 2025 08:11:08 -0500 Subject: [PATCH 24/48] abstracted rtc --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 4 + src/hardware/RtcMCP79412.cpp | 108 ++++++++++++++++++++++++ src/hardware/RtcMCP79412.h | 47 +++++++++++ test/mocks/MockMCP79412.h | 55 ++++++++---- 6 files changed, 200 insertions(+), 18 deletions(-) create mode 100644 src/hardware/RtcMCP79412.cpp create mode 100644 src/hardware/RtcMCP79412.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 8498d1e..702bb9a 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 8498d1e689ce2009de40658447df46e32dab3bf5 +Subproject commit 702bb9ab701326f3c6a297d5056f7808015ebced diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 1f57444..ff86d2e 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 1f57444733344b9ad08f987ddb00cf239eb0a0f0 +Subproject commit ff86d2e2d6df177649cf5c2b36cb5edfdeadca06 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index a30f565..e0f3b3a 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -70,6 +70,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/SDI12TalonAdapter.h" #include "hardware/CurrentSenseAmplifierPAC1934.h" #include "hardware/LedPCA9634.h" +#include "hardware/RtcMCP79412.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -95,6 +96,8 @@ CurrentSenseAmplifierPAC1934 realCsaBeta(2,10,10,10,0x14); LedPCA9634 realLed(0x52); +RtcMCP79412 realRtc;; + Kestrel logger(realTimeProvider, realGpio, realSystem, @@ -107,6 +110,7 @@ Kestrel logger(realTimeProvider, realCsaAlpha, realCsaBeta, realLed, + realRtc, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/hardware/RtcMCP79412.cpp b/src/hardware/RtcMCP79412.cpp new file mode 100644 index 0000000..8e0cdd5 --- /dev/null +++ b/src/hardware/RtcMCP79412.cpp @@ -0,0 +1,108 @@ +/** + * @file RtcMCP79412.cpp + * @brief Implementation of the RtcMCP79412 class. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #include "RtcMCP79412.h" + + RtcMCP79412::RtcMCP79412() : rtc() { + // Default constructor, delegates to MCP79412 constructor + } + + int RtcMCP79412::begin(bool UseExtOsc) { + return rtc.begin(UseExtOsc); + } + + int RtcMCP79412::setTime(int Year, int Month, int Day, int DoW, int Hour, int Min, int Sec) { + return rtc.setTime(Year, Month, Day, DoW, Hour, Min, Sec); + } + + int RtcMCP79412::setTime(int Year, int Month, int Day, int Hour, int Min, int Sec) { + return rtc.setTime(Year, Month, Day, Hour, Min, Sec); + } + + IRtc::Timestamp RtcMCP79412::getRawTime() { + MCP79412::Timestamp rtcTimestamp = rtc.getRawTime(); + + // Convert from MCP79412::Timestamp to IRtc::Timestamp + IRtc::Timestamp interfaceTimestamp; + interfaceTimestamp.year = rtcTimestamp.year; + interfaceTimestamp.month = rtcTimestamp.month; + interfaceTimestamp.mday = rtcTimestamp.mday; + interfaceTimestamp.wday = rtcTimestamp.wday; + interfaceTimestamp.hour = rtcTimestamp.hour; + interfaceTimestamp.min = rtcTimestamp.min; + interfaceTimestamp.sec = rtcTimestamp.sec; + + return interfaceTimestamp; + } + + time_t RtcMCP79412::getTimeUnix() { + return rtc.getTimeUnix(); + } + + int RtcMCP79412::setMode(Mode Val) { + MCP79412::Mode mcpMode; + + // Convert from IRtc::Mode to MCP79412::Mode + switch(Val) { + case Mode::Normal: + mcpMode = MCP79412::Mode::Normal; + break; + case Mode::Inverted: + mcpMode = MCP79412::Mode::Inverted; + break; + default: + // Default to Normal mode + mcpMode = MCP79412::Mode::Normal; + break; + } + + return rtc.setMode(mcpMode); + } + + int RtcMCP79412::setAlarm(unsigned int Seconds, bool AlarmNum) { + return rtc.setAlarm(Seconds, AlarmNum); + } + + int RtcMCP79412::setMinuteAlarm(unsigned int Offset, bool AlarmNum) { + return rtc.setMinuteAlarm(Offset, AlarmNum); + } + + int RtcMCP79412::setHourAlarm(unsigned int Offset, bool AlarmNum) { + return rtc.setHourAlarm(Offset, AlarmNum); + } + + int RtcMCP79412::setDayAlarm(unsigned int Offset, bool AlarmNum) { + return rtc.setDayAlarm(Offset, AlarmNum); + } + + int RtcMCP79412::enableAlarm(bool State, bool AlarmNum) { + return rtc.enableAlarm(State, AlarmNum); + } + + int RtcMCP79412::clearAlarm(bool AlarmNum) { + return rtc.clearAlarm(AlarmNum); + } + + bool RtcMCP79412::readAlarm(bool AlarmNum) { + return rtc.readAlarm(AlarmNum); + } + + String RtcMCP79412::getUUIDString() { + return rtc.getUUIDString(); + } + + uint8_t RtcMCP79412::readByte(int Reg) { + return rtc.readByte(Reg); + } + + uint8_t RtcMCP79412::getErrorsArray(uint32_t errors[]) { + return rtc.getErrorsArray(errors); + } + + int RtcMCP79412::throwError(uint32_t error) { + return rtc.throwError(error); + } \ No newline at end of file diff --git a/src/hardware/RtcMCP79412.h b/src/hardware/RtcMCP79412.h new file mode 100644 index 0000000..2fd750a --- /dev/null +++ b/src/hardware/RtcMCP79412.h @@ -0,0 +1,47 @@ +/** + * @file RtcMCP79412.h + * @brief Concrete implementation of IRtc using the MCP79412 real-time clock. + * + * Adapts the MCP79412 real-time clock to the IRtc interface + * for dependency injection and testing. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #ifndef RTC_MCP79412_H + #define RTC_MCP79412_H + + #include "IRtc.h" + #include "MCP79412.h" // Include the actual MCP79412 library + + /** + * @brief Concrete implementation of IRtc using MCP79412 + */ + class RtcMCP79412 : public IRtc { + public: + RtcMCP79412(); + ~RtcMCP79412() override = default; + + int begin(bool UseExtOsc = false) override; + int setTime(int Year, int Month, int Day, int DoW, int Hour, int Min, int Sec) override; + int setTime(int Year, int Month, int Day, int Hour, int Min, int Sec) override; + Timestamp getRawTime() override; + time_t getTimeUnix() override; + int setMode(Mode Val) override; + int setAlarm(unsigned int Seconds, bool AlarmNum = false) override; + int setMinuteAlarm(unsigned int Offset, bool AlarmNum = false) override; + int setHourAlarm(unsigned int Offset, bool AlarmNum = false) override; + int setDayAlarm(unsigned int Offset, bool AlarmNum = false) override; + int enableAlarm(bool State = true, bool AlarmNum = false) override; + int clearAlarm(bool AlarmNum = false) override; + bool readAlarm(bool AlarmNum = false) override; + String getUUIDString() override; + uint8_t readByte(int Reg) override; + uint8_t getErrorsArray(uint32_t errors[]) override; + int throwError(uint32_t error) override; + + private: + MCP79412 rtc; // The concrete MCP79412 instance + }; + + #endif // RTC_MCP79412_H \ No newline at end of file diff --git a/test/mocks/MockMCP79412.h b/test/mocks/MockMCP79412.h index 53f91d8..d32d529 100644 --- a/test/mocks/MockMCP79412.h +++ b/test/mocks/MockMCP79412.h @@ -1,17 +1,40 @@ -#ifndef MOCK_MCP79412_H -#define MOCK_MCP79412_H +/** + * @file MockMCP79412.h + * @brief Mock implementation of IRtc for testing. + * + * This class uses Google Mock to create a testable version of the RTC interface. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ -#include -#include "MCP79412.h" - -class MockMCP79412 : public MCP79412 { -public: - MOCK_METHOD(int, begin, (bool exOsc)); - MOCK_METHOD(time_t, getTimeUnix, ()); - MOCK_METHOD(String, getUUIDString, ()); - MOCK_METHOD(int, setMode, (MCP79412::Mode mode)); - MOCK_METHOD(int, enableAlarm, (bool enable, uint8_t alarmNum)); - MOCK_METHOD(uint8_t, readByte, (uint8_t address)); -}; - -#endif // MOCK_MCP79412_H \ No newline at end of file + #ifndef MOCK_MCP79412_H + #define MOCK_MCP79412_H + + #include + #include "IRtc.h" + + /** + * @brief Google Mock implementation of IRtc for testing. + */ + class MockMCP79412 : public IRtc { + public: + // Mock all methods defined in the interface + MOCK_METHOD(int, begin, (bool UseExtOsc), (override)); + MOCK_METHOD(int, setTime, (int Year, int Month, int Day, int DoW, int Hour, int Min, int Sec), (override)); + MOCK_METHOD(int, setTime, (int Year, int Month, int Day, int Hour, int Min, int Sec), (override)); + MOCK_METHOD(Timestamp, getRawTime, (), (override)); + MOCK_METHOD(time_t, getTimeUnix, (), (override)); + MOCK_METHOD(int, setMode, (Mode Val), (override)); + MOCK_METHOD(int, setAlarm, (unsigned int Seconds, bool AlarmNum), (override)); + MOCK_METHOD(int, setMinuteAlarm, (unsigned int Offset, bool AlarmNum), (override)); + MOCK_METHOD(int, setHourAlarm, (unsigned int Offset, bool AlarmNum), (override)); + MOCK_METHOD(int, setDayAlarm, (unsigned int Offset, bool AlarmNum), (override)); + MOCK_METHOD(int, enableAlarm, (bool State, bool AlarmNum), (override)); + MOCK_METHOD(int, clearAlarm, (bool AlarmNum), (override)); + MOCK_METHOD(bool, readAlarm, (bool AlarmNum), (override)); + MOCK_METHOD(uint8_t, readByte, (int Reg), (override)); + MOCK_METHOD(uint8_t, getErrorsArray, (uint32_t errors[]), (override)); + MOCK_METHOD(int, throwError, (uint32_t error), (override)); + }; + + #endif // MOCK_MCP79412_H \ No newline at end of file From 13acbfefb7e7d2f46cabfdb1a915fae171fd9f08 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 24 Apr 2025 09:19:54 -0500 Subject: [PATCH 25/48] abstracted ambient light sensor --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 8 +-- src/hardware/AmbientLightVEML3328.cpp | 80 +++++++++++++++++++++++++ src/hardware/AmbientLightVEML3328.h | 35 +++++++++++ test/mocks/MockVEML3328.h | 38 ++++++++---- 6 files changed, 149 insertions(+), 16 deletions(-) create mode 100644 src/hardware/AmbientLightVEML3328.cpp create mode 100644 src/hardware/AmbientLightVEML3328.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 702bb9a..c4084ab 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 702bb9ab701326f3c6a297d5056f7808015ebced +Subproject commit c4084ab902f444be5cf32d534a1da82996075b05 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index ff86d2e..543750a 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit ff86d2e2d6df177649cf5c2b36cb5edfdeadca06 +Subproject commit 543750af8a7996c59718cb50333cee558d0913ab diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index e0f3b3a..b1a63b0 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -71,6 +71,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/CurrentSenseAmplifierPAC1934.h" #include "hardware/LedPCA9634.h" #include "hardware/RtcMCP79412.h" +#include "hardware/AmbientLightVEML3328.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -90,13 +91,11 @@ ParticleHardwareSerial realSerialSdi12; IOExpanderPCAL9535A realIoOB(0x20); //0x20 is the PCAL Base address IOExpanderPCAL9535A realIoTalon(0x21); - CurrentSenseAmplifierPAC1934 realCsaAlpha(2,2,2,2,0x18); CurrentSenseAmplifierPAC1934 realCsaBeta(2,10,10,10,0x14); - LedPCA9634 realLed(0x52); - -RtcMCP79412 realRtc;; +RtcMCP79412 realRtc; +AmbientLightVEML3328 realAls; Kestrel logger(realTimeProvider, realGpio, @@ -111,6 +110,7 @@ Kestrel logger(realTimeProvider, realCsaBeta, realLed, realRtc, + realAls, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/hardware/AmbientLightVEML3328.cpp b/src/hardware/AmbientLightVEML3328.cpp new file mode 100644 index 0000000..e238d60 --- /dev/null +++ b/src/hardware/AmbientLightVEML3328.cpp @@ -0,0 +1,80 @@ +/** + * @file AmbientLightVEML3328.cpp + * @brief Implementation of the AmbientLightVEML3328 class. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #include "AmbientLightVEML3328.h" + + AmbientLightVEML3328::AmbientLightVEML3328() : veml() { + // Default constructor, delegates to VEML3328 constructor + } + + int AmbientLightVEML3328::begin() { + return veml.begin(); + } + + float AmbientLightVEML3328::getValue(Channel channel) { + // Map from IAmbientLight::Channel to VEML3328::Channel + VEML3328::Channel vemlChannel; + switch (channel) { + case Channel::Clear: + vemlChannel = VEML3328::Channel::Clear; + break; + case Channel::Red: + vemlChannel = VEML3328::Channel::Red; + break; + case Channel::Green: + vemlChannel = VEML3328::Channel::Green; + break; + case Channel::Blue: + vemlChannel = VEML3328::Channel::Blue; + break; + case Channel::IR: + vemlChannel = VEML3328::Channel::IR; + break; + default: + // Default to Clear if invalid channel + vemlChannel = VEML3328::Channel::Clear; + break; + } + + return veml.GetValue(vemlChannel); + } + + float AmbientLightVEML3328::getValue(Channel channel, bool &state) { + // Map from IAmbientLight::Channel to VEML3328::Channel + VEML3328::Channel vemlChannel; + switch (channel) { + case Channel::Clear: + vemlChannel = VEML3328::Channel::Clear; + break; + case Channel::Red: + vemlChannel = VEML3328::Channel::Red; + break; + case Channel::Green: + vemlChannel = VEML3328::Channel::Green; + break; + case Channel::Blue: + vemlChannel = VEML3328::Channel::Blue; + break; + case Channel::IR: + vemlChannel = VEML3328::Channel::IR; + break; + default: + // Default to Clear if invalid channel + vemlChannel = VEML3328::Channel::Clear; + break; + } + + return veml.GetValue(vemlChannel, state); + } + + float AmbientLightVEML3328::getLux() { + return veml.GetLux(); + } + + int AmbientLightVEML3328::autoRange() { + return veml.AutoRange(); + } \ No newline at end of file diff --git a/src/hardware/AmbientLightVEML3328.h b/src/hardware/AmbientLightVEML3328.h new file mode 100644 index 0000000..3318739 --- /dev/null +++ b/src/hardware/AmbientLightVEML3328.h @@ -0,0 +1,35 @@ +/** + * @file AmbientLightVEML3328.h + * @brief Concrete implementation of IAmbientLight using VEML3328 + * + * Adapts the VEML3328 ambient light sensor to the IAmbientLight interface + * for dependency injection and testing. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #ifndef AMBIENT_LIGHT_VEML3328_H + #define AMBIENT_LIGHT_VEML3328_H + + #include "IAmbientLight.h" + #include "VEML3328.h" // Include the actual VEML3328 library + + /** + * @brief Concrete implementation of IAmbientLight using VEML3328 + */ + class AmbientLightVEML3328 : public IAmbientLight { + public: + AmbientLightVEML3328(); + ~AmbientLightVEML3328() override = default; + + int begin() override; + float getValue(Channel channel) override; + float getValue(Channel channel, bool &state) override; + float getLux() override; + int autoRange() override; + + private: + VEML3328 veml; // The concrete VEML3328 instance + }; + + #endif // AMBIENT_LIGHT_VEML3328_H \ No newline at end of file diff --git a/test/mocks/MockVEML3328.h b/test/mocks/MockVEML3328.h index 9143545..116ad5e 100644 --- a/test/mocks/MockVEML3328.h +++ b/test/mocks/MockVEML3328.h @@ -1,11 +1,29 @@ -#include -#include "VEML3328.h" +/** + * @file MockVEML3328.h + * @brief Mock implementation of IAmbientLight for testing. + * + * This class uses Google Mock to create a testable version of the ambient light sensor interface. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ -class MockVEML3328 : public VEML3328 { - public: - MOCK_METHOD(int, begin, ()); - MOCK_METHOD(int, autoRange, ()); - MOCK_METHOD(float, GetValue, (Channel Param, bool &State)); - MOCK_METHOD(float, GetValue, (Channel Param)); - MOCK_METHOD(float, GetLux, ()); -}; \ No newline at end of file + #ifndef MOCK_VEML3328_H + #define MOCK_VEML3328_H + + #include + #include "IAmbientLight.h" + + /** + * @brief Google Mock implementation of IAmbientLight for testing. + */ + class MockVEML3328 : public IAmbientLight { + public: + // Mock all methods defined in the interface + MOCK_METHOD(int, begin, (), (override)); + MOCK_METHOD(float, getValue, (Channel channel), (override)); + MOCK_METHOD(float, getValue, (Channel channel, bool &state), (override)); + MOCK_METHOD(float, getLux, (), (override)); + MOCK_METHOD(int, autoRange, (), (override)); + }; + + #endif // MOCK_VEML3328_H \ No newline at end of file From 1070c7f071d96d8d0e7af426d7906e9e755a491d Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 29 Apr 2025 10:05:11 -0500 Subject: [PATCH 26/48] wip gps refactoring --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 11 ++- src/hardware/GpsSFE_UBLOX_GNSS.cpp | 112 ++++++++++++++++++++++++ src/hardware/GpsSFE_UBLOX_GNSS.h | 60 +++++++++++++ test/mocks/MockSFE_UBLOX_GNSS.h | 67 ++++++++------ 6 files changed, 221 insertions(+), 33 deletions(-) create mode 100644 src/hardware/GpsSFE_UBLOX_GNSS.cpp create mode 100644 src/hardware/GpsSFE_UBLOX_GNSS.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index c4084ab..a93daa1 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit c4084ab902f444be5cf32d534a1da82996075b05 +Subproject commit a93daa1c644f304a774b646d62a0cbd57481a077 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 543750a..d4f48e5 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 543750af8a7996c59718cb50333cee558d0913ab +Subproject commit d4f48e520a2fbaca44954ad4cce61c5ed07fe270 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index b1a63b0..370e012 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -72,6 +72,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/LedPCA9634.h" #include "hardware/RtcMCP79412.h" #include "hardware/AmbientLightVEML3328.h" +#include "hardware/GpsSFE_UBLOX_GNSS.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -96,6 +97,7 @@ CurrentSenseAmplifierPAC1934 realCsaBeta(2,10,10,10,0x14); LedPCA9634 realLed(0x52); RtcMCP79412 realRtc; AmbientLightVEML3328 realAls; +GpsSFE_UBLOX_GNSS realGps; Kestrel logger(realTimeProvider, realGpio, @@ -111,6 +113,7 @@ Kestrel logger(realTimeProvider, realLed, realRtc, realAls, + realGps, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 @@ -329,13 +332,13 @@ void setup() { // fileSys.writeToSD(initDiagnostic, "Dummy.txt"); #ifndef RAPID_START //Only do this if not rapid starting - while((!Particle.connected() || logger.gps.getFixType() == 0) && (millis() - startTime) < maxConnectTime) { //Wait while at least one of the remote systems is not connected + while((!Particle.connected() || logger.m_gps.getFixType() == 0) && (millis() - startTime) < maxConnectTime) { //Wait while at least one of the remote systems is not connected if(Particle.connected()) { logger.setIndicatorState(IndicatorLight::CELL, IndicatorMode::PASS); //If cell is connected, set to PASS state if(WAIT_GPS == false) break; //If not told to wait for GPS, break out after cell is connected } - if(logger.gps.getTimeValid() == true) { - if(logger.gps.getFixType() >= 2 && logger.gps.getFixType() <= 4 && logger.gps.getGnssFixOk()) { //If you get a 2D fix or better, pass GPS + if(logger.m_gps.getTimeValid() == true) { + if(logger.m_gps.getFixType() >= 2 && logger.m_gps.getFixType() <= 4 && logger.m_gps.getGnssFixOk()) { //If you get a 2D fix or better, pass GPS logger.setIndicatorState(IndicatorLight::GPS, IndicatorMode::PASS); } else { @@ -352,7 +355,7 @@ void setup() { logger.setIndicatorState(IndicatorLight::CELL, IndicatorMode::ERROR); //If cell still not connected, display error // Particle.disconnect(); //DEBUG! } - if(logger.gps.getFixType() >= 2 && logger.gps.getFixType() <= 4 && logger.gps.getGnssFixOk()) { //Make fix report is in range and fix is OK + if(logger.m_gps.getFixType() >= 2 && logger.m_gps.getFixType() <= 4 && logger.m_gps.getGnssFixOk()) { //Make fix report is in range and fix is OK logger.setIndicatorState(IndicatorLight::GPS, IndicatorMode::PASS); //Catches connection of GPS is second device to connect } else { diff --git a/src/hardware/GpsSFE_UBLOX_GNSS.cpp b/src/hardware/GpsSFE_UBLOX_GNSS.cpp new file mode 100644 index 0000000..725680d --- /dev/null +++ b/src/hardware/GpsSFE_UBLOX_GNSS.cpp @@ -0,0 +1,112 @@ +/** + * @file GpsSFE_UBLOX_GNSS.cpp + * @brief Implementation of GpsSFE_UBLOX_GNSS class + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + +#include "GpsSFE_UBLOX_GNSS.h" + +GpsSFE_UBLOX_GNSS::GpsSFE_UBLOX_GNSS() : gps(){ + //default constructor +} + +bool GpsSFE_UBLOX_GNSS::begin() { + return gps.begin(); +} + +void GpsSFE_UBLOX_GNSS::setI2COutput(uint8_t comType) { + gps.setI2COutput(comType); +} + +bool GpsSFE_UBLOX_GNSS::setNavigationFrequency(uint8_t navFreq) { + return gps.setNavigationFrequency(navFreq); +} + +void GpsSFE_UBLOX_GNSS::setAutoPVT(bool autoPVT) { + gps.setAutoPVT(autoPVT); +} + +uint8_t GpsSFE_UBLOX_GNSS::getNavigationFrequency() { + return gps.getNavigationFrequency(); +} + +uint8_t GpsSFE_UBLOX_GNSS::getMeasurementRate() { + return gps.getMeasurementRate(); +} + +uint8_t GpsSFE_UBLOX_GNSS::getNavigationRate() { + return gps.getNavigationRate(); +} + +int16_t GpsSFE_UBLOX_GNSS::getATTroll() { + return gps.getATTroll(); +} + +int16_t GpsSFE_UBLOX_GNSS::getATTpitch() { + return gps.getATTpitch(); +} + +int16_t GpsSFE_UBLOX_GNSS::getATTheading() { + return gps.getATTheading(); +} + +void GpsSFE_UBLOX_GNSS::setPacketCfgPayloadSize(uint16_t payloadSize) { + gps.setPacketCfgPayloadSize(payloadSize); +} + +uint8_t GpsSFE_UBLOX_GNSS::getSIV() { + return gps.getSIV(); +} + +uint8_t GpsSFE_UBLOX_GNSS::getFixType() { + return gps.getFixType(); +} + +bool GpsSFE_UBLOX_GNSS::getPVT() { + return gps.getPVT(); +} + +bool GpsSFE_UBLOX_GNSS::getGnssFixOk() { + return gps.getGnssFixOk(); +} + +long GpsSFE_UBLOX_GNSS::getAltitude() { + return gps.getAltitude(); +} + +long GpsSFE_UBLOX_GNSS::getLongitude() { + return gps.getLongitude(); +} + +long GpsSFE_UBLOX_GNSS::getLatitude() { + return gps.getLatitude(); +} + +uint8_t GpsSFE_UBLOX_GNSS::getHour() { + return gps.getHour(); +} + +uint8_t GpsSFE_UBLOX_GNSS::getMinute() { + return gps.getMinute(); +} + +uint8_t GpsSFE_UBLOX_GNSS::getSecond() { + return gps.getSecond(); +} + +bool GpsSFE_UBLOX_GNSS::getDateValid() { + return gps.getDateValid(); +} + +bool GpsSFE_UBLOX_GNSS::getTimeValid() { + return gps.getTimeValid(); +} + +bool GpsSFE_UBLOX_GNSS::getTimeFullyResolved() { + return gps.getTimeFullyResolved(); +} + +bool GpsSFE_UBLOX_GNSS::powerOffWithInterrupt(bool enableInterrupt) { + return gps.powerOffWithInterrupt(enableInterrupt); +} \ No newline at end of file diff --git a/src/hardware/GpsSFE_UBLOX_GNSS.h b/src/hardware/GpsSFE_UBLOX_GNSS.h new file mode 100644 index 0000000..8708652 --- /dev/null +++ b/src/hardware/GpsSFE_UBLOX_GNSS.h @@ -0,0 +1,60 @@ +/** + * @file GpsSFE_UBLOX_GNSS.h + * @brief Concrete implementation of IGPS using SparkFun u-blox GNSS library + * + * Adapts the SparkFun u-blox GNSS library to the IGPS interface + * for dependency injection and testing. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + +#ifndef GPS_SFE_UBLOX_GNSS_H +#define GPS_SFE_UBLOX_GNSS_H + +#include "../../lib/FlightControl-hardware-dependencies/src/IGps.h" +#include "../../lib/SparkFun_u-blox_GNSS_Arduino_Library/src/SparkFun_u-blox_GNSS_Arduino_Library.h" +#include "../../lib/FlightControl-platform-dependencies/src/IWire.h" + +/** + * @brief Concrete implementation of IGPS using SFE_UBLOX_GNSS + */ +class GpsSFE_UBLOX_GNSS : public IGps { +public: + /** + * @brief Constructor + * @param wire IWire instance for I2C communication + */ + GpsSFE_UBLOX_GNSS(); + ~GpsSFE_UBLOX_GNSS() override = default; + + bool begin() override; + void setI2COutput(uint8_t comType) override; + bool setNavigationFrequency(uint8_t navFreq) override; + void setAutoPVT(bool autoPVT) override; + uint8_t getNavigationFrequency() override; + uint8_t getMeasurementRate() override; + uint8_t getNavigationRate() override; + int16_t getATTroll() override; + int16_t getATTpitch() override; + int16_t getATTheading() override; + void setPacketCfgPayloadSize(uint16_t payloadSize) override; + uint8_t getSIV() override; + uint8_t getFixType() override; + bool getPVT() override; + bool getGnssFixOk() override; + long getAltitude() override; + long getLongitude() override; + long getLatitude() override; + uint8_t getHour() override; + uint8_t getMinute() override; + uint8_t getSecond() override; + bool getDateValid() override; + bool getTimeValid() override; + bool getTimeFullyResolved() override; + bool powerOffWithInterrupt(bool enableInterrupt) override; + +private: + SFE_UBLOX_GNSS gps; // The concrete GPS instance +}; + +#endif // GPS_SFE_UBLOX_GNSS_H \ No newline at end of file diff --git a/test/mocks/MockSFE_UBLOX_GNSS.h b/test/mocks/MockSFE_UBLOX_GNSS.h index 9582d3a..131d3ff 100644 --- a/test/mocks/MockSFE_UBLOX_GNSS.h +++ b/test/mocks/MockSFE_UBLOX_GNSS.h @@ -1,36 +1,49 @@ +/** + * @file MockSFE_UBLOX_GNSS.h + * @brief Mock implementation of IGPS for testing + * + * This class uses Google Mock to create a testable version of the GPS interface. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + #ifndef MOCK_SFE_UBLOX_GNSS_H #define MOCK_SFE_UBLOX_GNSS_H #include -#include "SparkFun_u-blox_GNSS_Arduino_Library.h" - -// Definitions for protocol types -#define COM_TYPE_UBX 0x01 - -// UBX classes and message IDs -#define UBX_CLASS_NAV 0x01 -#define UBX_NAV_STATUS 0x03 -#define UBX_CLASS_CFG 0x06 -#define UBX_MON_VER 0x04 - -// Default payload size -#define MAX_PAYLOAD_SIZE 256 +#include "../../lib/FlightControl-hardware-dependencies/src/IGps.h" -// Mock for SFE_UBLOX_GNSS -class MockSFE_UBLOX_GNSS : public SFE_UBLOX_GNSS { +/** + * @brief Google Mock implementation of IGPS for testing + */ +class MockSFE_UBLOX_GNSS : public IGPS { public: - MOCK_METHOD(bool, begin, ()); - MOCK_METHOD(void, setI2COutput, (uint8_t comType)); - MOCK_METHOD(bool, setNavigationFrequency, (uint8_t navFreq)); - MOCK_METHOD(void, setAutoPVT, (bool autoPVT)); - MOCK_METHOD(uint8_t, getNavigationFrequency, ()); - MOCK_METHOD(uint8_t, getMeasurementRate, ()); - MOCK_METHOD(uint8_t, getNavigationRate, ()); - MOCK_METHOD(int16_t, getATTroll, ()); - MOCK_METHOD(int16_t, getATTpitch, ()); - MOCK_METHOD(int16_t, getATTheading, ()); - MOCK_METHOD(void, setPacketCfgPayloadSize, (uint16_t payloadSize)); - MOCK_METHOD(uint8_t, sendCommand, (ubxPacket* outgoingUBX, uint16_t maxWait)); + // Mock all methods defined in the interface + MOCK_METHOD(bool, begin, (), (override)); + MOCK_METHOD(void, setI2COutput, (uint8_t comType), (override)); + MOCK_METHOD(bool, setNavigationFrequency, (uint8_t navFreq), (override)); + MOCK_METHOD(void, setAutoPVT, (bool autoPVT), (override)); + MOCK_METHOD(uint8_t, getNavigationFrequency, (), (override)); + MOCK_METHOD(uint8_t, getMeasurementRate, (), (override)); + MOCK_METHOD(uint8_t, getNavigationRate, (), (override)); + MOCK_METHOD(int16_t, getATTroll, (), (override)); + MOCK_METHOD(int16_t, getATTpitch, (), (override)); + MOCK_METHOD(int16_t, getATTheading, (), (override)); + MOCK_METHOD(void, setPacketCfgPayloadSize, (uint16_t payloadSize), (override)); + MOCK_METHOD(uint8_t, getSIV, (), (override)); + MOCK_METHOD(uint8_t, getFixType, (), (override)); + MOCK_METHOD(bool, getPVT, (), (override)); + MOCK_METHOD(bool, getGnssFixOk, (), (override)); + MOCK_METHOD(long, getAltitude, (), (override)); + MOCK_METHOD(long, getLongitude, (), (override)); + MOCK_METHOD(long, getLatitude, (), (override)); + MOCK_METHOD(uint8_t, getHour, (), (override)); + MOCK_METHOD(uint8_t, getMinute, (), (override)); + MOCK_METHOD(uint8_t, getSecond, (), (override)); + MOCK_METHOD(bool, getDateValid, (), (override)); + MOCK_METHOD(bool, getTimeValid, (), (override)); + MOCK_METHOD(bool, getTimeFullyResolved, (), (override)); + MOCK_METHOD(bool, powerOffWithInterrupt, (bool enableInterrupt), (override)); }; #endif // MOCK_SFE_UBLOX_GNSS_H \ No newline at end of file From c0e6bf3baa354f8789dc071e4ff5c70bba72d9d8 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 29 Apr 2025 14:43:43 -0500 Subject: [PATCH 27/48] fixed gps refactoring --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/hardware/GpsSFE_UBLOX_GNSS.cpp | 65 ++++++++++++++++++- src/hardware/GpsSFE_UBLOX_GNSS.h | 4 +- test/mocks/MockSFE_UBLOX_GNSS.h | 86 +++++++++++++------------ 5 files changed, 113 insertions(+), 46 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index a93daa1..d407c5e 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit a93daa1c644f304a774b646d62a0cbd57481a077 +Subproject commit d407c5eafc61286095cf95f62ca7caa89c97eec8 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index d4f48e5..1217881 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit d4f48e520a2fbaca44954ad4cce61c5ed07fe270 +Subproject commit 1217881291d82cdd1c585e0de220120dd2e04d1e diff --git a/src/hardware/GpsSFE_UBLOX_GNSS.cpp b/src/hardware/GpsSFE_UBLOX_GNSS.cpp index 725680d..ab71b73 100644 --- a/src/hardware/GpsSFE_UBLOX_GNSS.cpp +++ b/src/hardware/GpsSFE_UBLOX_GNSS.cpp @@ -107,6 +107,67 @@ bool GpsSFE_UBLOX_GNSS::getTimeFullyResolved() { return gps.getTimeFullyResolved(); } -bool GpsSFE_UBLOX_GNSS::powerOffWithInterrupt(bool enableInterrupt) { - return gps.powerOffWithInterrupt(enableInterrupt); +bool GpsSFE_UBLOX_GNSS::powerOffWithInterrupt(uint32_t durationInMs, uint32_t wakeupSources, bool forceWhileUsb /*= true*/) { + // Directly call the underlying library function with the provided arguments. + // The SFE library function has a 'maxWait' parameter which we can use a default for here, + // or you could add it to the interface if needed. Using default for now. + // Note: The SFE library uses defaultMaxWait (often 1100ms) + return gps.powerOffWithInterrupt(durationInMs, wakeupSources, forceWhileUsb); // Pass arguments directly +} + +Isfe_ublox_status_e GpsSFE_UBLOX_GNSS::sendCommand(IUbxPacket *outgoingUBX, uint16_t maxWait) { + + if (outgoingUBX == nullptr) { + return INVALID_ARG; // Or another appropriate error status + } + + // 1. Create a temporary ubxPacket structure compatible with the SFE library + ubxPacket internalPacket; + + // 2. Copy the essential fields from the interface packet (IUbxPacket) + // to the internal packet (ubxPacket). + internalPacket.cls = outgoingUBX->cls; + internalPacket.id = outgoingUBX->id; + internalPacket.len = outgoingUBX->len; + internalPacket.payload = outgoingUBX->payload; // Share the payload pointer + + // These fields are typically managed by the SFE library during sending (checksum) + // or receiving (counter, startingSpot, validity flags), so initialize reasonably. + internalPacket.counter = 0; + internalPacket.startingSpot = 0; + internalPacket.checksumA = 0; // Will be calculated by gps.sendCommand() + internalPacket.checksumB = 0; // Will be calculated by gps.sendCommand() + internalPacket.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; // Status set upon reception if used + internalPacket.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; // Status set upon reception if used + + // 3. Call the underlying SFE library's sendCommand function, passing the address + // of the temporary internalPacket. + sfe_ublox_status_e status = gps.sendCommand(&internalPacket, maxWait); + + // 4. Translate the SFE library's status enum (sfe_ublox_status_e) + // to the interface's status enum (Isfe_ublox_status_e). + Isfe_ublox_status_e returnStatus; + switch (status) { + case SFE_UBLOX_STATUS_SUCCESS: returnStatus = SUCCESS; break; + case SFE_UBLOX_STATUS_FAIL: returnStatus = FAIL; break; + case SFE_UBLOX_STATUS_CRC_FAIL: returnStatus = CRC_FAIL; break; + case SFE_UBLOX_STATUS_TIMEOUT: returnStatus = TIMEOUT; break; + case SFE_UBLOX_STATUS_COMMAND_NACK: returnStatus = COMMAND_NACK; break; + case SFE_UBLOX_STATUS_OUT_OF_RANGE: returnStatus = OUT_OF_RANGE; break; + case SFE_UBLOX_STATUS_INVALID_ARG: returnStatus = INVALID_ARG; break; + case SFE_UBLOX_STATUS_INVALID_OPERATION:returnStatus = INVALID_OPERATION; break; + case SFE_UBLOX_STATUS_MEM_ERR: returnStatus = MEM_ERR; break; + case SFE_UBLOX_STATUS_HW_ERR: returnStatus = HW_ERR; break; + case SFE_UBLOX_STATUS_DATA_SENT: returnStatus = DATA_SENT; break; + case SFE_UBLOX_STATUS_DATA_RECEIVED: returnStatus = DATA_RECEIVED; break; + case SFE_UBLOX_STATUS_I2C_COMM_FAILURE: returnStatus = I2C_COMM_FAILURE; break; + case SFE_UBLOX_STATUS_DATA_OVERWRITTEN: returnStatus = DATA_OVERWRITTEN; break; + default: + // Handle any unexpected status from the SFE library, map to a generic failure. + returnStatus = FAIL; + break; + } + + // 5. Return the translated status defined by the IGps interface. + return returnStatus; } \ No newline at end of file diff --git a/src/hardware/GpsSFE_UBLOX_GNSS.h b/src/hardware/GpsSFE_UBLOX_GNSS.h index 8708652..5983be5 100644 --- a/src/hardware/GpsSFE_UBLOX_GNSS.h +++ b/src/hardware/GpsSFE_UBLOX_GNSS.h @@ -51,8 +51,8 @@ class GpsSFE_UBLOX_GNSS : public IGps { bool getDateValid() override; bool getTimeValid() override; bool getTimeFullyResolved() override; - bool powerOffWithInterrupt(bool enableInterrupt) override; - + bool powerOffWithInterrupt(uint32_t durationInMs, uint32_t wakeupSources, bool forceWhileUsb = true) override; + Isfe_ublox_status_e sendCommand(IUbxPacket *outgoingUBX, uint16_t maxWait) override; private: SFE_UBLOX_GNSS gps; // The concrete GPS instance }; diff --git a/test/mocks/MockSFE_UBLOX_GNSS.h b/test/mocks/MockSFE_UBLOX_GNSS.h index 131d3ff..0461751 100644 --- a/test/mocks/MockSFE_UBLOX_GNSS.h +++ b/test/mocks/MockSFE_UBLOX_GNSS.h @@ -7,43 +7,49 @@ * © 2025 Regents of the University of Minnesota. All rights reserved. */ -#ifndef MOCK_SFE_UBLOX_GNSS_H -#define MOCK_SFE_UBLOX_GNSS_H - -#include -#include "../../lib/FlightControl-hardware-dependencies/src/IGps.h" - -/** - * @brief Google Mock implementation of IGPS for testing - */ -class MockSFE_UBLOX_GNSS : public IGPS { -public: - // Mock all methods defined in the interface - MOCK_METHOD(bool, begin, (), (override)); - MOCK_METHOD(void, setI2COutput, (uint8_t comType), (override)); - MOCK_METHOD(bool, setNavigationFrequency, (uint8_t navFreq), (override)); - MOCK_METHOD(void, setAutoPVT, (bool autoPVT), (override)); - MOCK_METHOD(uint8_t, getNavigationFrequency, (), (override)); - MOCK_METHOD(uint8_t, getMeasurementRate, (), (override)); - MOCK_METHOD(uint8_t, getNavigationRate, (), (override)); - MOCK_METHOD(int16_t, getATTroll, (), (override)); - MOCK_METHOD(int16_t, getATTpitch, (), (override)); - MOCK_METHOD(int16_t, getATTheading, (), (override)); - MOCK_METHOD(void, setPacketCfgPayloadSize, (uint16_t payloadSize), (override)); - MOCK_METHOD(uint8_t, getSIV, (), (override)); - MOCK_METHOD(uint8_t, getFixType, (), (override)); - MOCK_METHOD(bool, getPVT, (), (override)); - MOCK_METHOD(bool, getGnssFixOk, (), (override)); - MOCK_METHOD(long, getAltitude, (), (override)); - MOCK_METHOD(long, getLongitude, (), (override)); - MOCK_METHOD(long, getLatitude, (), (override)); - MOCK_METHOD(uint8_t, getHour, (), (override)); - MOCK_METHOD(uint8_t, getMinute, (), (override)); - MOCK_METHOD(uint8_t, getSecond, (), (override)); - MOCK_METHOD(bool, getDateValid, (), (override)); - MOCK_METHOD(bool, getTimeValid, (), (override)); - MOCK_METHOD(bool, getTimeFullyResolved, (), (override)); - MOCK_METHOD(bool, powerOffWithInterrupt, (bool enableInterrupt), (override)); -}; - -#endif // MOCK_SFE_UBLOX_GNSS_H \ No newline at end of file + #ifndef MOCK_SFE_UBLOX_GNSS_H + #define MOCK_SFE_UBLOX_GNSS_H + + #include + // Use the correct relative path to your IGps.h header + #include "../../lib/FlightControl-hardware-dependencies/src/IGps.h" // Assuming this path is correct + + /** + * @brief Google Mock implementation of IGPS for testing + */ + class MockSFE_UBLOX_GNSS : public IGps { // Ensure IGps is the correct base class name + public: + // Make sure the base class destructor is virtual if you derive from it + virtual ~MockSFE_UBLOX_GNSS() = default; + + // Mock all methods defined in the interface + MOCK_METHOD(bool, begin, (), (override)); + MOCK_METHOD(void, setI2COutput, (uint8_t comType), (override)); + MOCK_METHOD(bool, setNavigationFrequency, (uint8_t navFreq), (override)); + MOCK_METHOD(void, setAutoPVT, (bool autoPVT), (override)); + MOCK_METHOD(uint8_t, getNavigationFrequency, (), (override)); + MOCK_METHOD(uint8_t, getMeasurementRate, (), (override)); + MOCK_METHOD(uint8_t, getNavigationRate, (), (override)); + MOCK_METHOD(int16_t, getATTroll, (), (override)); + MOCK_METHOD(int16_t, getATTpitch, (), (override)); + MOCK_METHOD(int16_t, getATTheading, (), (override)); + MOCK_METHOD(void, setPacketCfgPayloadSize, (uint16_t payloadSize), (override)); + MOCK_METHOD(uint8_t, getSIV, (), (override)); + MOCK_METHOD(uint8_t, getFixType, (), (override)); + MOCK_METHOD(bool, getPVT, (), (override)); + MOCK_METHOD(bool, getGnssFixOk, (), (override)); + MOCK_METHOD(long, getAltitude, (), (override)); + MOCK_METHOD(long, getLongitude, (), (override)); + MOCK_METHOD(long, getLatitude, (), (override)); + MOCK_METHOD(uint8_t, getHour, (), (override)); + MOCK_METHOD(uint8_t, getMinute, (), (override)); + MOCK_METHOD(uint8_t, getSecond, (), (override)); + MOCK_METHOD(bool, getDateValid, (), (override)); + MOCK_METHOD(bool, getTimeValid, (), (override)); + MOCK_METHOD(bool, getTimeFullyResolved, (), (override)); + MOCK_METHOD(bool, powerOffWithInterrupt, (uint32_t durationInMs, uint32_t wakeupSources, bool forceWhileUsb), (override)); + MOCK_METHOD(Isfe_ublox_status_e, sendCommand, (IUbxPacket *outgoingUBX, uint16_t maxWait), (override)); + + }; + + #endif // MOCK_SFE_UBLOX_GNSS_H \ No newline at end of file From c8ce135591cd34ad3a3f1a4826f8f00d2cbe1f7d Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Tue, 29 Apr 2025 15:46:58 -0500 Subject: [PATCH 28/48] added haar --- src/FlightControl_Demo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 370e012..19d8e70 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -165,7 +165,7 @@ Hedorah gas(0, 0, 0x10); //Instantiate CO2 sensor with default ports and v1.0 ha LI710 et(realTimeProvider, realSdi12, 0, 0); //Instantiate ET sensor with default ports and unknown version, pass over SDI12 Talon interface BaroVue10 campPressure(sdi12, 0, 0x00); // Instantiate Barovue10 with default ports and v0.0 hardware -const uint8_t numSensors = 7; //Number must match the number of objects defined in `sensors` array +const uint8_t numSensors = 8; //Number must match the number of objects defined in `sensors` array Sensor* const sensors[numSensors] = { &fileSys, @@ -174,8 +174,8 @@ Sensor* const sensors[numSensors] = { &sdi12, &battery, &logger, //Add sensors after this line - &et - // &haar + &et, + &haar // &soil1, // &apogeeSolar, From ce62556eca2d5318f3e1b660f584386b25dc8e66 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 30 Apr 2025 11:43:51 -0500 Subject: [PATCH 29/48] refactored temp/humididty adafruit sensor --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index d407c5e..93c1cdc 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit d407c5eafc61286095cf95f62ca7caa89c97eec8 +Subproject commit 93c1cdc28529ab3ff24b773b72055137b3219604 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 1217881..3ad0fb4 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 1217881291d82cdd1c585e0de220120dd2e04d1e +Subproject commit 3ad0fb4f50b621baea59b5fd798f1dfeb259c91c diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 19d8e70..dc3b097 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -73,6 +73,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/RtcMCP79412.h" #include "hardware/AmbientLightVEML3328.h" #include "hardware/GpsSFE_UBLOX_GNSS.h" +#include "hardware/HumidityTemperatureAdafruit_SHT4X.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -98,6 +99,7 @@ LedPCA9634 realLed(0x52); RtcMCP79412 realRtc; AmbientLightVEML3328 realAls; GpsSFE_UBLOX_GNSS realGps; +HumidityTemperatureAdafruit_SHT4X realTempHumidity; Kestrel logger(realTimeProvider, realGpio, @@ -114,6 +116,7 @@ Kestrel logger(realTimeProvider, realRtc, realAls, realGps, + realTempHumidity, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 From 0a79101f6ad2bfeed42a787ef27f5f838695af53 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 30 Apr 2025 11:44:27 -0500 Subject: [PATCH 30/48] forgot to added temp/humididty files --- .../HumidityTemperatureAdafruit_SHT4X.cpp | 74 +++++++++++++++++++ .../HumidityTemperatureAdafruit_SHT4X.h | 67 +++++++++++++++++ test/mocks/MockAdafruit_SHT4X.h | 33 +++++++++ 3 files changed, 174 insertions(+) create mode 100644 src/hardware/HumidityTemperatureAdafruit_SHT4X.cpp create mode 100644 src/hardware/HumidityTemperatureAdafruit_SHT4X.h create mode 100644 test/mocks/MockAdafruit_SHT4X.h diff --git a/src/hardware/HumidityTemperatureAdafruit_SHT4X.cpp b/src/hardware/HumidityTemperatureAdafruit_SHT4X.cpp new file mode 100644 index 0000000..f51324c --- /dev/null +++ b/src/hardware/HumidityTemperatureAdafruit_SHT4X.cpp @@ -0,0 +1,74 @@ +/** + * @file HumidityTemperatureAdafruit_SHT4X.cpp + * @brief Implementation of HumidityTemperatureAdafruit_SHT4X class + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #include "HumidityTemperatureAdafruit_SHT4X.h" + + HumidityTemperatureAdafruit_SHT4X::HumidityTemperatureAdafruit_SHT4X() : currentPrecision(HT_HIGH_PRECISION) { + // Constructor initializes with high precision by default + } + + bool HumidityTemperatureAdafruit_SHT4X::begin() { + // Initialize the underlying Adafruit_SHT4x sensor + return sht4x.begin(); + } + + void HumidityTemperatureAdafruit_SHT4X::setPrecision(Iht_precision_t prec) { + // Store current precision setting + currentPrecision = prec; + + // Convert to Adafruit enum and apply to the hardware + sht4x_precision_t adafruitPrec = mapPrecision(prec); + sht4x.setPrecision(adafruitPrec); + } + + Iht_precision_t HumidityTemperatureAdafruit_SHT4X::getPrecision() { + // Return the stored precision setting + return currentPrecision; + } + + bool HumidityTemperatureAdafruit_SHT4X::getEvent(Isensors_event_t *humidity, Isensors_event_t *temp) { + //translate Isensors_event_t to Adafruit_Sensor event + sensors_event_t humidityEvent; + sensors_event_t tempEvent; + + // Call the underlying implementation to get the events + bool success = sht4x.getEvent(&humidityEvent, &tempEvent); + + //set the interface struct values + humidity->relative_humidity = humidityEvent.relative_humidity; + temp->temperature = tempEvent.temperature; + + return success; + } + + sht4x_precision_t HumidityTemperatureAdafruit_SHT4X::mapPrecision(Iht_precision_t prec) { + // Map interface precision enum to implementation precision enum + switch (prec) { + case HT_HIGH_PRECISION: + return SHT4X_HIGH_PRECISION; + case HT_MED_PRECISION: + return SHT4X_MED_PRECISION; + case HT_LOW_PRECISION: + return SHT4X_LOW_PRECISION; + default: + return SHT4X_HIGH_PRECISION; // Default to high precision + } + } + + Iht_precision_t HumidityTemperatureAdafruit_SHT4X::mapPrecision(sht4x_precision_t prec) { + // Map implementation precision enum to interface precision enum + switch (prec) { + case SHT4X_HIGH_PRECISION: + return HT_HIGH_PRECISION; + case SHT4X_MED_PRECISION: + return HT_MED_PRECISION; + case SHT4X_LOW_PRECISION: + return HT_LOW_PRECISION; + default: + return HT_HIGH_PRECISION; // Default to high precision + } + } \ No newline at end of file diff --git a/src/hardware/HumidityTemperatureAdafruit_SHT4X.h b/src/hardware/HumidityTemperatureAdafruit_SHT4X.h new file mode 100644 index 0000000..91f1293 --- /dev/null +++ b/src/hardware/HumidityTemperatureAdafruit_SHT4X.h @@ -0,0 +1,67 @@ +/** + * @file HumidityTemperatureAdafruit_SHT4X.h + * @brief Concrete implementation of IHumidityTemperature using Adafruit_SHT4x + * + * Adapts the Adafruit SHT4x humidity and temperature sensor to the IHumidityTemperature + * interface for dependency injection and testing. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #ifndef HUMIDITY_TEMPERATURE_ADAFRUIT_SHT4X_H + #define HUMIDITY_TEMPERATURE_ADAFRUIT_SHT4X_H + + #include "IHumidityTemperature.h" + #include + + /** + * @brief Concrete implementation of IHumidityTemperature using Adafruit_SHT4x + */ + class HumidityTemperatureAdafruit_SHT4X : public IHumidityTemperature { + public: + /** + * @brief Constructor + */ + HumidityTemperatureAdafruit_SHT4X(); + + /** + * @brief Destructor + */ + ~HumidityTemperatureAdafruit_SHT4X() override = default; + + /** + * @brief Initialize the sensor + * @return true on success, false on failure + */ + bool begin() override; + + /** + * @brief Sets the precision level for measurements + * @param prec The desired precision setting + */ + void setPrecision(Iht_precision_t prec) override; + + /** + * @brief Gets the current precision setting + * @return Current precision setting + */ + Iht_precision_t getPrecision() override; + + /** + * @brief Get humidity and temperature values as sensor events + * @param humidity Event object to be populated with humidity data (can be NULL) + * @param temp Event object to be populated with temperature data (can be NULL) + * @return true if read was successful + */ + bool getEvent(Isensors_event_t *humidity, Isensors_event_t *temp) override; + + private: + Adafruit_SHT4x sht4x; // The concrete SHT4x instance + Iht_precision_t currentPrecision; + + // Helper to map between interface and implementation enums + sht4x_precision_t mapPrecision(Iht_precision_t prec); + Iht_precision_t mapPrecision(sht4x_precision_t prec); + }; + + #endif // HUMIDITY_TEMPERATURE_ADAFRUIT_SHT4X_H \ No newline at end of file diff --git a/test/mocks/MockAdafruit_SHT4X.h b/test/mocks/MockAdafruit_SHT4X.h new file mode 100644 index 0000000..a7e9a0b --- /dev/null +++ b/test/mocks/MockAdafruit_SHT4X.h @@ -0,0 +1,33 @@ +/** + * @file MockAdafruit_SHT4X.h + * @brief Mock implementation of IHumidityTemperature for testing + * + * Provides a mock implementation of the IHumidityTemperature interface + * that can be used for unit testing Kestrel without hardware dependencies. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #ifndef MOCK_ADAFRUIT_SHT4X_H + #define MOCK_ADAFRUIT_SHT4X_H + + #include "IHumidityTemperature.h" + #include + + /** + * @brief Mock implementation of IHumidityTemperature for testing + */ + class MockAdafruit_SHT4X : public IHumidityTemperature { + public: + // Default constructor and destructor + MockAdafruit_SHT4X() = default; + ~MockAdafruit_SHT4X() override = default; + + // Mock methods + MOCK_METHOD(bool, begin, (), (override)); + MOCK_METHOD(void, setPrecision, (ht_precision_t prec), (override)); + MOCK_METHOD(ht_precision_t, getPrecision, (), (override)); + MOCK_METHOD(bool, getEvent, (sensors_event_t *humidity, sensors_event_t *temp), (override)); + }; + + #endif // MOCK_ADAFRUIT_SHT4X_H \ No newline at end of file From 42952b00049379595063e5310aa8f9f912b16639 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 30 Apr 2025 15:02:01 -0500 Subject: [PATCH 31/48] accelerometer refactor --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- src/FlightControl_Demo.cpp | 3 ++ src/hardware/AccelerometerMXC6655.cpp | 42 +++++++++++++++++++++++++ src/hardware/AccelerometerMXC6655.h | 40 +++++++++++++++++++++++ test/mocks/MockMXC6655.h | 37 +++++++++++++++++----- 6 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 src/hardware/AccelerometerMXC6655.cpp create mode 100644 src/hardware/AccelerometerMXC6655.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 93c1cdc..6a31fad 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 93c1cdc28529ab3ff24b773b72055137b3219604 +Subproject commit 6a31fad7a18b4b8ff0caaf07c5d0e33646975360 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 3ad0fb4..6f393b9 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 3ad0fb4f50b621baea59b5fd798f1dfeb259c91c +Subproject commit 6f393b92c09f42e49768303683de5e6b61368496 diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index dc3b097..34b4021 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -74,6 +74,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/AmbientLightVEML3328.h" #include "hardware/GpsSFE_UBLOX_GNSS.h" #include "hardware/HumidityTemperatureAdafruit_SHT4X.h" +#include "hardware/AccelerometerMXC6655.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -100,6 +101,7 @@ RtcMCP79412 realRtc; AmbientLightVEML3328 realAls; GpsSFE_UBLOX_GNSS realGps; HumidityTemperatureAdafruit_SHT4X realTempHumidity; +AccelerometerMXC6655 realAccel; Kestrel logger(realTimeProvider, realGpio, @@ -117,6 +119,7 @@ Kestrel logger(realTimeProvider, realAls, realGps, realTempHumidity, + realAccel, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/hardware/AccelerometerMXC6655.cpp b/src/hardware/AccelerometerMXC6655.cpp new file mode 100644 index 0000000..7051980 --- /dev/null +++ b/src/hardware/AccelerometerMXC6655.cpp @@ -0,0 +1,42 @@ +/** + * @file AccelerometerMXC6655.cpp + * @brief Implementation of the AccelerometerMXC6655 class. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #include "AccelerometerMXC6655.h" + + AccelerometerMXC6655::AccelerometerMXC6655() { + // Default constructor, nothing to initialize here + } + + int AccelerometerMXC6655::begin() { + return accel.begin(); + } + + float AccelerometerMXC6655::getAccel(uint8_t axis, uint8_t range) { + return accel.getAccel(axis, range); + } + + int AccelerometerMXC6655::updateAccelAll() { + return accel.updateAccelAll(); + } + + float AccelerometerMXC6655::getTemp() { + return accel.getTemp(); + } + + float* AccelerometerMXC6655::getData() { + return accel.data; + } + + float* AccelerometerMXC6655::getOffset() { + return accel.offset; + } + + void AccelerometerMXC6655::setOffset(float offsetX, float offsetY, float offsetZ) { + accel.offset[0] = offsetX; + accel.offset[1] = offsetY; + accel.offset[2] = offsetZ; + } \ No newline at end of file diff --git a/src/hardware/AccelerometerMXC6655.h b/src/hardware/AccelerometerMXC6655.h new file mode 100644 index 0000000..d50aaf5 --- /dev/null +++ b/src/hardware/AccelerometerMXC6655.h @@ -0,0 +1,40 @@ +/** + * @file AccelerometerMXC6655.h + * @brief Concrete implementation of IAccelerometer using MXC6655 + * + * Adapts the MXC6655 accelerometer to the IAccelerometer interface + * for dependency injection and testing. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #ifndef ACCELEROMETER_MXC6655_H + #define ACCELEROMETER_MXC6655_H + + #include "IAccelerometer.h" + #include "MXC6655.h" + + /** + * @brief Concrete implementation of IAccelerometer using MXC6655 + */ + class AccelerometerMXC6655 : public IAccelerometer { + public: + /** + * @brief Constructor + */ + AccelerometerMXC6655(); + ~AccelerometerMXC6655() override = default; + + int begin() override; + float getAccel(uint8_t axis, uint8_t range = 0) override; + int updateAccelAll() override; + float getTemp() override; + float* getData() override; + float* getOffset() override; + void setOffset(float offsetX, float offsetY, float offsetZ) override; + + private: + MXC6655 accel; // The concrete MXC6655 instance + }; + + #endif // ACCELEROMETER_MXC6655_H \ No newline at end of file diff --git a/test/mocks/MockMXC6655.h b/test/mocks/MockMXC6655.h index b23bbab..1e0db5b 100644 --- a/test/mocks/MockMXC6655.h +++ b/test/mocks/MockMXC6655.h @@ -1,8 +1,31 @@ -#include -#include "MXC6655.h" +/** + * @file MockAccelerometer.h + * @brief Mock implementation of IAccelerometer for testing + * + * Provides a mock accelerometer implementation for unit testing + * without requiring hardware. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ -class MockMXC6655 : public MXC6655 { - public: - MOCK_METHOD(int, begin, ()); - MOCK_METHOD(float, getAccel, (uint8_t axis, uint8_t range)); -}; \ No newline at end of file + #ifndef MOCK_ACCELEROMETER_H + #define MOCK_ACCELEROMETER_H + + #include "IAccelerometer.h" + #include + + /** + * @brief Mock implementation of IAccelerometer for testing + */ + class MockAccelerometer : public IAccelerometer { + public: + MOCK_METHOD(int, begin, (), (override)); + MOCK_METHOD(float, getAccel, (uint8_t axis, uint8_t range), (override)); + MOCK_METHOD(int, updateAccelAll, (), (override)); + MOCK_METHOD(float, getTemp, (), (override)); + MOCK_METHOD(float*, getData, (), (override)); + MOCK_METHOD(float*, getOffset, (), (override)); + MOCK_METHOD(void, setOffset, (float offsetX, float offsetY, float offsetZ), (override)); + }; + + #endif // MOCK_ACCELEROMETER_H \ No newline at end of file From 0693d598a08a4a6dd17396ccc82766b6f61d26b5 Mon Sep 17 00:00:00 2001 From: zradlicz Date: Thu, 1 May 2025 11:55:32 -0500 Subject: [PATCH 32/48] refactored kestrel BMA456 dep --- lib/Driver_-_Kestrel | 2 +- src/FlightControl_Demo.cpp | 3 ++ src/hardware/AccelerometerBMA456.cpp | 52 +++++++++++++++++++++++++++ src/hardware/AccelerometerBMA456.h | 40 +++++++++++++++++++++ src/hardware/AccelerometerMXC6655.cpp | 2 +- 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/hardware/AccelerometerBMA456.cpp create mode 100644 src/hardware/AccelerometerBMA456.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 6a31fad..d1789e3 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 6a31fad7a18b4b8ff0caaf07c5d0e33646975360 +Subproject commit d1789e36342780b9227bafe194d4fed9c96cc84d diff --git a/src/FlightControl_Demo.cpp b/src/FlightControl_Demo.cpp index 34b4021..c9f7535 100644 --- a/src/FlightControl_Demo.cpp +++ b/src/FlightControl_Demo.cpp @@ -75,6 +75,7 @@ int configurePowerSave(int desiredPowerSaveMode); #include "hardware/GpsSFE_UBLOX_GNSS.h" #include "hardware/HumidityTemperatureAdafruit_SHT4X.h" #include "hardware/AccelerometerMXC6655.h" +#include "hardware/AccelerometerBMA456.h" const String firmwareVersion = "2.9.11"; const String schemaVersion = "2.2.9"; @@ -102,6 +103,7 @@ AmbientLightVEML3328 realAls; GpsSFE_UBLOX_GNSS realGps; HumidityTemperatureAdafruit_SHT4X realTempHumidity; AccelerometerMXC6655 realAccel; +AccelerometerBMA456 realBackupAccel; Kestrel logger(realTimeProvider, realGpio, @@ -120,6 +122,7 @@ Kestrel logger(realTimeProvider, realGps, realTempHumidity, realAccel, + realBackupAccel, true); KestrelFileHandler fileSys(logger); Gonk battery(5); //Instantiate with defaults, manually set to port 5 diff --git a/src/hardware/AccelerometerBMA456.cpp b/src/hardware/AccelerometerBMA456.cpp new file mode 100644 index 0000000..9595575 --- /dev/null +++ b/src/hardware/AccelerometerBMA456.cpp @@ -0,0 +1,52 @@ +/** + * @file AccelerometerBMA456.cpp + * @brief Implementation of the AccelerometerBMA456 class. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #include "AccelerometerBMA456.h" + + AccelerometerBMA456::AccelerometerBMA456() : accel() { + // Default constructor, nothing to initialize here + } + + int AccelerometerBMA456::begin() { + return accel.begin(); + } + + float AccelerometerBMA456::getAccel(uint8_t axis, uint8_t range) { + float x, y, z; + accel.getAcceleration(&x, &y, &z); + switch(axis) + { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + return x; + } + } + + int AccelerometerBMA456::updateAccelAll() { + accel.initialize(); + return 0; + } + + float AccelerometerBMA456::getTemp() { + return accel.getTemperature(); + } + + float* AccelerometerBMA456::getData() { //unimplemented + return 0; + } + + float* AccelerometerBMA456::getOffset() { //unimplemented + return 0; + } + + void AccelerometerBMA456::setOffset(float offsetX, float offsetY, float offsetZ) { //unimplemented + } \ No newline at end of file diff --git a/src/hardware/AccelerometerBMA456.h b/src/hardware/AccelerometerBMA456.h new file mode 100644 index 0000000..9f11c4c --- /dev/null +++ b/src/hardware/AccelerometerBMA456.h @@ -0,0 +1,40 @@ +/** + * @file AccelerometerBMA456.h + * @brief Concrete implementation of IAccelerometer using BMA456 + * + * Adapts the BMA456 accelerometer to the IAccelerometer interface + * for dependency injection and testing. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + + #ifndef ACCELEROMETER_BMA456_H + #define ACCELEROMETER_BMA456_H + + #include "IAccelerometer.h" + #include "arduino_bma456.h" + + /** + * @brief Concrete implementation of IAccelerometer using BMA456 + */ + class AccelerometerBMA456 : public IAccelerometer { + public: + /** + * @brief Constructor + */ + AccelerometerBMA456(); + ~AccelerometerBMA456() override = default; + + int begin() override; + float getAccel(uint8_t axis, uint8_t range = 0) override; + int updateAccelAll() override; + float getTemp() override; + float* getData() override; + float* getOffset() override; + void setOffset(float offsetX, float offsetY, float offsetZ) override; + + private: + BMA456 accel; // The concrete BMA456 instance + }; + + #endif // ACCELEROMETER_BMA456_H \ No newline at end of file diff --git a/src/hardware/AccelerometerMXC6655.cpp b/src/hardware/AccelerometerMXC6655.cpp index 7051980..83a5107 100644 --- a/src/hardware/AccelerometerMXC6655.cpp +++ b/src/hardware/AccelerometerMXC6655.cpp @@ -7,7 +7,7 @@ #include "AccelerometerMXC6655.h" - AccelerometerMXC6655::AccelerometerMXC6655() { + AccelerometerMXC6655::AccelerometerMXC6655() : accel() { // Default constructor, nothing to initialize here } From f4616aee546b6cd689183ceab0598c473b5272ad Mon Sep 17 00:00:00 2001 From: zradlicz Date: Thu, 1 May 2025 13:06:38 -0500 Subject: [PATCH 33/48] modifications for testing --- test/CMakeLists.txt | 4 ++-- test/mocks/MockMXC6655.h | 8 ++++---- test/mocks/MockPCA9634.h | 8 ++++---- test/mocks/MockPCAL9535A.h | 7 ++++--- test/mocks/MockSensor.h | 2 +- test/mocks/Particle.h | 12 ++++++++++++ test/unit/Driver_-_Kestrel/KestrelTest.cpp | 2 +- 7 files changed, 28 insertions(+), 15 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e052d37..061bba2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,8 +45,8 @@ add_executable(unit_tests main.cpp # Kestrel tests - #unit/Driver_-_Kestrel/KestrelTest.cpp - #${CMAKE_SOURCE_DIR}/lib/Driver_-_Kestrel/src/Kestrel.cpp + unit/Driver_-_Kestrel/KestrelTest.cpp + ${CMAKE_SOURCE_DIR}/lib/Driver_-_Kestrel/src/Kestrel.cpp # Li710 tests unit/Driver_-_Li710/Li710Test.cpp diff --git a/test/mocks/MockMXC6655.h b/test/mocks/MockMXC6655.h index 1e0db5b..c81c052 100644 --- a/test/mocks/MockMXC6655.h +++ b/test/mocks/MockMXC6655.h @@ -8,8 +8,8 @@ * © 2025 Regents of the University of Minnesota. All rights reserved. */ - #ifndef MOCK_ACCELEROMETER_H - #define MOCK_ACCELEROMETER_H + #ifndef MOCK_MXC6655_H + #define MOCK_MXC6655_H #include "IAccelerometer.h" #include @@ -17,7 +17,7 @@ /** * @brief Mock implementation of IAccelerometer for testing */ - class MockAccelerometer : public IAccelerometer { + class MockMXC6655 : public IAccelerometer { public: MOCK_METHOD(int, begin, (), (override)); MOCK_METHOD(float, getAccel, (uint8_t axis, uint8_t range), (override)); @@ -28,4 +28,4 @@ MOCK_METHOD(void, setOffset, (float offsetX, float offsetY, float offsetZ), (override)); }; - #endif // MOCK_ACCELEROMETER_H \ No newline at end of file + #endif // MOCK_MXC6655_H \ No newline at end of file diff --git a/test/mocks/MockPCA9634.h b/test/mocks/MockPCA9634.h index 1f28988..936eaca 100644 --- a/test/mocks/MockPCA9634.h +++ b/test/mocks/MockPCA9634.h @@ -13,14 +13,14 @@ class MockPCA9634 : public ILed { // Mock all methods defined in the interface MOCK_METHOD(int, begin, (), (override)); MOCK_METHOD(int, sleep, (bool State), (override)); - MOCK_METHOD(int, setOutputMode, (OutputMode State), (override)); - MOCK_METHOD(int, setGroupMode, (GroupMode State), (override)); + MOCK_METHOD(int, setOutputMode, (IOutputMode State), (override)); + MOCK_METHOD(int, setGroupMode, (IGroupMode State), (override)); MOCK_METHOD(int, setGroupBlinkPeriod, (uint16_t Period), (override)); MOCK_METHOD(int, setGroupOnTime, (uint16_t Period), (override)); MOCK_METHOD(int, setBrightness, (uint8_t Pos, float Brightness), (override)); MOCK_METHOD(int, setBrightnessArray, (float Brightness), (override)); - MOCK_METHOD(int, setOutput, (uint8_t Pos, PortState State), (override)); - MOCK_METHOD(int, setOutputArray, (PortState Val), (override)); + MOCK_METHOD(int, setOutput, (uint8_t Pos, IPortState State), (override)); + MOCK_METHOD(int, setOutputArray, (IPortState Val), (override)); }; #endif // MOCK_PCA9634_H \ No newline at end of file diff --git a/test/mocks/MockPCAL9535A.h b/test/mocks/MockPCAL9535A.h index c6ce337..0d00bed 100644 --- a/test/mocks/MockPCAL9535A.h +++ b/test/mocks/MockPCAL9535A.h @@ -2,11 +2,12 @@ #define MOCK_PCAL9535A_H #include -#include "PCAL9535A.h" +#include "IIOExpander.h" +#include "IWire.h" -class MockPCAL9535A : public PCAL9535A { +class MockPCAL9535A : public IIOExpander { public: - MOCK_METHOD(bool, begin, (TwoWire* wire, uint8_t addr)); + MOCK_METHOD(int, begin, ()); MOCK_METHOD(bool, pinMode, (uint16_t pin, uint8_t mode)); MOCK_METHOD(bool, digitalWrite, (uint16_t pin, uint8_t value)); MOCK_METHOD(uint8_t, digitalRead, (uint16_t pin)); diff --git a/test/mocks/MockSensor.h b/test/mocks/MockSensor.h index 66a81e2..82f8359 100644 --- a/test/mocks/MockSensor.h +++ b/test/mocks/MockSensor.h @@ -15,5 +15,5 @@ class MockSensor : public Sensor { MOCK_METHOD(void, setSensorPort, (uint8_t port)); MOCK_METHOD(bool, isPresent, (), (override)); MOCK_METHOD(int, sleep, (), (override)); - MOCK_METHOD(int, wake, (), (override)); + MOCK_METHOD(int, wake, (ITimeProvider& timeProvider), (override)); }; \ No newline at end of file diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h index 2ed9217..72edfba 100644 --- a/test/mocks/Particle.h +++ b/test/mocks/Particle.h @@ -10,6 +10,18 @@ #include #include // For std::min and std::max +#define D2 2 +#define D8 8 +#define D22 22 +#define A3 3 +#define D7 7 +#define A2 2 +#define D6 6 +#define A1 1 +#define D5 5 +#define D23 23 +#define A6 6 + // Forward declaration class StringSumHelper; diff --git a/test/unit/Driver_-_Kestrel/KestrelTest.cpp b/test/unit/Driver_-_Kestrel/KestrelTest.cpp index 5b17790..fd943fb 100644 --- a/test/unit/Driver_-_Kestrel/KestrelTest.cpp +++ b/test/unit/Driver_-_Kestrel/KestrelTest.cpp @@ -141,7 +141,7 @@ TEST_F(KestrelTest, TestMockVEML3328) { EXPECT_EQ(result, 1); bool state = false; - float value = veml.GetValue(VEML3328::Channel::Red, state); + float value = veml.getValue(VEML3328::Channel::Red, state); EXPECT_FLOAT_EQ(value, 123.45f); } From c0555218a5b911dca28d84910c7c3adea814de0b Mon Sep 17 00:00:00 2001 From: zradlicz Date: Thu, 1 May 2025 13:09:50 -0500 Subject: [PATCH 34/48] updated submods --- lib/FlightControl-hardware-dependencies | 2 +- lib/PCAL9535A_Library | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 6f393b9..4213e80 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 6f393b92c09f42e49768303683de5e6b61368496 +Subproject commit 4213e808119f2a840f227b20209934bbd068f571 diff --git a/lib/PCAL9535A_Library b/lib/PCAL9535A_Library index b922cdf..cb2dc3c 160000 --- a/lib/PCAL9535A_Library +++ b/lib/PCAL9535A_Library @@ -1 +1 @@ -Subproject commit b922cdf198f93788c9ac4454a98640972e74ff1e +Subproject commit cb2dc3c172f1aac6467d7b6a002964ff209c5871 From a7900b8da04c08a2bbbc636fabf59961d4a321c4 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 2 May 2025 15:56:57 -0500 Subject: [PATCH 35/48] made a bunch of misc changesd to get teh unit tests to build while including Kestrel.cpp --- lib/Driver_-_Kestrel | 2 +- lib/FlightControl-hardware-dependencies | 2 +- lib/FlightControl-platform-dependencies | 2 +- lib/PCAL9535A_Library | 2 +- src/hardware/IOExpanderPCAL9535A.cpp | 44 ++++- src/hardware/IOExpanderPCAL9535A.h | 4 +- test/mocks/MockAdafruit_SHT4X.h | 6 +- test/mocks/MockBMA456.h | 36 ++++ test/mocks/MockMCP79412.h | 1 + test/mocks/MockPAC1934.h | 2 +- test/mocks/MockPCAL9535A.h | 56 +++++- test/mocks/Particle.h | 122 ++++++++++++ test/unit/Driver_-_Kestrel/KestrelTest.cpp | 212 +++++---------------- 13 files changed, 306 insertions(+), 185 deletions(-) create mode 100644 test/mocks/MockBMA456.h diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index d1789e3..12c15aa 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit d1789e36342780b9227bafe194d4fed9c96cc84d +Subproject commit 12c15aaa9c77261b99e0f0ec475752bfe29c6510 diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index 4213e80..ae0397b 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit 4213e808119f2a840f227b20209934bbd068f571 +Subproject commit ae0397b1c07e7c15cdecbd83cfe1c535986fab2a diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index 183167f..8915df3 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit 183167f55afbe499f67b8ad22292a9c12602bdc6 +Subproject commit 8915df3abdc4a7d80989707fa56e67ba361b57ce diff --git a/lib/PCAL9535A_Library b/lib/PCAL9535A_Library index cb2dc3c..708272b 160000 --- a/lib/PCAL9535A_Library +++ b/lib/PCAL9535A_Library @@ -1 +1 @@ -Subproject commit cb2dc3c172f1aac6467d7b6a002964ff209c5871 +Subproject commit 708272b194d87050cf711e6d51ccb556d53dbdb2 diff --git a/src/hardware/IOExpanderPCAL9535A.cpp b/src/hardware/IOExpanderPCAL9535A.cpp index 383fa43..7c32a9b 100644 --- a/src/hardware/IOExpanderPCAL9535A.cpp +++ b/src/hardware/IOExpanderPCAL9535A.cpp @@ -52,12 +52,44 @@ // --- Specific Features --- - int IOExpanderPCAL9535A::pinSetDriveStrength(int Pin, DriveStrength State, bool Port) { - return pcal9535a.pinSetDriveStrength(Pin, State, Port); - } - - int IOExpanderPCAL9535A::pinSetDriveStrength(int Pin, DriveStrength State) { - return pcal9535a.pinSetDriveStrength(Pin, State); + int IOExpanderPCAL9535A::pinSetDriveStrength(int Pin, IDriveStrength State, bool Port) { + DriveStrength realState; + switch(State) + { + case DRIVE_STRENGTH_DEFAULT: + //realState = DriveStrength::DEFAULT; //commented out because of macro overlapping + //break; + case DRIVE_STRENGTH_HIGH: + realState = DriveStrength::HIGH; + break; + case DRIVE_STRENGTH_STANDARD: + realState = DriveStrength::STANDARD; + break; + default: + realState = DriveStrength::STANDARD; + break; + } + return pcal9535a.pinSetDriveStrength(Pin, realState, Port); + } + + int IOExpanderPCAL9535A::pinSetDriveStrength(int Pin, IDriveStrength State) { + DriveStrength realState; + switch(State) + { + case DRIVE_STRENGTH_DEFAULT: + //realState = DriveStrength::DEFAULT; //commented out because of macro overlapping + //break; + case DRIVE_STRENGTH_HIGH: + realState = DriveStrength::HIGH; + break; + case DRIVE_STRENGTH_STANDARD: + realState = DriveStrength::STANDARD; + break; + default: + realState = DriveStrength::STANDARD; + break; + } + return pcal9535a.pinSetDriveStrength(Pin, realState); } diff --git a/src/hardware/IOExpanderPCAL9535A.h b/src/hardware/IOExpanderPCAL9535A.h index 4f29e9d..2d62ee5 100644 --- a/src/hardware/IOExpanderPCAL9535A.h +++ b/src/hardware/IOExpanderPCAL9535A.h @@ -25,8 +25,8 @@ class IOExpanderPCAL9535A : public IIOExpander { int digitalRead(int Pin) override; // Specific Features - int pinSetDriveStrength(int Pin, DriveStrength State, bool Port) override; - int pinSetDriveStrength(int Pin, DriveStrength State) override; + int pinSetDriveStrength(int Pin, IDriveStrength State, bool Port) override; + int pinSetDriveStrength(int Pin, IDriveStrength State) override; int setInterrupt(int Pin, bool State, bool Port) override; int setInterrupt(int Pin, bool State) override; diff --git a/test/mocks/MockAdafruit_SHT4X.h b/test/mocks/MockAdafruit_SHT4X.h index a7e9a0b..bc73d57 100644 --- a/test/mocks/MockAdafruit_SHT4X.h +++ b/test/mocks/MockAdafruit_SHT4X.h @@ -25,9 +25,9 @@ // Mock methods MOCK_METHOD(bool, begin, (), (override)); - MOCK_METHOD(void, setPrecision, (ht_precision_t prec), (override)); - MOCK_METHOD(ht_precision_t, getPrecision, (), (override)); - MOCK_METHOD(bool, getEvent, (sensors_event_t *humidity, sensors_event_t *temp), (override)); + MOCK_METHOD(void, setPrecision, (Iht_precision_t prec), (override)); + MOCK_METHOD(Iht_precision_t, getPrecision, (), (override)); + MOCK_METHOD(bool, getEvent, (Isensors_event_t *humidity, Isensors_event_t *temp), (override)); }; #endif // MOCK_ADAFRUIT_SHT4X_H \ No newline at end of file diff --git a/test/mocks/MockBMA456.h b/test/mocks/MockBMA456.h new file mode 100644 index 0000000..16bf8aa --- /dev/null +++ b/test/mocks/MockBMA456.h @@ -0,0 +1,36 @@ +/** + * @file MockBMA456.h + * @brief Mock implementation of IAccelerometer for testing + * + * Provides a mock implementation of the IAccelerometer interface + * that can be used for unit testing without hardware dependencies. + * + * © 2025 Regents of the University of Minnesota. All rights reserved. + */ + +#ifndef MOCK_BMA456_H +#define MOCK_BMA456_H + +#include +#include "IAccelerometer.h" + +/** + * @brief Google Mock implementation of IAccelerometer for testing + */ +class MockBMA456 : public IAccelerometer { +public: + // Default constructor and destructor + MockBMA456() = default; + ~MockBMA456() override = default; + + // Mock methods + MOCK_METHOD(int, begin, (), (override)); + MOCK_METHOD(float, getAccel, (uint8_t axis, uint8_t range), (override)); + MOCK_METHOD(int, updateAccelAll, (), (override)); + MOCK_METHOD(float, getTemp, (), (override)); + MOCK_METHOD(float*, getData, (), (override)); + MOCK_METHOD(float*, getOffset, (), (override)); + MOCK_METHOD(void, setOffset, (float offsetX, float offsetY, float offsetZ), (override)); +}; + +#endif // MOCK_BMA456_H \ No newline at end of file diff --git a/test/mocks/MockMCP79412.h b/test/mocks/MockMCP79412.h index d32d529..cb69736 100644 --- a/test/mocks/MockMCP79412.h +++ b/test/mocks/MockMCP79412.h @@ -32,6 +32,7 @@ MOCK_METHOD(int, enableAlarm, (bool State, bool AlarmNum), (override)); MOCK_METHOD(int, clearAlarm, (bool AlarmNum), (override)); MOCK_METHOD(bool, readAlarm, (bool AlarmNum), (override)); + MOCK_METHOD(String, getUUIDString, (), (override)); MOCK_METHOD(uint8_t, readByte, (int Reg), (override)); MOCK_METHOD(uint8_t, getErrorsArray, (uint32_t errors[]), (override)); MOCK_METHOD(int, throwError, (uint32_t error), (override)); diff --git a/test/mocks/MockPAC1934.h b/test/mocks/MockPAC1934.h index e4ed15b..65a613d 100644 --- a/test/mocks/MockPAC1934.h +++ b/test/mocks/MockPAC1934.h @@ -20,7 +20,7 @@ class MockPAC1934 : public ICurrentSenseAmplifier { MOCK_METHOD(bool, begin, (), (override)); MOCK_METHOD(bool, setAddress, (uint8_t addr), (override)); MOCK_METHOD(bool, enableChannel, (uint8_t channel, bool state), (override)); - MOCK_METHOD(bool, setFrequency, (uint8_t frequency), (override)); + MOCK_METHOD(bool, setFrequency, (uint16_t frequency), (override)); MOCK_METHOD(int, getFrequency, (), (override)); // Measurement direction diff --git a/test/mocks/MockPCAL9535A.h b/test/mocks/MockPCAL9535A.h index 0d00bed..3a31316 100644 --- a/test/mocks/MockPCAL9535A.h +++ b/test/mocks/MockPCAL9535A.h @@ -7,12 +7,56 @@ class MockPCAL9535A : public IIOExpander { public: - MOCK_METHOD(int, begin, ()); - MOCK_METHOD(bool, pinMode, (uint16_t pin, uint8_t mode)); - MOCK_METHOD(bool, digitalWrite, (uint16_t pin, uint8_t value)); - MOCK_METHOD(uint8_t, digitalRead, (uint16_t pin)); - MOCK_METHOD(bool, digitalWritePort, (uint8_t port, uint8_t value)); - MOCK_METHOD(uint8_t, digitalReadPort, (uint8_t port)); + // Core functionality + MOCK_METHOD(int, begin, (), (override)); + + // Pin control methods + MOCK_METHOD(int, pinMode, (int Pin, uint8_t State, bool Port), (override)); + MOCK_METHOD(int, pinMode, (int Pin, uint8_t State), (override)); + MOCK_METHOD(int, digitalWrite, (int Pin, bool State, bool Port), (override)); + MOCK_METHOD(int, digitalWrite, (int Pin, bool State), (override)); + MOCK_METHOD(int, digitalRead, (int Pin, bool Port), (override)); + MOCK_METHOD(int, digitalRead, (int Pin), (override)); + + // Drive strength methods + MOCK_METHOD(int, pinSetDriveStrength, (int Pin, IDriveStrength State, bool Port), (override)); + MOCK_METHOD(int, pinSetDriveStrength, (int Pin, IDriveStrength State), (override)); + + // Interrupt methods + MOCK_METHOD(int, setInterrupt, (int Pin, bool State, bool Port), (override)); + MOCK_METHOD(int, setInterrupt, (int Pin, bool State), (override)); + MOCK_METHOD(int, getInterrupt, (int Pin), (override)); + MOCK_METHOD(uint16_t, getAllInterrupts, (uint8_t Option), (override)); + MOCK_METHOD(uint16_t, getInterruptMask, (), (override)); + MOCK_METHOD(unsigned int, clearInterrupt, (uint8_t age), (override)); + MOCK_METHOD(bool, isInterrupt, (uint8_t age), (override)); + + // Latch methods + MOCK_METHOD(int, setLatch, (int Pin, bool State, bool Port), (override)); + MOCK_METHOD(int, setLatch, (int Pin, bool State), (override)); + MOCK_METHOD(uint16_t, getLatch, (), (override)); + + // Input polarity methods + MOCK_METHOD(int, setInputPolarity, (int Pin, bool State, bool Port), (override)); + MOCK_METHOD(int, setInputPolarity, (int Pin, bool State), (override)); + MOCK_METHOD(bool, getInputPolarity, (int Pin, bool Port), (override)); + MOCK_METHOD(bool, getInputPolarity, (int Pin), (override)); + + // Configuration methods + MOCK_METHOD(int, setIntPinConfig, (int Pin, bool Latch), (override)); + MOCK_METHOD(int, setBusOutput, (uint8_t mode, bool Port), (override)); + MOCK_METHOD(uint8_t, getBusOutput, (), (override)); + + // Bus read method + MOCK_METHOD(uint16_t, readBus, (), (override)); + + // Error handling methods + MOCK_METHOD(uint16_t, getError, (), (override)); + MOCK_METHOD(uint16_t, clearError, (), (override)); + MOCK_METHOD(void, safeMode, (int state), (override)); + + // Additional methods + MOCK_METHOD(uint16_t, readWord, (int Pos, int &Error), (override)); }; #endif // MOCK_PCAL9535A_H \ No newline at end of file diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h index 72edfba..7fb306a 100644 --- a/test/mocks/Particle.h +++ b/test/mocks/Particle.h @@ -9,6 +9,10 @@ #include #include #include // For std::min and std::max +#include // For isnan +#include // For time literals + +using namespace std::chrono_literals; // For 20min, 30s literals #define D2 2 #define D8 8 @@ -22,6 +26,20 @@ #define D23 23 #define A6 6 +// Hex formatting +#define HEX (unsigned char)16 +// Make min and isnan available in global namespace for compatibility +using std::min; +using std::isnan; + +// Time change event values +#define time_changed_sync 1 +#define time_changed_manually 2 + +#define PLATFORM_BSOM 0 +#define PLATFORM_B5SOM 1 +#define PLATFORM_ID 0 + // Forward declaration class StringSumHelper; @@ -62,12 +80,53 @@ class String { *this = String(buf); } + // Make these unambiguous by ensuring the second parameter type is always distinct explicit String(float value, int decimalPlaces = 2) { char buf[33]; snprintf(buf, sizeof(buf), "%.*f", decimalPlaces, value); *this = String(buf); } + // Add constructor for double + explicit String(double value, int decimalPlaces = 2) { + char buf[33]; + snprintf(buf, sizeof(buf), "%.*f", decimalPlaces, value); + *this = String(buf); + } + + // Add constructor for unsigned long + explicit String(unsigned long value, unsigned char base = 10) { + char buf[33]; + if (base == 16) { + snprintf(buf, sizeof(buf), "%lx", value); + } else { + snprintf(buf, sizeof(buf), "%lu", value); + } + *this = String(buf); + } + + // Add constructor for long + explicit String(long value, unsigned char base = 10) { + char buf[33]; + if (base == 16) { + snprintf(buf, sizeof(buf), "%lx", value); + } else { + snprintf(buf, sizeof(buf), "%ld", value); + } + *this = String(buf); + } + + // Add constructor for uint32_t explicitly + explicit String(uint32_t value, unsigned char base = 10) { + char buf[33]; + if (base == 16) { + snprintf(buf, sizeof(buf), "%x", value); + } else { + snprintf(buf, sizeof(buf), "%u", value); + } + *this = String(buf); + } + ~String() { if (_buffer) free(_buffer); } @@ -252,6 +311,22 @@ class String { if (found == nullptr) return -1; return found - _buffer; } + + // Add endsWith method + bool endsWith(const String &suffix) const { + if (!_buffer || !suffix._buffer) return false; + if (suffix._length > _length) return false; + + return (strcmp(_buffer + (_length - suffix._length), suffix._buffer) == 0); + } + + bool endsWith(const char *suffix) const { + if (!_buffer || !suffix) return false; + size_t suffix_len = strlen(suffix); + if (suffix_len > _length) return false; + + return (strcmp(_buffer + (_length - suffix_len), suffix) == 0); + } void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { if (buf == nullptr || bufsize == 0) { @@ -398,4 +473,51 @@ inline StringSumHelper operator + (const String &lhs, int num) { return result; } +// Mock EEPROM class +class EEPROMClass { +public: + template + void get(int address, T& value) { + // Simple mock implementation - just provide default values + value = T(); + } + + template + void put(int address, const T& value) { + // Mock implementation - doesn't actually store anything + } +}; + +// Global EEPROM instance - use inline to avoid multiple definition errors +inline EEPROMClass EEPROM; + +// Mock RGB LED control +class RGBClass { +public: + void control(bool controlEnabled) { + // Mock implementation - doesn't do anything + } + + void color(int r, int g, int b) { + // Mock implementation - doesn't do anything + } +}; + +// Global RGB instance - use inline to avoid multiple definition errors +inline RGBClass RGB; + +// Mock HAL functions +inline uint32_t HAL_RNG_GetRandomNumber() { + return rand(); // Simple mock implementation +} + +// Mock functions for floating point control register +inline int __get_FPSCR() { + return 0; // Mock implementation +} + +inline void __set_FPSCR(int value) { + // Mock implementation - doesn't do anything +} + #endif // MOCK_PARTICLE_H \ No newline at end of file diff --git a/test/unit/Driver_-_Kestrel/KestrelTest.cpp b/test/unit/Driver_-_Kestrel/KestrelTest.cpp index fd943fb..f7e21b2 100644 --- a/test/unit/Driver_-_Kestrel/KestrelTest.cpp +++ b/test/unit/Driver_-_Kestrel/KestrelTest.cpp @@ -1,16 +1,6 @@ #include #include -// Include our mock headers first -#include "Particle.h" -#include "MockSensor.h" -#include "MockPCAL9535A.h" -#include "MockPCA9634.h" -#include "MockMCP79412.h" -#include "MockSFE_UBLOX_GNSS.h" -#include "MockVEML3328.h" -#include "MockMXC6655.h" - // Include platform interface mocks #include "MockTimeProvider.h" #include "MockGpio.h" @@ -19,8 +9,20 @@ #include "MockCloud.h" #include "MockSerial.h" -// This must be included AFTER mocks to ensure the mocks are used -// instead of actual hardware implementations +// Include hardware component mocks +#include "MockPCAL9535A.h" +#include "MockPAC1934.h" +#include "MockPCA9634.h" +#include "MockMCP79412.h" +#include "MockVEML3328.h" +#include "MockSFE_UBLOX_GNSS.h" +#include "MockMXC6655.h" +#include "MockAdafruit_SHT4X.h" +#include "MockBMA456.h" + +//include mock Particle + +// Include the Kestrel header #include "Kestrel.h" // Test fixture class for Kestrel tests @@ -35,173 +37,57 @@ class KestrelTest : public ::testing::Test { MockSerial mockSerialDebug; MockSerial mockSerialSdi12; - // Additional hardware component mocks that would need injection + // Hardware component mocks MockPCAL9535A mockIoOB; MockPCAL9535A mockIoTalon; + MockPAC1934 mockCsaAlpha; + MockPAC1934 mockCsaBeta; MockPCA9634 mockLed; MockMCP79412 mockRtc; - MockSFE_UBLOX_GNSS mockGps; MockVEML3328 mockAls; + MockSFE_UBLOX_GNSS mockGps; + MockAdafruit_SHT4X mockHumidityTemp; MockMXC6655 mockAccel; + MockBMA456 mockBackupAccel; + - // Set up mocks and test objects void SetUp() override { - // Set up default mock behavior here + // Set up default mock behavior EXPECT_CALL(mockWire, isEnabled()) .WillRepeatedly(::testing::Return(false)); - - // Add more default behaviors as needed - } - - void TearDown() override { - // Any test cleanup code goes here - } - - // Helper method to create a Kestrel instance with mocks - // Note: This only passes the platform interface mocks, not hardware component mocks - Kestrel createKestrel() { - return Kestrel( - mockTimeProvider, - mockGpio, - mockSystem, - mockWire, - mockCloud, - mockSerialDebug, - mockSerialSdi12, - false // useSensors - ); } }; -// Basic test to verify our testing framework works -TEST_F(KestrelTest, TestSensorMockWorks) { - MockSensor mockSensor; - time_t dummyTime = 0; - bool criticalFault = false; - bool fault = false; - - EXPECT_CALL(mockSensor, begin(dummyTime, ::testing::_, ::testing::_)) - .Times(1) - .WillOnce(::testing::Return("Sensor Initialized")); - - String result = mockSensor.begin(dummyTime, criticalFault, fault); - EXPECT_EQ(result, "Sensor Initialized"); -} - -// Test that the MCP79412 RTC mock works -TEST_F(KestrelTest, TestMockRTC) { - MockMCP79412 rtc; - - // Set up expectations - EXPECT_CALL(rtc, begin(true)) - .Times(1) - .WillOnce(::testing::Return(1)); // Return success - - EXPECT_CALL(rtc, getTimeUnix()) - .Times(1) - .WillOnce(::testing::Return(1585699200)); // Return a fixed time (April 1, 2020) - - EXPECT_CALL(rtc, getUUIDString()) - .Times(1) - .WillOnce(::testing::Return("test-uuid-1234")); - - // Test that the mock behaves as expected - int result = rtc.begin(true); - EXPECT_EQ(result, 1); - - time_t time = rtc.getTimeUnix(); - EXPECT_EQ(time, 1585699200); - - String uuid = rtc.getUUIDString(); - EXPECT_EQ(uuid, "test-uuid-1234"); -} - -// Test that the VEML3328 mock works -TEST_F(KestrelTest, TestMockVEML3328) { - MockVEML3328 veml; - - // Set up expectations - EXPECT_CALL(veml, begin()) - .Times(1) - .WillOnce(::testing::Return(0)); // Return success - - EXPECT_CALL(veml, autoRange()) - .Times(1) - .WillOnce(::testing::Return(1)); // Return success - - EXPECT_CALL(veml, GetValue(::testing::_, ::testing::_)) - .Times(1) - .WillOnce(::testing::Return(123.45f)); // Return a fixed value - - // Test that the mock behaves as expected - int result = veml.begin(); - EXPECT_EQ(result, 0); - - result = veml.autoRange(); - EXPECT_EQ(result, 1); - - bool state = false; - float value = veml.getValue(VEML3328::Channel::Red, state); - EXPECT_FLOAT_EQ(value, 123.45f); +// Test Kestrel constructor +TEST_F(KestrelTest, TestKestrelConstructor) { + // Create a Kestrel instance with our mocks + Kestrel kestrel(mockTimeProvider, + mockGpio, + mockSystem, + mockWire, + mockCloud, + mockSerialDebug, + mockSerialSdi12, + mockIoOB, + mockIoTalon, + mockCsaAlpha, + mockCsaBeta, + mockLed, + mockRtc, + mockAls, + mockGps, + mockHumidityTemp, + mockAccel, + mockBackupAccel, + false); + + // Verify basic properties + EXPECT_EQ(kestrel.sensorInterface, BusType::CORE); + EXPECT_EQ(kestrel.wake(), 0); } -// Test platform interfaces in Kestrel -TEST_F(KestrelTest, TestKestrelPlatformInteractions) { - // Set up expectations for the Kestrel constructor and begin method - EXPECT_CALL(mockWire, begin()).Times(1); - EXPECT_CALL(mockWire, setClock(400000)).Times(1); - EXPECT_CALL(mockSystem, on(IEventType::TIME_CHANGED, ::testing::_)).Times(1); - EXPECT_CALL(mockSystem, on(IEventType::OUT_OF_MEMORY, ::testing::_)).Times(1); - EXPECT_CALL(mockSystem, resetReason()).WillOnce(::testing::Return(IResetReason::POWER_DOWN)); - - // Additional expectations for hardware components would go here - // but we can't easily mock them without refactoring Kestrel - - // Create a Kestrel instance with our mocked platform interfaces - Kestrel kestrel = createKestrel(); - - // This would initialize Kestrel, but we can't fully test it without hardware component mocks - // bool criticalFault = false; - // bool fault = false; - // String result = kestrel.begin(0, criticalFault, fault); - - // Instead, we can test isolated methods that primarily use platform interfaces - // For example, methods that use timeProvider, gpio, etc. -} - -// Test platform ITimeProvider interactions -TEST_F(KestrelTest, TestKestrelTimeOperations) { - // Set up expectations - EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(1585699200)); // April 1, 2020 - - // Create Kestrel instance (note: this doesn't test begin()) - Kestrel kestrel = createKestrel(); - - // Test getTime() which should use mockTimeProvider - time_t time = kestrel.getTime(); - EXPECT_EQ(time, 1585699200); -} - -// Test GPIO interactions -TEST_F(KestrelTest, TestKestrelGpioOperations) { - // Set up expectations - EXPECT_CALL(mockGpio, pinMode(Pins::WD_HOLD, IPinMode::OUTPUT)).Times(1); - EXPECT_CALL(mockGpio, digitalWrite(Pins::WD_HOLD, 1)).Times(1); - - // Create Kestrel instance - Kestrel kestrel = createKestrel(); - - // Test feedWDT which should use mockGpio - bool result = kestrel.feedWDT(); - EXPECT_TRUE(result); -} - -// More tests would be added to cover other Kestrel methods -// that interact with the platform interfaces we've mocked - /* -// Remaining test outline (commented out since we can't fully implement these yet) +// Remaining test outline for future implementation // Kestrel begin tests // Verify Kestrel begin throws error with reset reason if init is not done From 04d5fa6c8b9a8903d18e14d20f0dae14eb2f9334 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Wed, 7 May 2025 09:22:50 -0500 Subject: [PATCH 36/48] added many tests, wip on getting them to be valuable --- lib/Driver_-_Kestrel | 2 +- test/unit/Driver_-_Kestrel/KestrelTest.cpp | 1191 +++++++++++++++++++- 2 files changed, 1133 insertions(+), 60 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index 12c15aa..d0cbf77 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit 12c15aaa9c77261b99e0f0ec475752bfe29c6510 +Subproject commit d0cbf77b2bcde6329b991a1098c4be722e3ee75c diff --git a/test/unit/Driver_-_Kestrel/KestrelTest.cpp b/test/unit/Driver_-_Kestrel/KestrelTest.cpp index f7e21b2..718e5e9 100644 --- a/test/unit/Driver_-_Kestrel/KestrelTest.cpp +++ b/test/unit/Driver_-_Kestrel/KestrelTest.cpp @@ -1,28 +1,28 @@ #include #include -// Include platform interface mocks -#include "MockTimeProvider.h" -#include "MockGpio.h" -#include "MockSystem.h" -#include "MockWire.h" -#include "MockCloud.h" -#include "MockSerial.h" - -// Include hardware component mocks +// Include our mock headers first +#include "Particle.h" +#include "MockSensor.h" #include "MockPCAL9535A.h" -#include "MockPAC1934.h" #include "MockPCA9634.h" #include "MockMCP79412.h" -#include "MockVEML3328.h" #include "MockSFE_UBLOX_GNSS.h" +#include "MockVEML3328.h" #include "MockMXC6655.h" #include "MockAdafruit_SHT4X.h" -#include "MockBMA456.h" +#include "MockPAC1934.h" -//include mock Particle +// Include platform interface mocks +#include "MockTimeProvider.h" +#include "MockGpio.h" +#include "MockSystem.h" +#include "MockWire.h" +#include "MockCloud.h" +#include "MockSerial.h" -// Include the Kestrel header +// This must be included AFTER mocks to ensure the mocks are used +// instead of actual hardware implementations #include "Kestrel.h" // Test fixture class for Kestrel tests @@ -37,60 +37,1134 @@ class KestrelTest : public ::testing::Test { MockSerial mockSerialDebug; MockSerial mockSerialSdi12; - // Hardware component mocks + // Additional hardware component mocks that would need injection MockPCAL9535A mockIoOB; MockPCAL9535A mockIoTalon; + MockAdafruit_SHT4X mockTempHumidity; + MockMXC6655 mockAccel; + MockMXC6655 mockBackupAccel; + MockPCA9634 mockLed; MockPAC1934 mockCsaAlpha; MockPAC1934 mockCsaBeta; - MockPCA9634 mockLed; MockMCP79412 mockRtc; - MockVEML3328 mockAls; MockSFE_UBLOX_GNSS mockGps; - MockAdafruit_SHT4X mockHumidityTemp; - MockMXC6655 mockAccel; - MockBMA456 mockBackupAccel; - + MockVEML3328 mockAls; + // Set up mocks and test objects void SetUp() override { - // Set up default mock behavior - EXPECT_CALL(mockWire, isEnabled()) - .WillRepeatedly(::testing::Return(false)); + + } + + void TearDown() override { + // Any test cleanup code goes here + } + + // Helper method to create a Kestrel instance with mocks + Kestrel createFullyMockedKestrel(bool useSensors = false) { + return Kestrel( + mockTimeProvider, + mockGpio, + mockSystem, + mockWire, + mockCloud, + mockSerialDebug, + mockSerialSdi12, + mockIoOB, + mockIoTalon, + mockCsaAlpha, + mockCsaBeta, + mockLed, + mockRtc, + mockAls, + mockGps, + mockTempHumidity, + mockAccel, + mockBackupAccel, + useSensors + ); } }; -// Test Kestrel constructor -TEST_F(KestrelTest, TestKestrelConstructor) { - // Create a Kestrel instance with our mocks - Kestrel kestrel(mockTimeProvider, - mockGpio, - mockSystem, - mockWire, - mockCloud, - mockSerialDebug, - mockSerialSdi12, - mockIoOB, - mockIoTalon, - mockCsaAlpha, - mockCsaBeta, - mockLed, - mockRtc, - mockAls, - mockGps, - mockHumidityTemp, - mockAccel, - mockBackupAccel, - false); - - // Verify basic properties - EXPECT_EQ(kestrel.sensorInterface, BusType::CORE); - EXPECT_EQ(kestrel.wake(), 0); -} - -/* -// Remaining test outline for future implementation - -// Kestrel begin tests -// Verify Kestrel begin throws error with reset reason if init is not done +// Constructor Tests +TEST_F(KestrelTest, ConstructorDefaultUseSensors) { + EXPECT_NO_THROW({ + Kestrel kestrel = createFullyMockedKestrel(false); + // Constructor should set reportSensors to false by default + }); +} + +TEST_F(KestrelTest, ConstructorWithUseSensorsTrue) { + EXPECT_NO_THROW({ + Kestrel kestrel = createFullyMockedKestrel(true); + // Constructor should set reportSensors to true + // We can test this indirectly through getData() + }); +} + +// Begin() Tests +TEST_F(KestrelTest, BeginFirstTimeInitializationThrowsSystemReset) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + float data[3] = {0.0, 0.0, 0.0}; + + EXPECT_CALL(mockSystem, resetReason()) + .WillOnce(::testing::Return(IResetReason::WATCHDOG)); + + EXPECT_CALL(mockAccel, getData()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockAccel, getOffset()) + .WillRepeatedly(::testing::Return(data)); + + // We would need to verify throwError is called, but it's private + // This tests the overall begin() behavior + String result = kestrel.begin(0, criticalFault, fault); + + // Check that we get through initialization + EXPECT_EQ(result, ""); +} + +TEST_F(KestrelTest, BeginI2CInitializationSuccess) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + + // Test I2C initialization + EXPECT_CALL(mockWire, isEnabled()) + .WillOnce(::testing::Return(false)); + EXPECT_CALL(mockWire, begin()) + .Times(1); + EXPECT_CALL(mockWire, setClock(400000)) + .Times(1); + + kestrel.begin(0, criticalFault, fault); + + EXPECT_FALSE(criticalFault); +} + +TEST_F(KestrelTest, BeginIOExpanderInitializationFailureCritical) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + + // Make IO expander initialization fail + EXPECT_CALL(mockIoOB, begin()) + .WillOnce(::testing::Return(1)); // Non-zero = failure + + kestrel.begin(0, criticalFault, fault); + + EXPECT_TRUE(criticalFault); +} + +// enablePower() Tests +TEST_F(KestrelTest, EnablePowerPort5ReturnsImmediately) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Port 5 is a special case - should return false + bool result = kestrel.enablePower(5, true); + EXPECT_FALSE(result); +} + +TEST_F(KestrelTest, EnablePowerInvalidPortThrowsError) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test port 0 and port > 4 + kestrel.enablePower(0, true); + kestrel.enablePower(10, true); + + // Would need to check if throwError was called + // This is limited since throwError is private +} + +TEST_F(KestrelTest, EnablePowerValidPortOperation) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up expectations for enabling power on port 1 + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockIoTalon, pinMode(0, ::testing::_)) // EN[0] for port 1 + .Times(1); + EXPECT_CALL(mockIoTalon, digitalWrite(0, true)) + .Times(1); + + bool result = kestrel.enablePower(1, true); + EXPECT_FALSE(result); // Always returns false for debugging +} + +// enableData() Tests +TEST_F(KestrelTest, EnableDataPort5CallsExternalI2C) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Port 5 calls enableI2C_External + // We'd need to mock the enableI2C_External behavior + bool result = kestrel.enableData(5, true); + EXPECT_FALSE(result); +} + +TEST_F(KestrelTest, EnableDataValidPortOperation) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up expectations for enabling data on port 1 + EXPECT_CALL(mockIoTalon, pinMode(1, ::testing::_)) // I2C_EN[0] for port 1 + .Times(1); + EXPECT_CALL(mockIoTalon, digitalWrite(1, true)) + .Times(1); + + bool result = kestrel.enableData(1, true); + EXPECT_FALSE(result); // Always returns false for debugging +} + +// feedWDT() Tests +TEST_F(KestrelTest, FeedWDTNormalOperation) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up expectations for normal WDT feeding + EXPECT_CALL(mockGpio, pinMode(Pins::WD_HOLD, IPinMode::OUTPUT)) + .Times(1); + EXPECT_CALL(mockGpio, digitalWrite(Pins::WD_HOLD, 0)) + .Times(1); + EXPECT_CALL(mockGpio, digitalWrite(Pins::WD_HOLD, 1)) + .Times(1); + EXPECT_CALL(mockTimeProvider, delay(1)) + .Times(2); + + bool result = kestrel.feedWDT(); + EXPECT_TRUE(result); +} + +// getTime() Tests +TEST_F(KestrelTest, GetTimeValidTimeProvider) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up a valid time condition + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(true)); + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(1585699200)); // April 1, 2020 + + time_t time = kestrel.getTime(); + EXPECT_EQ(time, 1585699200); +} + +TEST_F(KestrelTest, GetTimeInvalidTimeTriggersSync) { + Kestrel kestrel = createFullyMockedKestrel(); + + // First call: time is invalid + // Second call (after sync): time is valid + EXPECT_CALL(mockTimeProvider, isValid()) + .WillOnce(::testing::Return(false)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(1585699200)); + + // This should trigger a sync and return valid time + time_t time = kestrel.getTime(); + EXPECT_EQ(time, 1585699200); +} + +// getTimeString() Tests +TEST_F(KestrelTest, GetTimeStringValidTime) { + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(true)); + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(1585699200)); + + String result = kestrel.getTimeString(); + EXPECT_EQ(result, "1585699200"); +} + +TEST_F(KestrelTest, GetTimeStringInvalidTime) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Force getTime() to return 0 + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(false)); + + String result = kestrel.getTimeString(); + EXPECT_EQ(result, "null"); +} + +// setIndicatorState() Tests +TEST_F(KestrelTest, SetIndicatorStateSensorsPass) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test SENSORS bank with PASS mode + EXPECT_CALL(mockLed, setOutput(0, ILed::IPortState::PWM)) // Green on + .Times(1); + EXPECT_CALL(mockLed, setOutput(1, ILed::IPortState::Off)) // Amber off + .Times(1); + EXPECT_CALL(mockLed, setOutput(2, ILed::IPortState::Off)) // Red off + .Times(1); + + bool result = kestrel.setIndicatorState(IndicatorLight::SENSORS, IndicatorMode::PASS); + EXPECT_EQ(result, 0); +} + +TEST_F(KestrelTest, SetIndicatorStateAllInit) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test ALL bank with INIT mode - should set all to group blink + EXPECT_CALL(mockLed, setOutputArray(ILed::IPortState::Group)) + .Times(1); + EXPECT_CALL(mockLed, setGroupBlinkPeriod(250)) + .Times(1); + EXPECT_CALL(mockLed, setGroupOnTime(25)) + .Times(1); + + bool result = kestrel.setIndicatorState(IndicatorLight::ALL, IndicatorMode::INIT); + EXPECT_EQ(result, 0); +} + +// updateLocation() Tests +TEST_F(KestrelTest, UpdateLocationForceUpdate) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up GPS to have a valid fix + EXPECT_CALL(mockGps, getPVT()) + .WillOnce(::testing::Return(true)); + EXPECT_CALL(mockGps, getFixType()) + .WillOnce(::testing::Return(3)); // 3D fix + EXPECT_CALL(mockGps, getGnssFixOk()) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(mockGps, getLongitude()) + .WillOnce(::testing::Return(1000000)); // Example value + EXPECT_CALL(mockGps, getLatitude()) + .WillOnce(::testing::Return(2000000)); // Example value + EXPECT_CALL(mockGps, getAltitude()) + .WillOnce(::testing::Return(3000)); // Example value + + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(1585699200)); + + bool result = kestrel.updateLocation(true); + EXPECT_TRUE(result); +} + +TEST_F(KestrelTest, UpdateLocationNoFix) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up GPS to have no fix + EXPECT_CALL(mockGps, getPVT()) + .WillOnce(::testing::Return(false)); + + bool result = kestrel.updateLocation(true); + EXPECT_FALSE(result); +} + +// startTimer() Tests +TEST_F(KestrelTest, StartTimerDefaultPeriod) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test with period = 0 should use defaultPeriod + EXPECT_CALL(mockRtc, setAlarm(300, true)) // defaultPeriod is 300 + .Times(1); + + bool result = kestrel.startTimer(0); + EXPECT_FALSE(result); +} + +TEST_F(KestrelTest, StartTimerCustomPeriod) { + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockRtc, setAlarm(600, true)) + .Times(1); + + bool result = kestrel.startTimer(600); + EXPECT_FALSE(result); +} + +// waitUntilTimerDone() Tests +TEST_F(KestrelTest, WaitUntilTimerDoneUnitialized) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Without calling startTimer(), logPeriod should be 0 + bool result = kestrel.waitUntilTimerDone(); + EXPECT_FALSE(result); +} + +TEST_F(KestrelTest, WaitUntilTimerDoneSuccess) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Start a timer first + kestrel.startTimer(300); + + // Set up GPIO to show clock interrupt triggered + EXPECT_CALL(mockGpio, digitalRead(Pins::Clock_INT)) + .WillOnce(::testing::Return(0)); // LOW = interrupt triggered + + bool result = kestrel.waitUntilTimerDone(); + EXPECT_TRUE(result); +} + +// getSensorPortString() Tests +TEST_F(KestrelTest, GetPosLatValidLocation) { + Kestrel kestrel = createFullyMockedKestrel(); + + // First call should not trigger GPS update + String result = kestrel.getPosLat(); + EXPECT_EQ(result, "null"); // No location set yet + + // Now set a location through updateLocation + kestrel.updateLocation(true); // This would set latitude + // Test requires internal state which is hard to verify +} + +// testForBat() Tests +TEST_F(KestrelTest, TestForBatValidBattery) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up voltage reading to indicate valid battery + EXPECT_CALL(mockCsaAlpha, getBusVoltage(IChannel::CSA_CH1, false)) + .WillOnce(::testing::Return(3.7f)); // Valid battery voltage + + bool result = kestrel.testForBat(); + EXPECT_TRUE(result); +} + +TEST_F(KestrelTest, TestForBatLowBattery) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up voltage reading to indicate low/no battery + EXPECT_CALL(mockCsaAlpha, getBusVoltage(IChannel::CSA_CH1, false)) + .WillOnce(::testing::Return(1.5f)); // Low voltage + + bool result = kestrel.testForBat(); + EXPECT_FALSE(result); +} + +// getMessageID() Tests +TEST_F(KestrelTest, GetMessageIDValidTime) { + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(1000000)); + EXPECT_CALL(mockTimeProvider, millis()) + .WillOnce(::testing::Return(5000)); // 5 seconds + + unsigned long id = kestrel.getMessageID(); + EXPECT_EQ(id, 0); // 1000000 % 5 = 0 +} + +TEST_F(KestrelTest, GetMessageIDInvalidTime) { + Kestrel kestrel = createFullyMockedKestrel(); + + // When time is invalid (0), should use random number + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(0)); + + // Can't really test the random value, just ensure it doesn't crash + unsigned long id = kestrel.getMessageID(); + // Any value is acceptable for random +} + +// More comprehensive begin() test +TEST_F(KestrelTest, BeginFullInitializationSequence) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + + // Complete begin sequence + testing::InSequence seq; + + // System events + EXPECT_CALL(mockSystem, on(IEventType::TIME_CHANGED, ::testing::_)); + EXPECT_CALL(mockSystem, on(IEventType::OUT_OF_MEMORY, ::testing::_)); + + // I2C initialization + EXPECT_CALL(mockWire, isEnabled()).WillOnce(::testing::Return(false)); + EXPECT_CALL(mockWire, begin()); + EXPECT_CALL(mockWire, setClock(400000)); + + // IO Expanders + EXPECT_CALL(mockIoOB, begin()).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockIoTalon, begin()).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockIoTalon, safeMode(IIOExpander::SAFEOFF)); + + // CSA initialization + EXPECT_CALL(mockCsaAlpha, begin()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockCsaBeta, begin()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockCsaAlpha, setFrequency(IFrequency::CSA_SPS_64)); + + // LED driver + EXPECT_CALL(mockLed, begin()).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockLed, setOutputMode(ILed::IOutputMode::OpenDrain)); + EXPECT_CALL(mockLed, setGroupMode(ILed::IGroupMode::Blink)); + EXPECT_CALL(mockLed, setOutputArray(ILed::IPortState::Off)); + + // Serial port + EXPECT_CALL(mockSerialSdi12, begin(1200, 0b00000000)); + + // RTC + EXPECT_CALL(mockRtc, begin(true)).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockRtc, enableAlarm(false, 0)); + EXPECT_CALL(mockRtc, enableAlarm(false, 1)); + EXPECT_CALL(mockRtc, setMode(IRtc::Mode::Normal)); + + // GPS + EXPECT_CALL(mockGps, begin()).WillOnce(::testing::Return(true)); + EXPECT_CALL(mockGps, setI2COutput(GPS_COM_TYPE_UBX)); + EXPECT_CALL(mockGps, setNavigationFrequency(1)); + EXPECT_CALL(mockGps, setAutoPVT(false)); + + String result = kestrel.begin(0, criticalFault, fault); + + EXPECT_EQ(result, ""); + EXPECT_FALSE(criticalFault); +} + +// Additional edge case tests +TEST_F(KestrelTest, EnablePowerAndDataAllPorts) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test enabling all ports + for(int i = 1; i <= 5; i++) { + kestrel.enablePower(i, true); + kestrel.enableData(i, true); + } + + // Test disabling all ports + kestrel.disablePowerAll(); + kestrel.disableDataAll(); +} + +TEST_F(KestrelTest, GetDataWithSensorsEnabled) { + Kestrel kestrel = createFullyMockedKestrel(true); // Enable sensors + + // Set up ALS expectations + EXPECT_CALL(mockAls, begin()).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockAls, autoRange()).WillOnce(::testing::Return(1)); + + bool readState = false; + EXPECT_CALL(mockAls, getValue(::testing::_, ::testing::Ref(readState))) + .WillRepeatedly(::testing::DoAll( + ::testing::SetArgReferee<1>(false), + ::testing::Return(100.0f) + )); + + String result = kestrel.getData(0); + EXPECT_NE(result.indexOf("\"ALS\""), -1); // Should contain ALS data +} + +TEST_F(KestrelTest, SelfDiagnosticLevel5Memory) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test low memory warning (75% used) + EXPECT_CALL(mockSystem, freeMemory()) + .WillOnce(::testing::Return(20000)); // Below 46800 threshold + + String result = kestrel.selfDiagnostic(5, 0); + // Would need to check if RAM_LOW error was thrown +} + +// Add tests for sleep modes +TEST_F(KestrelTest, SleepPerformanceMode) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Performance mode should return immediately + int result = kestrel.sleep(); + EXPECT_EQ(result, 0); +} + +TEST_F(KestrelTest, WakePerformanceMode) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Performance mode should return immediately + int result = kestrel.wake(); + EXPECT_EQ(result, 0); +} + +// Tests for zero accelerometer +TEST_F(KestrelTest, ZeroAccelReset) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test reset mode + bool result = kestrel.zeroAccel(true); + EXPECT_TRUE(result); +} + +TEST_F(KestrelTest, ZeroAccelCalibrate) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up accelerometer expectations + EXPECT_CALL(mockAccel, begin()).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockAccel, getAccel(2, ::testing::_)).WillOnce(::testing::Return(1.0f)); + + bool result = kestrel.zeroAccel(false); + EXPECT_FALSE(result); +} + +// Test I2C enable/disable functions +TEST_F(KestrelTest, EnableI2COperations) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test OB I2C + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockGpio, pinMode(Pins::I2C_OB_EN, IPinMode::OUTPUT)); + EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_OB_EN, true)); + + bool prevState = kestrel.enableI2C_OB(true); + EXPECT_FALSE(prevState); + + // Test Global I2C + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)).WillOnce(::testing::Return(1)); + EXPECT_CALL(mockGpio, pinMode(Pins::I2C_GLOBAL_EN, IPinMode::OUTPUT)); + EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_GLOBAL_EN, false)); + + prevState = kestrel.enableI2C_Global(false); + EXPECT_TRUE(prevState); +} + +// Test SD card operations +TEST_F(KestrelTest, SDCardOperations) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test SD enable + EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_EN)).WillOnce(::testing::Return(0)); + EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_EN, ::testing::_)); + EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::SD_EN, 1)); + + bool prevState = kestrel.enableSD(true); + EXPECT_FALSE(prevState); + + // Test SD card detection + EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); + EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)).WillOnce(::testing::Return(0)); + + bool inserted = kestrel.sdInserted(); + EXPECT_TRUE(inserted); +} + +// Test aux power control +TEST_F(KestrelTest, AuxPowerControl) { + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockIoOB, digitalRead(PinsOB::AUX_EN)).WillOnce(::testing::Return(1)); + EXPECT_CALL(mockIoOB, pinMode(PinsOB::AUX_EN, ::testing::_)); + EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::AUX_EN, false)); + + bool prevState = kestrel.enableAuxPower(false); + EXPECT_TRUE(prevState); +} + +// Test total errors function +TEST_F(KestrelTest, TotalErrors) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Since we can't directly set errors, we can only test the getter + uint8_t total = kestrel.totalErrors(); + // Default should be 0 + 0 + EXPECT_EQ(total, 0); +} + +// Test fault detection +TEST_F(KestrelTest, GetFaultValidPort) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test fault present (pin LOW) + EXPECT_CALL(mockIoTalon, digitalRead(2)).WillOnce(::testing::Return(0)); // FAULT[0] for port 1 + + bool fault = kestrel.getFault(1); + EXPECT_TRUE(fault); + + // Test no fault (pin HIGH) + EXPECT_CALL(mockIoTalon, digitalRead(2)).WillOnce(::testing::Return(1)); + + fault = kestrel.getFault(1); + EXPECT_FALSE(fault); +} + +// Test statLED function +TEST_F(KestrelTest, StatLEDControl) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test LED on (state = true) + bool result = kestrel.statLED(true); + EXPECT_FALSE(result); // Always returns false + + // Test LED off (state = false) + result = kestrel.statLED(false); + EXPECT_FALSE(result); // Always returns false +} + +// Test connectToCell function +TEST_F(KestrelTest, ConnectToCellSuccess) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test successful connection + EXPECT_CALL(mockCloud, connect()).Times(1); + EXPECT_CALL(mockCloud, connected()) + .WillRepeatedly(::testing::Return(true)); + + bool result = kestrel.connectToCell(); + EXPECT_TRUE(result); +} + +TEST_F(KestrelTest, ConnectToCellFailure) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test failed connection (timeout) + EXPECT_CALL(mockCloud, connect()).Times(1); + EXPECT_CALL(mockCloud, connected()) + .WillRepeatedly(::testing::Return(false)); + + bool result = kestrel.connectToCell(); + EXPECT_FALSE(result); +} + +// Test releaseWDT function +TEST_F(KestrelTest, ReleaseWDTToggle) { + Kestrel kestrel = createFullyMockedKestrel(); + + // First call should return false (default) and set to true + bool result = kestrel.releaseWDT(); + EXPECT_FALSE(result); + + // Second call should return true (previous state) + result = kestrel.releaseWDT(); + EXPECT_TRUE(result); +} + +// Test syncTime function - limited test due to complexity +TEST_F(KestrelTest, SyncTimeBasicOperation) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up mock behavior for minimal sync time test + EXPECT_CALL(mockRtc, getTimeUnix()) + .WillOnce(::testing::Return(1585699200)); + + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(mockTimeProvider, now()) + .WillRepeatedly(::testing::Return(1585699200)); + + uint8_t source = kestrel.syncTime(false); + // Hard to test specific source without more detailed setup + EXPECT_LE(source, TimeSource::NONE); +} + +// Test configTalonSense function +TEST_F(KestrelTest, ConfigTalonSense) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test CSA Beta configuration + EXPECT_CALL(mockCsaBeta, setCurrentDirection(IChannel::CSA_CH4, 0)) + .Times(1); + EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH1, false)) + .Times(1); + EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH2, false)) + .Times(1); + EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH3, false)) + .Times(1); + EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH4, true)) + .Times(1); + + bool result = kestrel.configTalonSense(); + EXPECT_FALSE(result); +} + +// Test the updateTime function +TEST_F(KestrelTest, UpdateTimeSuccess) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up time provider expectations + EXPECT_CALL(mockTimeProvider, year()) + .WillOnce(::testing::Return(2023)); + EXPECT_CALL(mockTimeProvider, month()) + .WillOnce(::testing::Return(5)); + EXPECT_CALL(mockTimeProvider, day()) + .WillOnce(::testing::Return(15)); + EXPECT_CALL(mockTimeProvider, hour()) + .WillOnce(::testing::Return(10)); + EXPECT_CALL(mockTimeProvider, minute()) + .WillOnce(::testing::Return(30)); + EXPECT_CALL(mockTimeProvider, second()) + .WillOnce(::testing::Return(45)); + + uint8_t source = kestrel.updateTime(); + // Hard to test specific source in isolated test + EXPECT_LE(source, TimeSource::NONE); +} + +// Test getData for metadata gathering +TEST_F(KestrelTest, GetMetadataBasicInfo) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up time provider for metadata timestamp + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(true)); + EXPECT_CALL(mockTimeProvider, now()) + .WillRepeatedly(::testing::Return(1585699200)); + + // Set up RTC UUID for metadata + EXPECT_CALL(mockRtc, getUUIDString()) + .WillOnce(::testing::Return("test-uuid-1234")); + + String result = kestrel.getMetadata(); + EXPECT_NE(result.indexOf("\"RTC UUID\":\"test-uuid-1234\""), -1); + EXPECT_NE(result.indexOf("\"Time\":1585699200"), -1); +} + +// Test selfDiagnostic at different levels +TEST_F(KestrelTest, SelfDiagnosticLevel2) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test with level 2 diagnostics - should include accelerometer offsets + EXPECT_CALL(mockAccel, begin()) + .WillOnce(::testing::Return(0)); + + // Set up expectations for RTC register reads + EXPECT_CALL(mockRtc, readByte(0)) + .WillOnce(::testing::Return(0x80)); + EXPECT_CALL(mockRtc, readByte(3)) + .WillOnce(::testing::Return(0x38)); + EXPECT_CALL(mockRtc, readByte(7)) + .WillOnce(::testing::Return(0x12)); + EXPECT_CALL(mockRtc, readByte(8)) + .WillOnce(::testing::Return(0x34)); + EXPECT_CALL(mockRtc, readByte(0x0D)) + .WillOnce(::testing::Return(0x56)); + EXPECT_CALL(mockRtc, readByte(0x14)) + .WillOnce(::testing::Return(0x78)); + + String result = kestrel.selfDiagnostic(2, 0); + EXPECT_NE(result.indexOf("\"RTC_Config\""), -1); +} + +TEST_F(KestrelTest, SelfDiagnosticLevel3) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test RTC oscillator validation + EXPECT_CALL(mockWire, beginTransmission(0x6F)) + .Times(1); + EXPECT_CALL(mockWire, endTransmission()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockRtc, getTimeUnix()) + .WillOnce(::testing::Return(1585699200)) + .WillOnce(::testing::Return(1585699201)); // Should increment + + EXPECT_CALL(mockTimeProvider, delay(1200)) + .Times(1); + + String result = kestrel.selfDiagnostic(3, 0); + // Hard to verify all content in a simplified test +} + +TEST_F(KestrelTest, SelfDiagnosticLevel4) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test CSA setup and port readings + EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::CSA_EN, 1)) + .Times(1); + + EXPECT_CALL(mockCsaAlpha, begin()) + .WillOnce(::testing::Return(true)); + EXPECT_CALL(mockCsaBeta, begin()) + .WillOnce(::testing::Return(true)); + + // Setup for bus voltage readings - simplified + bool err = false; + EXPECT_CALL(mockCsaAlpha, getBusVoltage(::testing::_, true, ::testing::_)) + .WillRepeatedly(::testing::DoAll( + ::testing::SetArgReferee<2>(false), + ::testing::Return(3.3f) + )); + + EXPECT_CALL(mockCsaBeta, getBusVoltage(::testing::_, true, ::testing::_)) + .WillRepeatedly(::testing::DoAll( + ::testing::SetArgReferee<2>(false), + ::testing::Return(5.0f) + )); + + String result = kestrel.selfDiagnostic(4, 0); + EXPECT_NE(result.indexOf("\"PORT_V\""), -1); +} + +// Test GPS position getters +TEST_F(KestrelTest, PositionGetters) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Manually set position data + kestrel.updateLocation(true); + + // Test position getters + String latitude = kestrel.getPosLat(); + String longitude = kestrel.getPosLong(); + String altitude = kestrel.getPosAlt(); + time_t posTime = kestrel.getPosTime(); + String posTimeStr = kestrel.getPosTimeString(); + + // We can't easily verify values in isolation without injection ability +} + +// Test error handling and reporting +TEST_F(KestrelTest, GetErrorsFormatting) { + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockTimeProvider, now()) + .WillRepeatedly(::testing::Return(1585699200)); + + // There are no errors yet, so this should return an empty string + String errors = kestrel.getErrors(); + // We'd need to inject errors to test this fully +} + +// Test enableI2C_External with proper dependencies +TEST_F(KestrelTest, EnableI2CExternalIntegration) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test setup for external I2C enable + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)) + .WillOnce(::testing::Return(1)); + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockIoOB, digitalRead(PinsOB::I2C_EXT_EN)) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockIoOB, pinMode(PinsOB::I2C_EXT_EN, ::testing::_)); + EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::I2C_EXT_EN, true)); + + bool result = kestrel.enableI2C_External(true); + EXPECT_FALSE(result); +} + +// Enhanced sleep tests with mode configuration +TEST_F(KestrelTest, SleepBalancedMode) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Setup for Balanced mode sleep + // We would need to set powerSaveMode, but it's private + // This is a limitation of the current design + + // Set up mock for Clock_INT pin check + EXPECT_CALL(mockGpio, digitalRead(Pins::Clock_INT)) + .WillOnce(::testing::Return(1)); // Not triggered yet + + // We have limited ability to test internal state changes + int result = kestrel.sleep(); + // Can't verify result without knowing the mode +} + +// Test the GPS wake path in the wake function +TEST_F(KestrelTest, WakeWithGPSUpdate) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Setup for GPS wake test + // We would need to set powerSaveMode and posTime, but they're private + + // Set up basic I2C management for wake + EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_GLOBAL_EN, true)); + EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_OB_EN, false)); + + int result = kestrel.wake(); + // Can't verify result without knowing the mode +} + +// Test critical fault detection in selfDiagnostic +TEST_F(KestrelTest, SelfDiagnosticMemoryCritical) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test critical memory condition (90% used) + EXPECT_CALL(mockSystem, freeMemory()) + .WillOnce(::testing::Return(10000)); // Below 15600 threshold + + String result = kestrel.selfDiagnostic(5, 0); + // Would need to check if RAM_CRITICAL error was thrown +} + +// Test WDT feeding when there's a critical fault +TEST_F(KestrelTest, FeedWDTWithCriticalFault) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Set up a critical fault condition + // We'd need a way to set criticalFault to true + + // For now, we can only test the normal path + bool result = kestrel.feedWDT(); + EXPECT_TRUE(result); +} + +// Test accelerometer initialization fallback +TEST_F(KestrelTest, BeginAccelerometerFallback) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + + // Set up MXC6655 to fail and BMA456 to succeed + EXPECT_CALL(mockAccel, begin()) + .WillOnce(::testing::Return(-1)); // Fail + + // We'd need a way to test the BMA456 fallback + + String result = kestrel.begin(0, criticalFault, fault); + // Hard to test fallback mechanism in isolation +} + +// Test the entire diagnostic sequence +TEST_F(KestrelTest, FullDiagnosticSequence) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test with all diagnostic levels + for (int level = 0; level <= 5; level++) { + String result = kestrel.selfDiagnostic(level, 0); + EXPECT_GT(result.length(), 0); // Check that result is not empty + } +} + +// Test getData with different reportSensors settings +TEST_F(KestrelTest, GetDataWithReportSensorsToggle) { + // Test with sensors disabled + { + Kestrel kestrel = createFullyMockedKestrel(false); + String result = kestrel.getData(0); + EXPECT_GT(result.length(), 0); // Check that result is not empty + } + + // Test with sensors enabled + { + Kestrel kestrel = createFullyMockedKestrel(true); + + EXPECT_CALL(mockAls, begin()) + .WillOnce(::testing::Return(0)); + EXPECT_CALL(mockAls, autoRange()) + .WillOnce(::testing::Return(0)); + + bool readState = false; + EXPECT_CALL(mockAls, getValue(::testing::_, ::testing::Ref(readState))) + .WillRepeatedly(::testing::DoAll( + ::testing::SetArgReferee<1>(false), + ::testing::Return(100.0f) + )); + + String result = kestrel.getData(0); + EXPECT_GT(result.length(), 0); // Check that result is not empty + } +} + +// Test time validity check in getTime +TEST_F(KestrelTest, GetTimeWithInvalidTime) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Setup timeProvider to report invalid time + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(false)); + + // Time value should be 0 for invalid time + time_t result = kestrel.getTime(); + EXPECT_EQ(result, 0); +} + +// Test getTimeString with valid/invalid times +TEST_F(KestrelTest, GetTimeStringValidation) { + { + // Test valid time + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(true)); + EXPECT_CALL(mockTimeProvider, now()) + .WillOnce(::testing::Return(1585699200)); + + String result = kestrel.getTimeString(); + EXPECT_EQ(result, "1585699200"); + } + + { + // Test invalid time + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(false)); + + String result = kestrel.getTimeString(); + EXPECT_EQ(result, "null"); + } +} + +// Test sdInserted with different card states +TEST_F(KestrelTest, SDInsertedStates) { + { + // Test card inserted + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); + EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)) + .WillOnce(::testing::Return(0)); // LOW = inserted + + bool result = kestrel.sdInserted(); + EXPECT_TRUE(result); + } + + { + // Test card not inserted + Kestrel kestrel = createFullyMockedKestrel(); + + EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); + EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)) + .WillOnce(::testing::Return(1)); // HIGH = not inserted + + bool result = kestrel.sdInserted(); + EXPECT_FALSE(result); + } +} + +// Test setDirection function +TEST_F(KestrelTest, SetDirectionValidPort) { + Kestrel kestrel = createFullyMockedKestrel(); + + // Test setting direction for port 1 + EXPECT_CALL(mockIoTalon, pinMode(0, ::testing::_)); // SEL[0] for port 1 + EXPECT_CALL(mockIoTalon, digitalWrite(0, true)); + + bool result = kestrel.setDirection(1, true); + EXPECT_FALSE(result); // Always returns false +} + +// Final integration test +TEST_F(KestrelTest, CompleteLifecycleTest) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + + // Set up minimal begin expectations + EXPECT_CALL(mockSystem, on(::testing::_, ::testing::_)) + .Times(::testing::AtLeast(1)); + EXPECT_CALL(mockWire, isEnabled()) + .WillRepeatedly(::testing::Return(false)); + EXPECT_CALL(mockWire, begin()) + .Times(::testing::AtLeast(1)); + + // Begin the Kestrel + kestrel.begin(0, criticalFault, fault); + + // Test timer start/wait cycle + kestrel.startTimer(10); // Short period for test + bool timerResult = kestrel.waitUntilTimerDone(); + + // Test sleep and wake cycle + kestrel.sleep(); + kestrel.wake(); + + // Test WDT feeding + bool wdtResult = kestrel.feedWDT(); + + // Test data collection + String data = kestrel.getData(0); + String metadata = kestrel.getMetadata(); + String diagnostic = kestrel.selfDiagnostic(3, 0); + String errors = kestrel.getErrors(); + + // Overall lifecycle test is primarily checking for crashes +} // Verify Kestrel begin sets criticalFault true if ioOb.begin() fails // Verify Kestrel begin sets criticalFault true if ioTalon.begin() fails // Verify Kestrel begin sets criticalFault true if csaAlpha.begin() fails @@ -136,5 +1210,4 @@ TEST_F(KestrelTest, TestKestrelConstructor) { // Kestrel configTalonSense tests // Kestrel getMessageID tests // Kestrel testForBat tests -// Kestrel zeroAccel tests -*/ \ No newline at end of file +// Kestrel zeroAccel tests \ No newline at end of file From 555b92c13d386371f371c29cc47b115bb2748e46 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 8 May 2025 15:46:44 -0500 Subject: [PATCH 37/48] first round of passing tests for kestrel.cpp! --- lib/Driver_-_Kestrel | 2 +- test/unit/Driver_-_Kestrel/KestrelTest.cpp | 2142 +++++++++++--------- 2 files changed, 1174 insertions(+), 970 deletions(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index d0cbf77..b78cf75 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit d0cbf77b2bcde6329b991a1098c4be722e3ee75c +Subproject commit b78cf75150050f32f801e290e5145b832525ebcd diff --git a/test/unit/Driver_-_Kestrel/KestrelTest.cpp b/test/unit/Driver_-_Kestrel/KestrelTest.cpp index 718e5e9..388476e 100644 --- a/test/unit/Driver_-_Kestrel/KestrelTest.cpp +++ b/test/unit/Driver_-_Kestrel/KestrelTest.cpp @@ -101,8 +101,11 @@ TEST_F(KestrelTest, ConstructorWithUseSensorsTrue) { }); } -// Begin() Tests -TEST_F(KestrelTest, BeginFirstTimeInitializationThrowsSystemReset) { +////////////////////////////////////////////////////////////////////////////// +//BEGIN TESTS +////////////////////////////////////////////////////////////////////////////// +// verify critical fault is set rue if ioOB begin fails +TEST_F(KestrelTest, BeginIOBBeginFail) { Kestrel kestrel = createFullyMockedKestrel(); bool criticalFault = false; bool fault = false; @@ -117,50 +120,241 @@ TEST_F(KestrelTest, BeginFirstTimeInitializationThrowsSystemReset) { EXPECT_CALL(mockAccel, getOffset()) .WillRepeatedly(::testing::Return(data)); + EXPECT_CALL(mockIoOB, begin()) + .WillOnce(::testing::Return(1)); + + EXPECT_CALL(mockIoTalon, begin()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockRtc, begin(true)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(mockGps, begin()) + .WillRepeatedly(::testing::Return(true)); + + // We would need to verify throwError is called, but it's private + // This tests the overall begin() behavior + String result = kestrel.begin(0, criticalFault, fault); + + // Check that we get through initialization + EXPECT_TRUE(criticalFault); + EXPECT_FALSE(fault); + EXPECT_EQ(result, ""); +} + +// verify critical fault is set if ioTalon begin fails +TEST_F(KestrelTest, BeginIOTalonBeginFail) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + float data[3] = {0.0, 0.0, 0.0}; + + EXPECT_CALL(mockSystem, resetReason()) + .WillOnce(::testing::Return(IResetReason::WATCHDOG)); + + EXPECT_CALL(mockAccel, getData()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockAccel, getOffset()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockIoOB, begin()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockIoTalon, begin()) + .WillOnce(::testing::Return(1)); + + EXPECT_CALL(mockRtc, begin(true)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(mockGps, begin()) + .WillRepeatedly(::testing::Return(true)); + // We would need to verify throwError is called, but it's private // This tests the overall begin() behavior String result = kestrel.begin(0, criticalFault, fault); // Check that we get through initialization + EXPECT_TRUE(criticalFault); + EXPECT_FALSE(fault); EXPECT_EQ(result, ""); } -TEST_F(KestrelTest, BeginI2CInitializationSuccess) { +// verify that led setOupput is only called when initDone is false +TEST_F(KestrelTest, BeginLEDsetOutputOnlyCalledWhenInitDoneFalse) { +Kestrel kestrel = createFullyMockedKestrel(); +bool criticalFault = false; +bool fault = false; +float data[3] = {0.0, 0.0, 0.0}; + +EXPECT_CALL(mockSystem, resetReason()) + .WillOnce(::testing::Return(IResetReason::WATCHDOG)); + +EXPECT_CALL(mockAccel, getData()) + .WillRepeatedly(::testing::Return(data)); + +EXPECT_CALL(mockAccel, getOffset()) + .WillRepeatedly(::testing::Return(data)); + +EXPECT_CALL(mockIoOB, begin()) + .WillRepeatedly(::testing::Return(0)); + +EXPECT_CALL(mockIoTalon, begin()) + .WillRepeatedly(::testing::Return(0)); + +EXPECT_CALL(mockRtc, begin(true)) + .WillRepeatedly(::testing::Return(true)); + +EXPECT_CALL(mockGps, begin()) + .WillRepeatedly(::testing::Return(true)); + +EXPECT_CALL(mockLed, setOutputMode(ILed::IOutputMode::OpenDrain)) + .Times(1); + +// We would need to verify throwError is called, but it's private +// This tests the overall begin() behavior +String result = kestrel.begin(0, criticalFault, fault); + +// Check that we get through initialization +EXPECT_FALSE(criticalFault); +EXPECT_FALSE(fault); +EXPECT_EQ(result, ""); + +result = kestrel.begin(0, criticalFault, fault); + +// Check that we get through initialization +EXPECT_FALSE(criticalFault); +EXPECT_FALSE(fault); +EXPECT_EQ(result, ""); + +} + +//verify that critical fault is set if rtc.begin fails +TEST_F(KestrelTest, BeginRTCFail) { Kestrel kestrel = createFullyMockedKestrel(); bool criticalFault = false; bool fault = false; + float data[3] = {0.0, 0.0, 0.0}; + + EXPECT_CALL(mockSystem, resetReason()) + .WillOnce(::testing::Return(IResetReason::WATCHDOG)); + + EXPECT_CALL(mockAccel, getData()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockAccel, getOffset()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockIoOB, begin()) + .WillOnce(::testing::Return(0)); - // Test I2C initialization - EXPECT_CALL(mockWire, isEnabled()) + EXPECT_CALL(mockIoTalon, begin()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockRtc, begin(true)) .WillOnce(::testing::Return(false)); - EXPECT_CALL(mockWire, begin()) - .Times(1); - EXPECT_CALL(mockWire, setClock(400000)) - .Times(1); - kestrel.begin(0, criticalFault, fault); + EXPECT_CALL(mockGps, begin()) + .WillRepeatedly(::testing::Return(true)); - EXPECT_FALSE(criticalFault); + // We would need to verify throwError is called, but it's private + // This tests the overall begin() behavior + String result = kestrel.begin(0, criticalFault, fault); + + // Check that we get through initialization + EXPECT_TRUE(criticalFault); + EXPECT_FALSE(fault); + EXPECT_EQ(result, ""); } -TEST_F(KestrelTest, BeginIOExpanderInitializationFailureCritical) { +//verify that critical fault is set if gps.begin fails +TEST_F(KestrelTest, BeginGPSFail) { Kestrel kestrel = createFullyMockedKestrel(); bool criticalFault = false; bool fault = false; + float data[3] = {0.0, 0.0, 0.0}; + + EXPECT_CALL(mockSystem, resetReason()) + .WillOnce(::testing::Return(IResetReason::WATCHDOG)); + + EXPECT_CALL(mockAccel, getData()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockAccel, getOffset()) + .WillRepeatedly(::testing::Return(data)); - // Make IO expander initialization fail EXPECT_CALL(mockIoOB, begin()) - .WillOnce(::testing::Return(1)); // Non-zero = failure + .WillOnce(::testing::Return(0)); - kestrel.begin(0, criticalFault, fault); + EXPECT_CALL(mockIoTalon, begin()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockRtc, begin(true)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(mockGps, begin()) + .WillRepeatedly(::testing::Return(false)); + // We would need to verify throwError is called, but it's private + // This tests the overall begin() behavior + String result = kestrel.begin(0, criticalFault, fault); + + // Check that we get through initialization EXPECT_TRUE(criticalFault); + EXPECT_FALSE(fault); + EXPECT_EQ(result, ""); +} + +//verify that critical fault and fault are false when all begin calls are successul +TEST_F(KestrelTest, BeginAllSuccess) { + Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + float data[3] = {0.0, 0.0, 0.0}; + + EXPECT_CALL(mockSystem, resetReason()) + .WillOnce(::testing::Return(IResetReason::WATCHDOG)); + + EXPECT_CALL(mockAccel, getData()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockAccel, getOffset()) + .WillRepeatedly(::testing::Return(data)); + + EXPECT_CALL(mockIoOB, begin()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockIoTalon, begin()) + .WillOnce(::testing::Return(0)); + + EXPECT_CALL(mockRtc, begin(true)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(mockGps, begin()) + .WillRepeatedly(::testing::Return(true)); + + // We would need to verify throwError is called, but it's private + // This tests the overall begin() behavior + String result = kestrel.begin(0, criticalFault, fault); + + // Check that we get through initialization + EXPECT_FALSE(criticalFault); + EXPECT_FALSE(fault); + EXPECT_EQ(result, ""); } +////////////////////////////////////////////////////////////////////////////// +//END BEGIN TESTS +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// // enablePower() Tests +////////////////////////////////////////////////////////////////////////////// TEST_F(KestrelTest, EnablePowerPort5ReturnsImmediately) { Kestrel kestrel = createFullyMockedKestrel(); + EXPECT_CALL(mockIoTalon, pinMode(5, ::testing::_)) + .Times(0); // Should not be called for port 5 + // Port 5 is a special case - should return false bool result = kestrel.enablePower(5, true); EXPECT_FALSE(result); @@ -169,6 +363,9 @@ TEST_F(KestrelTest, EnablePowerPort5ReturnsImmediately) { TEST_F(KestrelTest, EnablePowerInvalidPortThrowsError) { Kestrel kestrel = createFullyMockedKestrel(); + EXPECT_CALL(mockIoTalon, pinMode(0, ::testing::_)) + .Times(0); // Should not be called for invalid ports + // Test port 0 and port > 4 kestrel.enablePower(0, true); kestrel.enablePower(10, true); @@ -182,23 +379,32 @@ TEST_F(KestrelTest, EnablePowerValidPortOperation) { // Set up expectations for enabling power on port 1 EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)) - .WillOnce(::testing::Return(0)); + .Times(2) + .WillRepeatedly(::testing::Return(0)); EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)) - .WillOnce(::testing::Return(0)); + .Times(2) + .WillRepeatedly(::testing::Return(0)); - EXPECT_CALL(mockIoTalon, pinMode(0, ::testing::_)) // EN[0] for port 1 + EXPECT_CALL(mockIoTalon, pinMode(3, IIOExpander::IPinMode::OUTPUT)) // EN[0] for port 1 .Times(1); - EXPECT_CALL(mockIoTalon, digitalWrite(0, true)) + EXPECT_CALL(mockIoTalon, digitalWrite(3, true)) .Times(1); bool result = kestrel.enablePower(1, true); EXPECT_FALSE(result); // Always returns false for debugging } +//////////////////////////////////////////////////////////////////////////////// +// End ebalePower() Tests +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // enableData() Tests +//////////////////////////////////////////////////////////////////////////////// TEST_F(KestrelTest, EnableDataPort5CallsExternalI2C) { Kestrel kestrel = createFullyMockedKestrel(); + EXPECT_CALL(mockIoTalon, pinMode(5, ::testing::_)) + .Times(0); // Should not be called for port 5 // Port 5 calls enableI2C_External // We'd need to mock the enableI2C_External behavior bool result = kestrel.enableData(5, true); @@ -208,17 +414,30 @@ TEST_F(KestrelTest, EnableDataPort5CallsExternalI2C) { TEST_F(KestrelTest, EnableDataValidPortOperation) { Kestrel kestrel = createFullyMockedKestrel(); - // Set up expectations for enabling data on port 1 - EXPECT_CALL(mockIoTalon, pinMode(1, ::testing::_)) // I2C_EN[0] for port 1 + // Set up expectations for enabling power on port 1 + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)) + .Times(2) + .WillRepeatedly(::testing::Return(0)); + EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)) + .Times(2) + .WillRepeatedly(::testing::Return(0)); + + EXPECT_CALL(mockIoTalon, pinMode(3, IIOExpander::IPinMode::OUTPUT)) // EN[0] for port 1 .Times(1); - EXPECT_CALL(mockIoTalon, digitalWrite(1, true)) + EXPECT_CALL(mockIoTalon, digitalWrite(3, true)) .Times(1); - bool result = kestrel.enableData(1, true); + bool result = kestrel.enablePower(1, true); EXPECT_FALSE(result); // Always returns false for debugging } +////////////////////////////////////////////////////////////////////////////////// +// End enableData() Tests +////////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// // feedWDT() Tests +///////////////////////////////////////////////////////////////////////////////// TEST_F(KestrelTest, FeedWDTNormalOperation) { Kestrel kestrel = createFullyMockedKestrel(); @@ -226,7 +445,7 @@ TEST_F(KestrelTest, FeedWDTNormalOperation) { EXPECT_CALL(mockGpio, pinMode(Pins::WD_HOLD, IPinMode::OUTPUT)) .Times(1); EXPECT_CALL(mockGpio, digitalWrite(Pins::WD_HOLD, 0)) - .Times(1); + .Times(2); EXPECT_CALL(mockGpio, digitalWrite(Pins::WD_HOLD, 1)) .Times(1); EXPECT_CALL(mockTimeProvider, delay(1)) @@ -236,978 +455,963 @@ TEST_F(KestrelTest, FeedWDTNormalOperation) { EXPECT_TRUE(result); } -// getTime() Tests -TEST_F(KestrelTest, GetTimeValidTimeProvider) { +// Test for WDT not being fed +TEST_F(KestrelTest, FeedWDTCriticalFault) { Kestrel kestrel = createFullyMockedKestrel(); + bool criticalFault = false; + bool fault = false; + float data[3] = {0.0, 0.0, 0.0}; - // Set up a valid time condition - EXPECT_CALL(mockTimeProvider, isValid()) - .WillRepeatedly(::testing::Return(true)); - EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(1585699200)); // April 1, 2020 - - time_t time = kestrel.getTime(); - EXPECT_EQ(time, 1585699200); -} - -TEST_F(KestrelTest, GetTimeInvalidTimeTriggersSync) { - Kestrel kestrel = createFullyMockedKestrel(); + EXPECT_CALL(mockSystem, resetReason()) + .WillOnce(::testing::Return(IResetReason::WATCHDOG)); - // First call: time is invalid - // Second call (after sync): time is valid - EXPECT_CALL(mockTimeProvider, isValid()) - .WillOnce(::testing::Return(false)) - .WillRepeatedly(::testing::Return(true)); + EXPECT_CALL(mockAccel, getData()) + .WillRepeatedly(::testing::Return(data)); - EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(1585699200)); + EXPECT_CALL(mockAccel, getOffset()) + .WillRepeatedly(::testing::Return(data)); - // This should trigger a sync and return valid time - time_t time = kestrel.getTime(); - EXPECT_EQ(time, 1585699200); -} - -// getTimeString() Tests -TEST_F(KestrelTest, GetTimeStringValidTime) { - Kestrel kestrel = createFullyMockedKestrel(); + EXPECT_CALL(mockIoOB, begin()) + .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockTimeProvider, isValid()) - .WillRepeatedly(::testing::Return(true)); - EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(1585699200)); + EXPECT_CALL(mockIoTalon, begin()) + .WillOnce(::testing::Return(0)); - String result = kestrel.getTimeString(); - EXPECT_EQ(result, "1585699200"); -} - -TEST_F(KestrelTest, GetTimeStringInvalidTime) { - Kestrel kestrel = createFullyMockedKestrel(); + EXPECT_CALL(mockRtc, begin(true)) + .WillOnce(::testing::Return(true)); - // Force getTime() to return 0 - EXPECT_CALL(mockTimeProvider, isValid()) + EXPECT_CALL(mockGps, begin()) .WillRepeatedly(::testing::Return(false)); - String result = kestrel.getTimeString(); - EXPECT_EQ(result, "null"); -} + // We would need to verify throwError is called, but it's private + // This tests the overall begin() behavior + kestrel.begin(0, criticalFault, fault); -// setIndicatorState() Tests -TEST_F(KestrelTest, SetIndicatorStateSensorsPass) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test SENSORS bank with PASS mode - EXPECT_CALL(mockLed, setOutput(0, ILed::IPortState::PWM)) // Green on - .Times(1); - EXPECT_CALL(mockLed, setOutput(1, ILed::IPortState::Off)) // Amber off - .Times(1); - EXPECT_CALL(mockLed, setOutput(2, ILed::IPortState::Off)) // Red off - .Times(1); - - bool result = kestrel.setIndicatorState(IndicatorLight::SENSORS, IndicatorMode::PASS); - EXPECT_EQ(result, 0); + bool result = kestrel.feedWDT(); + EXPECT_FALSE(result); // Should return false if critical fault is set } -TEST_F(KestrelTest, SetIndicatorStateAllInit) { +//////////////////////////////////////////////////////////////////////////////////////// +// getTime() Tests +//////////////////////////////////////////////////////////////////////////////////////// +TEST_F(KestrelTest, GetTimeValidTimeProvider) { Kestrel kestrel = createFullyMockedKestrel(); - // Test ALL bank with INIT mode - should set all to group blink - EXPECT_CALL(mockLed, setOutputArray(ILed::IPortState::Group)) - .Times(1); - EXPECT_CALL(mockLed, setGroupBlinkPeriod(250)) - .Times(1); - EXPECT_CALL(mockLed, setGroupOnTime(25)) - .Times(1); + // Set up a valid time condition + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(true)); + EXPECT_CALL(mockTimeProvider, now()) + .WillRepeatedly(::testing::Return(1585699200)); // April 1, 2020 - bool result = kestrel.setIndicatorState(IndicatorLight::ALL, IndicatorMode::INIT); - EXPECT_EQ(result, 0); + time_t time = kestrel.getTime(); + EXPECT_EQ(time, 1585699200); } -// updateLocation() Tests -TEST_F(KestrelTest, UpdateLocationForceUpdate) { +TEST_F(KestrelTest, GetTimeInvalidTimeProvider) { Kestrel kestrel = createFullyMockedKestrel(); - // Set up GPS to have a valid fix - EXPECT_CALL(mockGps, getPVT()) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(mockGps, getFixType()) - .WillOnce(::testing::Return(3)); // 3D fix - EXPECT_CALL(mockGps, getGnssFixOk()) - .WillOnce(::testing::Return(true)); - - EXPECT_CALL(mockGps, getLongitude()) - .WillOnce(::testing::Return(1000000)); // Example value - EXPECT_CALL(mockGps, getLatitude()) - .WillOnce(::testing::Return(2000000)); // Example value - EXPECT_CALL(mockGps, getAltitude()) - .WillOnce(::testing::Return(3000)); // Example value + // First call: time is invalid + // Second call (after sync): time is valid + EXPECT_CALL(mockTimeProvider, isValid()) + .WillRepeatedly(::testing::Return(false)); EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(1585699200)); - - bool result = kestrel.updateLocation(true); - EXPECT_TRUE(result); -} - -TEST_F(KestrelTest, UpdateLocationNoFix) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up GPS to have no fix - EXPECT_CALL(mockGps, getPVT()) - .WillOnce(::testing::Return(false)); + .WillRepeatedly(::testing::Return(1585699200)); - bool result = kestrel.updateLocation(true); - EXPECT_FALSE(result); -} + // This should trigger a sync and return valid time + time_t time = kestrel.getTime(); + EXPECT_EQ(time, 0); +} +//////////////////////////////////////////////////////////////////////////////////////// +//End getTime() tests +//////////////////////////////////////////////////////////////////////////////////////// + +// // getTimeString() Tests +// TEST_F(KestrelTest, GetTimeStringValidTime) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// EXPECT_CALL(mockTimeProvider, isValid()) +// .WillRepeatedly(::testing::Return(true)); +// EXPECT_CALL(mockTimeProvider, now()) +// .WillOnce(::testing::Return(1585699200)); + +// String result = kestrel.getTimeString(); +// EXPECT_EQ(result, "1585699200"); +// } + +// TEST_F(KestrelTest, TestTimeStringInvalidTime) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Force getTime() to return 0 +// EXPECT_CALL(mockTimeProvider, isValid()) +// .WillRepeatedly(::testing::Return(false)); + +// String result = kestrel.getTimeString(); +// EXPECT_EQ(result, "null"); +// } + +// // setIndicatorState() Tests +// TEST_F(KestrelTest, SetIndicatorStateSensorsPass) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test SENSORS bank with PASS mode +// EXPECT_CALL(mockLed, setOutput(0, ILed::IPortState::PWM)) // Green on +// .Times(1); +// EXPECT_CALL(mockLed, setOutput(1, ILed::IPortState::Off)) // Amber off +// .Times(1); +// EXPECT_CALL(mockLed, setOutput(2, ILed::IPortState::Off)) // Red off +// .Times(1); + +// bool result = kestrel.setIndicatorState(IndicatorLight::SENSORS, IndicatorMode::PASS); +// EXPECT_EQ(result, 0); +// } + +// TEST_F(KestrelTest, SetIndicatorStateAllInit) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test ALL bank with INIT mode - should set all to group blink +// EXPECT_CALL(mockLed, setOutputArray(ILed::IPortState::Group)) +// .Times(1); +// EXPECT_CALL(mockLed, setGroupBlinkPeriod(250)) +// .Times(1); +// EXPECT_CALL(mockLed, setGroupOnTime(25)) +// .Times(1); + +// bool result = kestrel.setIndicatorState(IndicatorLight::ALL, IndicatorMode::INIT); +// EXPECT_EQ(result, 0); +// } + +// // updateLocation() Tests +// TEST_F(KestrelTest, UpdateLocationForceUpdate) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up GPS to have a valid fix +// EXPECT_CALL(mockGps, getPVT()) +// .WillOnce(::testing::Return(true)); +// EXPECT_CALL(mockGps, getFixType()) +// .WillOnce(::testing::Return(3)); // 3D fix +// EXPECT_CALL(mockGps, getGnssFixOk()) +// .WillOnce(::testing::Return(true)); + +// EXPECT_CALL(mockGps, getLongitude()) +// .WillOnce(::testing::Return(1000000)); // Example value +// EXPECT_CALL(mockGps, getLatitude()) +// .WillOnce(::testing::Return(2000000)); // Example value +// EXPECT_CALL(mockGps, getAltitude()) +// .WillOnce(::testing::Return(3000)); // Example value + +// EXPECT_CALL(mockTimeProvider, now()) +// .WillOnce(::testing::Return(1585699200)); + +// bool result = kestrel.updateLocation(true); +// EXPECT_TRUE(result); +// } + +// TEST_F(KestrelTest, UpdateLocationNoFix) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up GPS to have no fix +// EXPECT_CALL(mockGps, getPVT()) +// .WillOnce(::testing::Return(false)); + +// bool result = kestrel.updateLocation(true); +// EXPECT_FALSE(result); +// } + +// // startTimer() Tests +// TEST_F(KestrelTest, StartTimerDefaultPeriod) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test with period = 0 should use defaultPeriod +// EXPECT_CALL(mockRtc, setAlarm(300, true)) // defaultPeriod is 300 +// .Times(1); + +// bool result = kestrel.startTimer(0); +// EXPECT_FALSE(result); +// } + +// TEST_F(KestrelTest, StartTimerCustomPeriod) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// EXPECT_CALL(mockRtc, setAlarm(600, true)) +// .Times(1); + +// bool result = kestrel.startTimer(600); +// EXPECT_FALSE(result); +// } + +// // waitUntilTimerDone() Tests +// TEST_F(KestrelTest, WaitUntilTimerDoneUnitialized) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Without calling startTimer(), logPeriod should be 0 +// bool result = kestrel.waitUntilTimerDone(); +// EXPECT_FALSE(result); +// } + +// TEST_F(KestrelTest, WaitUntilTimerDoneSuccess) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Start a timer first +// kestrel.startTimer(300); + +// // Set up GPIO to show clock interrupt triggered +// EXPECT_CALL(mockGpio, digitalRead(Pins::Clock_INT)) +// .WillOnce(::testing::Return(0)); // LOW = interrupt triggered + +// bool result = kestrel.waitUntilTimerDone(); +// EXPECT_TRUE(result); +// } + +// // getSensorPortString() Tests +// TEST_F(KestrelTest, GetPosLatValidLocation) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // First call should not trigger GPS update +// String result = kestrel.getPosLat(); +// EXPECT_EQ(result, "null"); // No location set yet + +// // Now set a location through updateLocation +// kestrel.updateLocation(true); // This would set latitude +// // Test requires internal state which is hard to verify +// } + +// // testForBat() Tests +// TEST_F(KestrelTest, TestForBatValidBattery) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up voltage reading to indicate valid battery +// EXPECT_CALL(mockCsaAlpha, getBusVoltage(IChannel::CSA_CH1, false)) +// .WillOnce(::testing::Return(3.7f)); // Valid battery voltage + +// bool result = kestrel.testForBat(); +// EXPECT_TRUE(result); +// } + +// TEST_F(KestrelTest, TestForBatLowBattery) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up voltage reading to indicate low/no battery +// EXPECT_CALL(mockCsaAlpha, getBusVoltage(IChannel::CSA_CH1, false)) +// .WillOnce(::testing::Return(1.5f)); // Low voltage + +// bool result = kestrel.testForBat(); +// EXPECT_FALSE(result); +// } -// startTimer() Tests -TEST_F(KestrelTest, StartTimerDefaultPeriod) { - Kestrel kestrel = createFullyMockedKestrel(); +// // getMessageID() Tests +// TEST_F(KestrelTest, GetMessageIDValidTime) { +// Kestrel kestrel = createFullyMockedKestrel(); - // Test with period = 0 should use defaultPeriod - EXPECT_CALL(mockRtc, setAlarm(300, true)) // defaultPeriod is 300 - .Times(1); +// EXPECT_CALL(mockTimeProvider, now()) +// .WillOnce(::testing::Return(1000000)); +// EXPECT_CALL(mockTimeProvider, millis()) +// .WillOnce(::testing::Return(5000)); // 5 seconds + +// unsigned long id = kestrel.getMessageID(); +// EXPECT_EQ(id, 0); // 1000000 % 5 = 0 +// } + +// TEST_F(KestrelTest, GetMessageIDInvalidTime) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // When time is invalid (0), should use random number +// EXPECT_CALL(mockTimeProvider, now()) +// .WillOnce(::testing::Return(0)); + +// // Can't really test the random value, just ensure it doesn't crash +// unsigned long id = kestrel.getMessageID(); +// // Any value is acceptable for random +// } + +// // Additional edge case tests +// TEST_F(KestrelTest, EnablePowerAndDataAllPorts) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test enabling all ports +// for(int i = 1; i <= 5; i++) { +// kestrel.enablePower(i, true); +// kestrel.enableData(i, true); +// } + +// // Test disabling all ports +// kestrel.disablePowerAll(); +// kestrel.disableDataAll(); +// } + +// TEST_F(KestrelTest, GetDataWithSensorsEnabled) { +// Kestrel kestrel = createFullyMockedKestrel(true); // Enable sensors + +// // Set up ALS expectations +// EXPECT_CALL(mockAls, begin()).WillOnce(::testing::Return(0)); +// EXPECT_CALL(mockAls, autoRange()).WillOnce(::testing::Return(1)); + +// bool readState = false; +// EXPECT_CALL(mockAls, getValue(::testing::_, ::testing::Ref(readState))) +// .WillRepeatedly(::testing::DoAll( +// ::testing::SetArgReferee<1>(false), +// ::testing::Return(100.0f) +// )); + +// String result = kestrel.getData(0); +// EXPECT_NE(result.indexOf("\"ALS\""), -1); // Should contain ALS data +// } + +// TEST_F(KestrelTest, SelfDiagnosticLevel5Memory) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test low memory warning (75% used) +// EXPECT_CALL(mockSystem, freeMemory()) +// .WillOnce(::testing::Return(20000)); // Below 46800 threshold + +// String result = kestrel.selfDiagnostic(5, 0); +// // Would need to check if RAM_LOW error was thrown +// } + +// // Add tests for sleep modes +// TEST_F(KestrelTest, SleepPerformanceMode) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Performance mode should return immediately +// int result = kestrel.sleep(); +// EXPECT_EQ(result, 0); +// } + +// TEST_F(KestrelTest, WakePerformanceMode) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Performance mode should return immediately +// int result = kestrel.wake(); +// EXPECT_EQ(result, 0); +// } + +// // Tests for zero accelerometer +// TEST_F(KestrelTest, ZeroAccelReset) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test reset mode +// bool result = kestrel.zeroAccel(true); +// EXPECT_TRUE(result); +// } + +// TEST_F(KestrelTest, ZeroAccelCalibrate) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up accelerometer expectations +// EXPECT_CALL(mockAccel, begin()).WillOnce(::testing::Return(0)); +// EXPECT_CALL(mockAccel, getAccel(2, ::testing::_)).WillOnce(::testing::Return(1.0f)); + +// bool result = kestrel.zeroAccel(false); +// EXPECT_FALSE(result); +// } + +// // Test I2C enable/disable functions +// TEST_F(KestrelTest, EnableI2COperations) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test OB I2C +// EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)).WillOnce(::testing::Return(0)); +// EXPECT_CALL(mockGpio, pinMode(Pins::I2C_OB_EN, IPinMode::OUTPUT)); +// EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_OB_EN, true)); + +// bool prevState = kestrel.enableI2C_OB(true); +// EXPECT_FALSE(prevState); + +// // Test Global I2C +// EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)).WillOnce(::testing::Return(1)); +// EXPECT_CALL(mockGpio, pinMode(Pins::I2C_GLOBAL_EN, IPinMode::OUTPUT)); +// EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_GLOBAL_EN, false)); + +// prevState = kestrel.enableI2C_Global(false); +// EXPECT_TRUE(prevState); +// } + +// // Test SD card operations +// TEST_F(KestrelTest, SDCardOperations) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test SD enable +// EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_EN)).WillOnce(::testing::Return(0)); +// EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_EN, ::testing::_)); +// EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::SD_EN, 1)); + +// bool prevState = kestrel.enableSD(true); +// EXPECT_FALSE(prevState); + +// // Test SD card detection +// EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); +// EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)).WillOnce(::testing::Return(0)); + +// bool inserted = kestrel.sdInserted(); +// EXPECT_TRUE(inserted); +// } + +// // Test aux power control +// TEST_F(KestrelTest, AuxPowerControl) { +// Kestrel kestrel = createFullyMockedKestrel(); - bool result = kestrel.startTimer(0); - EXPECT_FALSE(result); -} - -TEST_F(KestrelTest, StartTimerCustomPeriod) { - Kestrel kestrel = createFullyMockedKestrel(); +// EXPECT_CALL(mockIoOB, digitalRead(PinsOB::AUX_EN)).WillOnce(::testing::Return(1)); +// EXPECT_CALL(mockIoOB, pinMode(PinsOB::AUX_EN, ::testing::_)); +// EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::AUX_EN, false)); + +// bool prevState = kestrel.enableAuxPower(false); +// EXPECT_TRUE(prevState); +// } + +// // Test total errors function +// TEST_F(KestrelTest, TotalErrors) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Since we can't directly set errors, we can only test the getter +// uint8_t total = kestrel.totalErrors(); +// // Default should be 0 + 0 +// EXPECT_EQ(total, 0); +// } + +// // Test fault detection +// TEST_F(KestrelTest, GetFaultValidPort) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test fault present (pin LOW) +// EXPECT_CALL(mockIoTalon, digitalRead(2)).WillOnce(::testing::Return(0)); // FAULT[0] for port 1 + +// bool fault = kestrel.getFault(1); +// EXPECT_TRUE(fault); + +// // Test no fault (pin HIGH) +// EXPECT_CALL(mockIoTalon, digitalRead(2)).WillOnce(::testing::Return(1)); + +// fault = kestrel.getFault(1); +// EXPECT_FALSE(fault); +// } + +// // Test statLED function +// TEST_F(KestrelTest, StatLEDControl) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test LED on (state = true) +// bool result = kestrel.statLED(true); +// EXPECT_FALSE(result); // Always returns false + +// // Test LED off (state = false) +// result = kestrel.statLED(false); +// EXPECT_FALSE(result); // Always returns false +// } + +// // Test connectToCell function +// TEST_F(KestrelTest, ConnectToCellSuccess) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test successful connection +// EXPECT_CALL(mockCloud, connect()).Times(1); +// EXPECT_CALL(mockCloud, connected()) +// .WillRepeatedly(::testing::Return(true)); + +// bool result = kestrel.connectToCell(); +// EXPECT_TRUE(result); +// } + +// TEST_F(KestrelTest, ConnectToCellFailure) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test failed connection (timeout) +// EXPECT_CALL(mockCloud, connect()).Times(1); +// EXPECT_CALL(mockCloud, connected()) +// .WillRepeatedly(::testing::Return(false)); + +// bool result = kestrel.connectToCell(); +// EXPECT_FALSE(result); +// } + +// // Test releaseWDT function +// TEST_F(KestrelTest, ReleaseWDTToggle) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // First call should return false (default) and set to true +// bool result = kestrel.releaseWDT(); +// EXPECT_FALSE(result); + +// // Second call should return true (previous state) +// result = kestrel.releaseWDT(); +// EXPECT_TRUE(result); +// } + +// // Test syncTime function - limited test due to complexity +// TEST_F(KestrelTest, SyncTimeBasicOperation) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up mock behavior for minimal sync time test +// EXPECT_CALL(mockRtc, getTimeUnix()) +// .WillOnce(::testing::Return(1585699200)); + +// EXPECT_CALL(mockTimeProvider, isValid()) +// .WillRepeatedly(::testing::Return(true)); + +// EXPECT_CALL(mockTimeProvider, now()) +// .WillRepeatedly(::testing::Return(1585699200)); + +// uint8_t source = kestrel.syncTime(false); +// // Hard to test specific source without more detailed setup +// EXPECT_LE(source, TimeSource::NONE); +// } + +// // Test configTalonSense function +// TEST_F(KestrelTest, ConfigTalonSense) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test CSA Beta configuration +// EXPECT_CALL(mockCsaBeta, setCurrentDirection(IChannel::CSA_CH4, 0)) +// .Times(1); +// EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH1, false)) +// .Times(1); +// EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH2, false)) +// .Times(1); +// EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH3, false)) +// .Times(1); +// EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH4, true)) +// .Times(1); + +// bool result = kestrel.configTalonSense(); +// EXPECT_FALSE(result); +// } + +// // Test the updateTime function +// TEST_F(KestrelTest, UpdateTimeSuccess) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up time provider expectations +// EXPECT_CALL(mockTimeProvider, year()) +// .WillOnce(::testing::Return(2023)); +// EXPECT_CALL(mockTimeProvider, month()) +// .WillOnce(::testing::Return(5)); +// EXPECT_CALL(mockTimeProvider, day()) +// .WillOnce(::testing::Return(15)); +// EXPECT_CALL(mockTimeProvider, hour()) +// .WillOnce(::testing::Return(10)); +// EXPECT_CALL(mockTimeProvider, minute()) +// .WillOnce(::testing::Return(30)); +// EXPECT_CALL(mockTimeProvider, second()) +// .WillOnce(::testing::Return(45)); + +// uint8_t source = kestrel.updateTime(); +// // Hard to test specific source in isolated test +// EXPECT_LE(source, TimeSource::NONE); +// } + +// // Test getData for metadata gathering +// TEST_F(KestrelTest, GetMetadataBasicInfo) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up time provider for metadata timestamp +// EXPECT_CALL(mockTimeProvider, isValid()) +// .WillRepeatedly(::testing::Return(true)); +// EXPECT_CALL(mockTimeProvider, now()) +// .WillRepeatedly(::testing::Return(1585699200)); + +// // Set up RTC UUID for metadata +// EXPECT_CALL(mockRtc, getUUIDString()) +// .WillOnce(::testing::Return("test-uuid-1234")); + +// String result = kestrel.getMetadata(); +// EXPECT_NE(result.indexOf("\"RTC UUID\":\"test-uuid-1234\""), -1); +// EXPECT_NE(result.indexOf("\"Time\":1585699200"), -1); +// } + +// // Test selfDiagnostic at different levels +// TEST_F(KestrelTest, SelfDiagnosticLevel2) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test with level 2 diagnostics - should include accelerometer offsets +// EXPECT_CALL(mockAccel, begin()) +// .WillOnce(::testing::Return(0)); + +// // Set up expectations for RTC register reads +// EXPECT_CALL(mockRtc, readByte(0)) +// .WillOnce(::testing::Return(0x80)); +// EXPECT_CALL(mockRtc, readByte(3)) +// .WillOnce(::testing::Return(0x38)); +// EXPECT_CALL(mockRtc, readByte(7)) +// .WillOnce(::testing::Return(0x12)); +// EXPECT_CALL(mockRtc, readByte(8)) +// .WillOnce(::testing::Return(0x34)); +// EXPECT_CALL(mockRtc, readByte(0x0D)) +// .WillOnce(::testing::Return(0x56)); +// EXPECT_CALL(mockRtc, readByte(0x14)) +// .WillOnce(::testing::Return(0x78)); + +// String result = kestrel.selfDiagnostic(2, 0); +// EXPECT_NE(result.indexOf("\"RTC_Config\""), -1); +// } + +// TEST_F(KestrelTest, SelfDiagnosticLevel3) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test RTC oscillator validation +// EXPECT_CALL(mockWire, beginTransmission(0x6F)) +// .Times(1); +// EXPECT_CALL(mockWire, endTransmission()) +// .WillOnce(::testing::Return(0)); + +// EXPECT_CALL(mockRtc, getTimeUnix()) +// .WillOnce(::testing::Return(1585699200)) +// .WillOnce(::testing::Return(1585699201)); // Should increment + +// EXPECT_CALL(mockTimeProvider, delay(1200)) +// .Times(1); + +// String result = kestrel.selfDiagnostic(3, 0); +// // Hard to verify all content in a simplified test +// } + +// TEST_F(KestrelTest, SelfDiagnosticLevel4) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test CSA setup and port readings +// EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::CSA_EN, 1)) +// .Times(1); + +// EXPECT_CALL(mockCsaAlpha, begin()) +// .WillOnce(::testing::Return(true)); +// EXPECT_CALL(mockCsaBeta, begin()) +// .WillOnce(::testing::Return(true)); + +// // Setup for bus voltage readings - simplified +// bool err = false; +// EXPECT_CALL(mockCsaAlpha, getBusVoltage(::testing::_, true, ::testing::_)) +// .WillRepeatedly(::testing::DoAll( +// ::testing::SetArgReferee<2>(false), +// ::testing::Return(3.3f) +// )); + +// EXPECT_CALL(mockCsaBeta, getBusVoltage(::testing::_, true, ::testing::_)) +// .WillRepeatedly(::testing::DoAll( +// ::testing::SetArgReferee<2>(false), +// ::testing::Return(5.0f) +// )); + +// String result = kestrel.selfDiagnostic(4, 0); +// EXPECT_NE(result.indexOf("\"PORT_V\""), -1); +// } + +// // Test GPS position getters +// TEST_F(KestrelTest, PositionGetters) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Manually set position data +// kestrel.updateLocation(true); + +// // Test position getters +// String latitude = kestrel.getPosLat(); +// String longitude = kestrel.getPosLong(); +// String altitude = kestrel.getPosAlt(); +// time_t posTime = kestrel.getPosTime(); +// String posTimeStr = kestrel.getPosTimeString(); + +// // We can't easily verify values in isolation without injection ability +// } + +// // Test error handling and reporting +// TEST_F(KestrelTest, GetErrorsFormatting) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// EXPECT_CALL(mockTimeProvider, now()) +// .WillRepeatedly(::testing::Return(1585699200)); + +// // There are no errors yet, so this should return an empty string +// String errors = kestrel.getErrors(); +// // We'd need to inject errors to test this fully +// } + +// // Test enableI2C_External with proper dependencies +// TEST_F(KestrelTest, EnableI2CExternalIntegration) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test setup for external I2C enable +// EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)) +// .WillOnce(::testing::Return(1)); +// EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)) +// .WillOnce(::testing::Return(0)); + +// EXPECT_CALL(mockIoOB, digitalRead(PinsOB::I2C_EXT_EN)) +// .WillOnce(::testing::Return(0)); +// EXPECT_CALL(mockIoOB, pinMode(PinsOB::I2C_EXT_EN, ::testing::_)); +// EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::I2C_EXT_EN, true)); + +// bool result = kestrel.enableI2C_External(true); +// EXPECT_FALSE(result); +// } + +// // Enhanced sleep tests with mode configuration +// TEST_F(KestrelTest, SleepBalancedMode) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Setup for Balanced mode sleep +// // We would need to set powerSaveMode, but it's private +// // This is a limitation of the current design + +// // Set up mock for Clock_INT pin check +// EXPECT_CALL(mockGpio, digitalRead(Pins::Clock_INT)) +// .WillOnce(::testing::Return(1)); // Not triggered yet + +// // We have limited ability to test internal state changes +// int result = kestrel.sleep(); +// // Can't verify result without knowing the mode +// } + +// // Test the GPS wake path in the wake function +// TEST_F(KestrelTest, WakeWithGPSUpdate) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Setup for GPS wake test +// // We would need to set powerSaveMode and posTime, but they're private + +// // Set up basic I2C management for wake +// EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_GLOBAL_EN, true)); +// EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_OB_EN, false)); + +// int result = kestrel.wake(); +// // Can't verify result without knowing the mode +// } + +// // Test critical fault detection in selfDiagnostic +// TEST_F(KestrelTest, SelfDiagnosticMemoryCritical) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test critical memory condition (90% used) +// EXPECT_CALL(mockSystem, freeMemory()) +// .WillOnce(::testing::Return(10000)); // Below 15600 threshold + +// String result = kestrel.selfDiagnostic(5, 0); +// // Would need to check if RAM_CRITICAL error was thrown +// } + +// // Test WDT feeding when there's a critical fault +// TEST_F(KestrelTest, FeedWDTWithCriticalFault) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Set up a critical fault condition +// // We'd need a way to set criticalFault to true + +// // For now, we can only test the normal path +// bool result = kestrel.feedWDT(); +// EXPECT_TRUE(result); +// } + +// // Test accelerometer initialization fallback +// TEST_F(KestrelTest, BeginAccelerometerFallback) { +// Kestrel kestrel = createFullyMockedKestrel(); +// bool criticalFault = false; +// bool fault = false; + +// // Set up MXC6655 to fail and BMA456 to succeed +// EXPECT_CALL(mockAccel, begin()) +// .WillOnce(::testing::Return(-1)); // Fail + +// // We'd need a way to test the BMA456 fallback + +// String result = kestrel.begin(0, criticalFault, fault); +// // Hard to test fallback mechanism in isolation +// } + +// // Test the entire diagnostic sequence +// TEST_F(KestrelTest, FullDiagnosticSequence) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test with all diagnostic levels +// for (int level = 0; level <= 5; level++) { +// String result = kestrel.selfDiagnostic(level, 0); +// EXPECT_GT(result.length(), 0); // Check that result is not empty +// } +// } + +// // Test getData with different reportSensors settings +// TEST_F(KestrelTest, GetDataWithReportSensorsToggle) { +// // Test with sensors disabled +// { +// Kestrel kestrel = createFullyMockedKestrel(false); +// String result = kestrel.getData(0); +// EXPECT_GT(result.length(), 0); // Check that result is not empty +// } + +// // Test with sensors enabled +// { +// Kestrel kestrel = createFullyMockedKestrel(true); + +// EXPECT_CALL(mockAls, begin()) +// .WillOnce(::testing::Return(0)); +// EXPECT_CALL(mockAls, autoRange()) +// .WillOnce(::testing::Return(0)); + +// bool readState = false; +// EXPECT_CALL(mockAls, getValue(::testing::_, ::testing::Ref(readState))) +// .WillRepeatedly(::testing::DoAll( +// ::testing::SetArgReferee<1>(false), +// ::testing::Return(100.0f) +// )); + +// String result = kestrel.getData(0); +// EXPECT_GT(result.length(), 0); // Check that result is not empty +// } +// } + +// // Test time validity check in getTime +// TEST_F(KestrelTest, GetTimeWithInvalidTime) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Setup timeProvider to report invalid time +// EXPECT_CALL(mockTimeProvider, isValid()) +// .WillRepeatedly(::testing::Return(false)); + +// // Time value should be 0 for invalid time +// time_t result = kestrel.getTime(); +// EXPECT_EQ(result, 0); +// } + +// // Test getTimeString with valid/invalid times +// TEST_F(KestrelTest, GetTimeStringValidation) { +// { +// // Test valid time +// Kestrel kestrel = createFullyMockedKestrel(); + +// EXPECT_CALL(mockTimeProvider, isValid()) +// .WillRepeatedly(::testing::Return(true)); +// EXPECT_CALL(mockTimeProvider, now()) +// .WillOnce(::testing::Return(1585699200)); + +// String result = kestrel.getTimeString(); +// EXPECT_EQ(result, "1585699200"); +// } - EXPECT_CALL(mockRtc, setAlarm(600, true)) - .Times(1); +// { +// // Test invalid time +// Kestrel kestrel = createFullyMockedKestrel(); + +// EXPECT_CALL(mockTimeProvider, isValid()) +// .WillRepeatedly(::testing::Return(false)); + +// String result = kestrel.getTimeString(); +// EXPECT_EQ(result, "null"); +// } +// } + +// // Test sdInserted with different card states +// TEST_F(KestrelTest, SDInsertedStates) { +// { +// // Test card inserted +// Kestrel kestrel = createFullyMockedKestrel(); + +// EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); +// EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)) +// .WillOnce(::testing::Return(0)); // LOW = inserted + +// bool result = kestrel.sdInserted(); +// EXPECT_TRUE(result); +// } - bool result = kestrel.startTimer(600); - EXPECT_FALSE(result); -} - -// waitUntilTimerDone() Tests -TEST_F(KestrelTest, WaitUntilTimerDoneUnitialized) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Without calling startTimer(), logPeriod should be 0 - bool result = kestrel.waitUntilTimerDone(); - EXPECT_FALSE(result); -} - -TEST_F(KestrelTest, WaitUntilTimerDoneSuccess) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Start a timer first - kestrel.startTimer(300); - - // Set up GPIO to show clock interrupt triggered - EXPECT_CALL(mockGpio, digitalRead(Pins::Clock_INT)) - .WillOnce(::testing::Return(0)); // LOW = interrupt triggered - - bool result = kestrel.waitUntilTimerDone(); - EXPECT_TRUE(result); -} - -// getSensorPortString() Tests -TEST_F(KestrelTest, GetPosLatValidLocation) { - Kestrel kestrel = createFullyMockedKestrel(); - - // First call should not trigger GPS update - String result = kestrel.getPosLat(); - EXPECT_EQ(result, "null"); // No location set yet - - // Now set a location through updateLocation - kestrel.updateLocation(true); // This would set latitude - // Test requires internal state which is hard to verify -} - -// testForBat() Tests -TEST_F(KestrelTest, TestForBatValidBattery) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up voltage reading to indicate valid battery - EXPECT_CALL(mockCsaAlpha, getBusVoltage(IChannel::CSA_CH1, false)) - .WillOnce(::testing::Return(3.7f)); // Valid battery voltage - - bool result = kestrel.testForBat(); - EXPECT_TRUE(result); -} - -TEST_F(KestrelTest, TestForBatLowBattery) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up voltage reading to indicate low/no battery - EXPECT_CALL(mockCsaAlpha, getBusVoltage(IChannel::CSA_CH1, false)) - .WillOnce(::testing::Return(1.5f)); // Low voltage - - bool result = kestrel.testForBat(); - EXPECT_FALSE(result); -} - -// getMessageID() Tests -TEST_F(KestrelTest, GetMessageIDValidTime) { - Kestrel kestrel = createFullyMockedKestrel(); - - EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(1000000)); - EXPECT_CALL(mockTimeProvider, millis()) - .WillOnce(::testing::Return(5000)); // 5 seconds - - unsigned long id = kestrel.getMessageID(); - EXPECT_EQ(id, 0); // 1000000 % 5 = 0 -} - -TEST_F(KestrelTest, GetMessageIDInvalidTime) { - Kestrel kestrel = createFullyMockedKestrel(); - - // When time is invalid (0), should use random number - EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(0)); - - // Can't really test the random value, just ensure it doesn't crash - unsigned long id = kestrel.getMessageID(); - // Any value is acceptable for random -} - -// More comprehensive begin() test -TEST_F(KestrelTest, BeginFullInitializationSequence) { - Kestrel kestrel = createFullyMockedKestrel(); - bool criticalFault = false; - bool fault = false; - - // Complete begin sequence - testing::InSequence seq; - - // System events - EXPECT_CALL(mockSystem, on(IEventType::TIME_CHANGED, ::testing::_)); - EXPECT_CALL(mockSystem, on(IEventType::OUT_OF_MEMORY, ::testing::_)); - - // I2C initialization - EXPECT_CALL(mockWire, isEnabled()).WillOnce(::testing::Return(false)); - EXPECT_CALL(mockWire, begin()); - EXPECT_CALL(mockWire, setClock(400000)); - - // IO Expanders - EXPECT_CALL(mockIoOB, begin()).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockIoTalon, begin()).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockIoTalon, safeMode(IIOExpander::SAFEOFF)); - - // CSA initialization - EXPECT_CALL(mockCsaAlpha, begin()).WillOnce(::testing::Return(true)); - EXPECT_CALL(mockCsaBeta, begin()).WillOnce(::testing::Return(true)); - EXPECT_CALL(mockCsaAlpha, setFrequency(IFrequency::CSA_SPS_64)); - - // LED driver - EXPECT_CALL(mockLed, begin()).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockLed, setOutputMode(ILed::IOutputMode::OpenDrain)); - EXPECT_CALL(mockLed, setGroupMode(ILed::IGroupMode::Blink)); - EXPECT_CALL(mockLed, setOutputArray(ILed::IPortState::Off)); - - // Serial port - EXPECT_CALL(mockSerialSdi12, begin(1200, 0b00000000)); - - // RTC - EXPECT_CALL(mockRtc, begin(true)).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockRtc, enableAlarm(false, 0)); - EXPECT_CALL(mockRtc, enableAlarm(false, 1)); - EXPECT_CALL(mockRtc, setMode(IRtc::Mode::Normal)); - - // GPS - EXPECT_CALL(mockGps, begin()).WillOnce(::testing::Return(true)); - EXPECT_CALL(mockGps, setI2COutput(GPS_COM_TYPE_UBX)); - EXPECT_CALL(mockGps, setNavigationFrequency(1)); - EXPECT_CALL(mockGps, setAutoPVT(false)); - - String result = kestrel.begin(0, criticalFault, fault); - - EXPECT_EQ(result, ""); - EXPECT_FALSE(criticalFault); -} - -// Additional edge case tests -TEST_F(KestrelTest, EnablePowerAndDataAllPorts) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test enabling all ports - for(int i = 1; i <= 5; i++) { - kestrel.enablePower(i, true); - kestrel.enableData(i, true); - } - - // Test disabling all ports - kestrel.disablePowerAll(); - kestrel.disableDataAll(); -} - -TEST_F(KestrelTest, GetDataWithSensorsEnabled) { - Kestrel kestrel = createFullyMockedKestrel(true); // Enable sensors - - // Set up ALS expectations - EXPECT_CALL(mockAls, begin()).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockAls, autoRange()).WillOnce(::testing::Return(1)); - - bool readState = false; - EXPECT_CALL(mockAls, getValue(::testing::_, ::testing::Ref(readState))) - .WillRepeatedly(::testing::DoAll( - ::testing::SetArgReferee<1>(false), - ::testing::Return(100.0f) - )); - - String result = kestrel.getData(0); - EXPECT_NE(result.indexOf("\"ALS\""), -1); // Should contain ALS data -} - -TEST_F(KestrelTest, SelfDiagnosticLevel5Memory) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test low memory warning (75% used) - EXPECT_CALL(mockSystem, freeMemory()) - .WillOnce(::testing::Return(20000)); // Below 46800 threshold - - String result = kestrel.selfDiagnostic(5, 0); - // Would need to check if RAM_LOW error was thrown -} - -// Add tests for sleep modes -TEST_F(KestrelTest, SleepPerformanceMode) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Performance mode should return immediately - int result = kestrel.sleep(); - EXPECT_EQ(result, 0); -} - -TEST_F(KestrelTest, WakePerformanceMode) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Performance mode should return immediately - int result = kestrel.wake(); - EXPECT_EQ(result, 0); -} - -// Tests for zero accelerometer -TEST_F(KestrelTest, ZeroAccelReset) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test reset mode - bool result = kestrel.zeroAccel(true); - EXPECT_TRUE(result); -} - -TEST_F(KestrelTest, ZeroAccelCalibrate) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up accelerometer expectations - EXPECT_CALL(mockAccel, begin()).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockAccel, getAccel(2, ::testing::_)).WillOnce(::testing::Return(1.0f)); - - bool result = kestrel.zeroAccel(false); - EXPECT_FALSE(result); -} - -// Test I2C enable/disable functions -TEST_F(KestrelTest, EnableI2COperations) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test OB I2C - EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockGpio, pinMode(Pins::I2C_OB_EN, IPinMode::OUTPUT)); - EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_OB_EN, true)); - - bool prevState = kestrel.enableI2C_OB(true); - EXPECT_FALSE(prevState); - - // Test Global I2C - EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)).WillOnce(::testing::Return(1)); - EXPECT_CALL(mockGpio, pinMode(Pins::I2C_GLOBAL_EN, IPinMode::OUTPUT)); - EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_GLOBAL_EN, false)); - - prevState = kestrel.enableI2C_Global(false); - EXPECT_TRUE(prevState); -} - -// Test SD card operations -TEST_F(KestrelTest, SDCardOperations) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test SD enable - EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_EN)).WillOnce(::testing::Return(0)); - EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_EN, ::testing::_)); - EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::SD_EN, 1)); - - bool prevState = kestrel.enableSD(true); - EXPECT_FALSE(prevState); - - // Test SD card detection - EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); - EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)).WillOnce(::testing::Return(0)); - - bool inserted = kestrel.sdInserted(); - EXPECT_TRUE(inserted); -} - -// Test aux power control -TEST_F(KestrelTest, AuxPowerControl) { - Kestrel kestrel = createFullyMockedKestrel(); - - EXPECT_CALL(mockIoOB, digitalRead(PinsOB::AUX_EN)).WillOnce(::testing::Return(1)); - EXPECT_CALL(mockIoOB, pinMode(PinsOB::AUX_EN, ::testing::_)); - EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::AUX_EN, false)); - - bool prevState = kestrel.enableAuxPower(false); - EXPECT_TRUE(prevState); -} - -// Test total errors function -TEST_F(KestrelTest, TotalErrors) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Since we can't directly set errors, we can only test the getter - uint8_t total = kestrel.totalErrors(); - // Default should be 0 + 0 - EXPECT_EQ(total, 0); -} - -// Test fault detection -TEST_F(KestrelTest, GetFaultValidPort) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test fault present (pin LOW) - EXPECT_CALL(mockIoTalon, digitalRead(2)).WillOnce(::testing::Return(0)); // FAULT[0] for port 1 - - bool fault = kestrel.getFault(1); - EXPECT_TRUE(fault); - - // Test no fault (pin HIGH) - EXPECT_CALL(mockIoTalon, digitalRead(2)).WillOnce(::testing::Return(1)); - - fault = kestrel.getFault(1); - EXPECT_FALSE(fault); -} - -// Test statLED function -TEST_F(KestrelTest, StatLEDControl) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test LED on (state = true) - bool result = kestrel.statLED(true); - EXPECT_FALSE(result); // Always returns false - - // Test LED off (state = false) - result = kestrel.statLED(false); - EXPECT_FALSE(result); // Always returns false -} - -// Test connectToCell function -TEST_F(KestrelTest, ConnectToCellSuccess) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test successful connection - EXPECT_CALL(mockCloud, connect()).Times(1); - EXPECT_CALL(mockCloud, connected()) - .WillRepeatedly(::testing::Return(true)); - - bool result = kestrel.connectToCell(); - EXPECT_TRUE(result); -} - -TEST_F(KestrelTest, ConnectToCellFailure) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test failed connection (timeout) - EXPECT_CALL(mockCloud, connect()).Times(1); - EXPECT_CALL(mockCloud, connected()) - .WillRepeatedly(::testing::Return(false)); - - bool result = kestrel.connectToCell(); - EXPECT_FALSE(result); -} - -// Test releaseWDT function -TEST_F(KestrelTest, ReleaseWDTToggle) { - Kestrel kestrel = createFullyMockedKestrel(); - - // First call should return false (default) and set to true - bool result = kestrel.releaseWDT(); - EXPECT_FALSE(result); - - // Second call should return true (previous state) - result = kestrel.releaseWDT(); - EXPECT_TRUE(result); -} - -// Test syncTime function - limited test due to complexity -TEST_F(KestrelTest, SyncTimeBasicOperation) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up mock behavior for minimal sync time test - EXPECT_CALL(mockRtc, getTimeUnix()) - .WillOnce(::testing::Return(1585699200)); - - EXPECT_CALL(mockTimeProvider, isValid()) - .WillRepeatedly(::testing::Return(true)); - - EXPECT_CALL(mockTimeProvider, now()) - .WillRepeatedly(::testing::Return(1585699200)); - - uint8_t source = kestrel.syncTime(false); - // Hard to test specific source without more detailed setup - EXPECT_LE(source, TimeSource::NONE); -} - -// Test configTalonSense function -TEST_F(KestrelTest, ConfigTalonSense) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test CSA Beta configuration - EXPECT_CALL(mockCsaBeta, setCurrentDirection(IChannel::CSA_CH4, 0)) - .Times(1); - EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH1, false)) - .Times(1); - EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH2, false)) - .Times(1); - EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH3, false)) - .Times(1); - EXPECT_CALL(mockCsaBeta, enableChannel(IChannel::CSA_CH4, true)) - .Times(1); - - bool result = kestrel.configTalonSense(); - EXPECT_FALSE(result); -} - -// Test the updateTime function -TEST_F(KestrelTest, UpdateTimeSuccess) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up time provider expectations - EXPECT_CALL(mockTimeProvider, year()) - .WillOnce(::testing::Return(2023)); - EXPECT_CALL(mockTimeProvider, month()) - .WillOnce(::testing::Return(5)); - EXPECT_CALL(mockTimeProvider, day()) - .WillOnce(::testing::Return(15)); - EXPECT_CALL(mockTimeProvider, hour()) - .WillOnce(::testing::Return(10)); - EXPECT_CALL(mockTimeProvider, minute()) - .WillOnce(::testing::Return(30)); - EXPECT_CALL(mockTimeProvider, second()) - .WillOnce(::testing::Return(45)); - - uint8_t source = kestrel.updateTime(); - // Hard to test specific source in isolated test - EXPECT_LE(source, TimeSource::NONE); -} - -// Test getData for metadata gathering -TEST_F(KestrelTest, GetMetadataBasicInfo) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up time provider for metadata timestamp - EXPECT_CALL(mockTimeProvider, isValid()) - .WillRepeatedly(::testing::Return(true)); - EXPECT_CALL(mockTimeProvider, now()) - .WillRepeatedly(::testing::Return(1585699200)); - - // Set up RTC UUID for metadata - EXPECT_CALL(mockRtc, getUUIDString()) - .WillOnce(::testing::Return("test-uuid-1234")); - - String result = kestrel.getMetadata(); - EXPECT_NE(result.indexOf("\"RTC UUID\":\"test-uuid-1234\""), -1); - EXPECT_NE(result.indexOf("\"Time\":1585699200"), -1); -} - -// Test selfDiagnostic at different levels -TEST_F(KestrelTest, SelfDiagnosticLevel2) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test with level 2 diagnostics - should include accelerometer offsets - EXPECT_CALL(mockAccel, begin()) - .WillOnce(::testing::Return(0)); - - // Set up expectations for RTC register reads - EXPECT_CALL(mockRtc, readByte(0)) - .WillOnce(::testing::Return(0x80)); - EXPECT_CALL(mockRtc, readByte(3)) - .WillOnce(::testing::Return(0x38)); - EXPECT_CALL(mockRtc, readByte(7)) - .WillOnce(::testing::Return(0x12)); - EXPECT_CALL(mockRtc, readByte(8)) - .WillOnce(::testing::Return(0x34)); - EXPECT_CALL(mockRtc, readByte(0x0D)) - .WillOnce(::testing::Return(0x56)); - EXPECT_CALL(mockRtc, readByte(0x14)) - .WillOnce(::testing::Return(0x78)); - - String result = kestrel.selfDiagnostic(2, 0); - EXPECT_NE(result.indexOf("\"RTC_Config\""), -1); -} - -TEST_F(KestrelTest, SelfDiagnosticLevel3) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test RTC oscillator validation - EXPECT_CALL(mockWire, beginTransmission(0x6F)) - .Times(1); - EXPECT_CALL(mockWire, endTransmission()) - .WillOnce(::testing::Return(0)); - - EXPECT_CALL(mockRtc, getTimeUnix()) - .WillOnce(::testing::Return(1585699200)) - .WillOnce(::testing::Return(1585699201)); // Should increment - - EXPECT_CALL(mockTimeProvider, delay(1200)) - .Times(1); - - String result = kestrel.selfDiagnostic(3, 0); - // Hard to verify all content in a simplified test -} - -TEST_F(KestrelTest, SelfDiagnosticLevel4) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test CSA setup and port readings - EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::CSA_EN, 1)) - .Times(1); - - EXPECT_CALL(mockCsaAlpha, begin()) - .WillOnce(::testing::Return(true)); - EXPECT_CALL(mockCsaBeta, begin()) - .WillOnce(::testing::Return(true)); - - // Setup for bus voltage readings - simplified - bool err = false; - EXPECT_CALL(mockCsaAlpha, getBusVoltage(::testing::_, true, ::testing::_)) - .WillRepeatedly(::testing::DoAll( - ::testing::SetArgReferee<2>(false), - ::testing::Return(3.3f) - )); - - EXPECT_CALL(mockCsaBeta, getBusVoltage(::testing::_, true, ::testing::_)) - .WillRepeatedly(::testing::DoAll( - ::testing::SetArgReferee<2>(false), - ::testing::Return(5.0f) - )); - - String result = kestrel.selfDiagnostic(4, 0); - EXPECT_NE(result.indexOf("\"PORT_V\""), -1); -} - -// Test GPS position getters -TEST_F(KestrelTest, PositionGetters) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Manually set position data - kestrel.updateLocation(true); - - // Test position getters - String latitude = kestrel.getPosLat(); - String longitude = kestrel.getPosLong(); - String altitude = kestrel.getPosAlt(); - time_t posTime = kestrel.getPosTime(); - String posTimeStr = kestrel.getPosTimeString(); - - // We can't easily verify values in isolation without injection ability -} - -// Test error handling and reporting -TEST_F(KestrelTest, GetErrorsFormatting) { - Kestrel kestrel = createFullyMockedKestrel(); - - EXPECT_CALL(mockTimeProvider, now()) - .WillRepeatedly(::testing::Return(1585699200)); - - // There are no errors yet, so this should return an empty string - String errors = kestrel.getErrors(); - // We'd need to inject errors to test this fully -} - -// Test enableI2C_External with proper dependencies -TEST_F(KestrelTest, EnableI2CExternalIntegration) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test setup for external I2C enable - EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_GLOBAL_EN)) - .WillOnce(::testing::Return(1)); - EXPECT_CALL(mockGpio, digitalRead(Pins::I2C_OB_EN)) - .WillOnce(::testing::Return(0)); - - EXPECT_CALL(mockIoOB, digitalRead(PinsOB::I2C_EXT_EN)) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockIoOB, pinMode(PinsOB::I2C_EXT_EN, ::testing::_)); - EXPECT_CALL(mockIoOB, digitalWrite(PinsOB::I2C_EXT_EN, true)); - - bool result = kestrel.enableI2C_External(true); - EXPECT_FALSE(result); -} - -// Enhanced sleep tests with mode configuration -TEST_F(KestrelTest, SleepBalancedMode) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Setup for Balanced mode sleep - // We would need to set powerSaveMode, but it's private - // This is a limitation of the current design - - // Set up mock for Clock_INT pin check - EXPECT_CALL(mockGpio, digitalRead(Pins::Clock_INT)) - .WillOnce(::testing::Return(1)); // Not triggered yet - - // We have limited ability to test internal state changes - int result = kestrel.sleep(); - // Can't verify result without knowing the mode -} - -// Test the GPS wake path in the wake function -TEST_F(KestrelTest, WakeWithGPSUpdate) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Setup for GPS wake test - // We would need to set powerSaveMode and posTime, but they're private - - // Set up basic I2C management for wake - EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_GLOBAL_EN, true)); - EXPECT_CALL(mockGpio, digitalWrite(Pins::I2C_OB_EN, false)); - - int result = kestrel.wake(); - // Can't verify result without knowing the mode -} - -// Test critical fault detection in selfDiagnostic -TEST_F(KestrelTest, SelfDiagnosticMemoryCritical) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test critical memory condition (90% used) - EXPECT_CALL(mockSystem, freeMemory()) - .WillOnce(::testing::Return(10000)); // Below 15600 threshold - - String result = kestrel.selfDiagnostic(5, 0); - // Would need to check if RAM_CRITICAL error was thrown -} - -// Test WDT feeding when there's a critical fault -TEST_F(KestrelTest, FeedWDTWithCriticalFault) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Set up a critical fault condition - // We'd need a way to set criticalFault to true - - // For now, we can only test the normal path - bool result = kestrel.feedWDT(); - EXPECT_TRUE(result); -} - -// Test accelerometer initialization fallback -TEST_F(KestrelTest, BeginAccelerometerFallback) { - Kestrel kestrel = createFullyMockedKestrel(); - bool criticalFault = false; - bool fault = false; - - // Set up MXC6655 to fail and BMA456 to succeed - EXPECT_CALL(mockAccel, begin()) - .WillOnce(::testing::Return(-1)); // Fail - - // We'd need a way to test the BMA456 fallback - - String result = kestrel.begin(0, criticalFault, fault); - // Hard to test fallback mechanism in isolation -} - -// Test the entire diagnostic sequence -TEST_F(KestrelTest, FullDiagnosticSequence) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test with all diagnostic levels - for (int level = 0; level <= 5; level++) { - String result = kestrel.selfDiagnostic(level, 0); - EXPECT_GT(result.length(), 0); // Check that result is not empty - } -} - -// Test getData with different reportSensors settings -TEST_F(KestrelTest, GetDataWithReportSensorsToggle) { - // Test with sensors disabled - { - Kestrel kestrel = createFullyMockedKestrel(false); - String result = kestrel.getData(0); - EXPECT_GT(result.length(), 0); // Check that result is not empty - } - - // Test with sensors enabled - { - Kestrel kestrel = createFullyMockedKestrel(true); - - EXPECT_CALL(mockAls, begin()) - .WillOnce(::testing::Return(0)); - EXPECT_CALL(mockAls, autoRange()) - .WillOnce(::testing::Return(0)); - - bool readState = false; - EXPECT_CALL(mockAls, getValue(::testing::_, ::testing::Ref(readState))) - .WillRepeatedly(::testing::DoAll( - ::testing::SetArgReferee<1>(false), - ::testing::Return(100.0f) - )); - - String result = kestrel.getData(0); - EXPECT_GT(result.length(), 0); // Check that result is not empty - } -} - -// Test time validity check in getTime -TEST_F(KestrelTest, GetTimeWithInvalidTime) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Setup timeProvider to report invalid time - EXPECT_CALL(mockTimeProvider, isValid()) - .WillRepeatedly(::testing::Return(false)); - - // Time value should be 0 for invalid time - time_t result = kestrel.getTime(); - EXPECT_EQ(result, 0); -} - -// Test getTimeString with valid/invalid times -TEST_F(KestrelTest, GetTimeStringValidation) { - { - // Test valid time - Kestrel kestrel = createFullyMockedKestrel(); - - EXPECT_CALL(mockTimeProvider, isValid()) - .WillRepeatedly(::testing::Return(true)); - EXPECT_CALL(mockTimeProvider, now()) - .WillOnce(::testing::Return(1585699200)); - - String result = kestrel.getTimeString(); - EXPECT_EQ(result, "1585699200"); - } - - { - // Test invalid time - Kestrel kestrel = createFullyMockedKestrel(); +// { +// // Test card not inserted +// Kestrel kestrel = createFullyMockedKestrel(); - EXPECT_CALL(mockTimeProvider, isValid()) - .WillRepeatedly(::testing::Return(false)); +// EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); +// EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)) +// .WillOnce(::testing::Return(1)); // HIGH = not inserted - String result = kestrel.getTimeString(); - EXPECT_EQ(result, "null"); - } -} - -// Test sdInserted with different card states -TEST_F(KestrelTest, SDInsertedStates) { - { - // Test card inserted - Kestrel kestrel = createFullyMockedKestrel(); - - EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); - EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)) - .WillOnce(::testing::Return(0)); // LOW = inserted - - bool result = kestrel.sdInserted(); - EXPECT_TRUE(result); - } - - { - // Test card not inserted - Kestrel kestrel = createFullyMockedKestrel(); - - EXPECT_CALL(mockIoOB, pinMode(PinsOB::SD_CD, ::testing::_)); - EXPECT_CALL(mockIoOB, digitalRead(PinsOB::SD_CD)) - .WillOnce(::testing::Return(1)); // HIGH = not inserted - - bool result = kestrel.sdInserted(); - EXPECT_FALSE(result); - } -} - -// Test setDirection function -TEST_F(KestrelTest, SetDirectionValidPort) { - Kestrel kestrel = createFullyMockedKestrel(); - - // Test setting direction for port 1 - EXPECT_CALL(mockIoTalon, pinMode(0, ::testing::_)); // SEL[0] for port 1 - EXPECT_CALL(mockIoTalon, digitalWrite(0, true)); - - bool result = kestrel.setDirection(1, true); - EXPECT_FALSE(result); // Always returns false -} - -// Final integration test -TEST_F(KestrelTest, CompleteLifecycleTest) { - Kestrel kestrel = createFullyMockedKestrel(); - bool criticalFault = false; - bool fault = false; - - // Set up minimal begin expectations - EXPECT_CALL(mockSystem, on(::testing::_, ::testing::_)) - .Times(::testing::AtLeast(1)); - EXPECT_CALL(mockWire, isEnabled()) - .WillRepeatedly(::testing::Return(false)); - EXPECT_CALL(mockWire, begin()) - .Times(::testing::AtLeast(1)); - - // Begin the Kestrel - kestrel.begin(0, criticalFault, fault); - - // Test timer start/wait cycle - kestrel.startTimer(10); // Short period for test - bool timerResult = kestrel.waitUntilTimerDone(); - - // Test sleep and wake cycle - kestrel.sleep(); - kestrel.wake(); - - // Test WDT feeding - bool wdtResult = kestrel.feedWDT(); - - // Test data collection - String data = kestrel.getData(0); - String metadata = kestrel.getMetadata(); - String diagnostic = kestrel.selfDiagnostic(3, 0); - String errors = kestrel.getErrors(); - - // Overall lifecycle test is primarily checking for crashes -} -// Verify Kestrel begin sets criticalFault true if ioOb.begin() fails -// Verify Kestrel begin sets criticalFault true if ioTalon.begin() fails -// Verify Kestrel begin sets criticalFault true if csaAlpha.begin() fails -// Verify Kestrel begin sets criticalFault true if csaBeta.begin() fails -// Verify Kestrel begin sets criticalFault true if led.begin() fails -// Verify Kestrel Begin sets led functions if init is not done -// Verify Kestrel begin sets criticalFault true if rtc.begin() fails -// Verify Kestel begin sets criticalFault true if gps.begin() fails -// Verify Kestrel begin sets initDone true if init is done - -// Kestrel getErrors tests -// Kestrel getData tests -// Kestrel getMetadata tests -// Kestrel selfDiagnostic tests -// Kestrel updateLocation tests -// Kestrel connectToCell tests -// Kestrel enablePower tests -// Kestrel enableData tests -// Kestrel setDirection tests -// Kestrel getFault tests -// Kestrel enableI2C_OB tests -// Kestrel enableI2C_Global tests -// Kestrel enableI2C_External tests -// Kestrel enableSD tests -// Kestrel sdInserted tests -// Kestrel enableAuxPower tests -// Kestrel getTime tests -// Kestrel syncTime tests -// Kestrel startTimer tests -// Kestrel waitUntilTimerDone tests -// Kestrel getTimeString tests -// Kestrel totalErrors tests -// Kestrel statLED tests -// Kestrel setIndicatorState tests -// Kestrel updateTime tests -// Kestrel feedWDT tests -// Kestrel releaseWDT tests -// Kestrel getPosLat tests -// Kestrel getPosLong tests -// Kestrel getPosAlt tests -// Kestrel getPosTime tests -// Kestrel getPosTimeString tests -// Kestrel configTalonSense tests -// Kestrel getMessageID tests -// Kestrel testForBat tests -// Kestrel zeroAccel tests \ No newline at end of file +// bool result = kestrel.sdInserted(); +// EXPECT_FALSE(result); +// } +// } + +// // Test setDirection function +// TEST_F(KestrelTest, SetDirectionValidPort) { +// Kestrel kestrel = createFullyMockedKestrel(); + +// // Test setting direction for port 1 +// EXPECT_CALL(mockIoTalon, pinMode(0, ::testing::_)); // SEL[0] for port 1 +// EXPECT_CALL(mockIoTalon, digitalWrite(0, true)); + +// bool result = kestrel.setDirection(1, true); +// EXPECT_FALSE(result); // Always returns false +// } + +// // Final integration test +// TEST_F(KestrelTest, CompleteLifecycleTest) { +// Kestrel kestrel = createFullyMockedKestrel(); +// bool criticalFault = false; +// bool fault = false; + +// // Set up minimal begin expectations +// EXPECT_CALL(mockSystem, on(::testing::_, ::testing::_)) +// .Times(::testing::AtLeast(1)); +// EXPECT_CALL(mockWire, isEnabled()) +// .WillRepeatedly(::testing::Return(false)); +// EXPECT_CALL(mockWire, begin()) +// .Times(::testing::AtLeast(1)); + +// // Begin the Kestrel +// kestrel.begin(0, criticalFault, fault); + +// // Test timer start/wait cycle +// kestrel.startTimer(10); // Short period for test +// bool timerResult = kestrel.waitUntilTimerDone(); + +// // Test sleep and wake cycle +// kestrel.sleep(); +// kestrel.wake(); + +// // Test WDT feeding +// bool wdtResult = kestrel.feedWDT(); + +// // Test data collection +// String data = kestrel.getData(0); +// String metadata = kestrel.getMetadata(); +// String diagnostic = kestrel.selfDiagnostic(3, 0); +// String errors = kestrel.getErrors(); + +// // Overall lifecycle test is primarily checking for crashes +// } +// // Verify Kestrel begin sets criticalFault true if ioOb.begin() fails +// // Verify Kestrel begin sets criticalFault true if ioTalon.begin() fails +// // Verify Kestrel begin sets criticalFault true if csaAlpha.begin() fails +// // Verify Kestrel begin sets criticalFault true if csaBeta.begin() fails +// // Verify Kestrel begin sets criticalFault true if led.begin() fails +// // Verify Kestrel Begin sets led functions if init is not done +// // Verify Kestrel begin sets criticalFault true if rtc.begin() fails +// // Verify Kestel begin sets criticalFault true if gps.begin() fails +// // Verify Kestrel begin sets initDone true if init is done + +// // Kestrel getErrors tests +// // Kestrel getData tests +// // Kestrel getMetadata tests +// // Kestrel selfDiagnostic tests +// // Kestrel updateLocation tests +// // Kestrel connectToCell tests +// // Kestrel enablePower tests +// // Kestrel enableData tests +// // Kestrel setDirection tests +// // Kestrel getFault tests +// // Kestrel enableI2C_OB tests +// // Kestrel enableI2C_Global tests +// // Kestrel enableI2C_External tests +// // Kestrel enableSD tests +// // Kestrel sdInserted tests +// // Kestrel enableAuxPower tests +// // Kestrel getTime tests +// // Kestrel syncTime tests +// // Kestrel startTimer tests +// // Kestrel waitUntilTimerDone tests +// // Kestrel getTimeString tests +// // Kestrel totalErrors tests +// // Kestrel statLED tests +// // Kestrel setIndicatorState tests +// // Kestrel updateTime tests +// // Kestrel feedWDT tests +// // Kestrel releaseWDT tests +// // Kestrel getPosLat tests +// // Kestrel getPosLong tests +// // Kestrel getPosAlt tests +// // Kestrel getPosTime tests +// // Kestrel getPosTimeString tests +// // Kestrel configTalonSense tests +// // Kestrel getMessageID tests +// // Kestrel testForBat tests +// // Kestrel zeroAccel tests \ No newline at end of file From ab579460c97912cce95a4bf6359946f52ac01644 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Thu, 8 May 2025 16:17:38 -0500 Subject: [PATCH 38/48] resolving more merge conflicts --- test/mocks/Particle.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/mocks/Particle.h b/test/mocks/Particle.h index 6476d76..1cc9e41 100644 --- a/test/mocks/Particle.h +++ b/test/mocks/Particle.h @@ -311,7 +311,6 @@ class String { if (found == nullptr) return -1; return found - _buffer; } -<<<<<<< HEAD // Add endsWith method bool endsWith(const String &suffix) const { @@ -328,8 +327,6 @@ class String { return (strcmp(_buffer + (_length - suffix_len), suffix) == 0); } -======= ->>>>>>> master void toCharArray(char *buf, unsigned int bufsize, unsigned int index = 0) const { if (buf == nullptr || bufsize == 0) { @@ -476,7 +473,6 @@ inline StringSumHelper operator + (const String &lhs, int num) { return result; } -<<<<<<< HEAD // Mock EEPROM class class EEPROMClass { public: @@ -525,6 +521,3 @@ inline void __set_FPSCR(int value) { } #endif // MOCK_PARTICLE_H -======= -#endif // MOCK_PARTICLE_H ->>>>>>> master From c390552c2863c9d5a2084a76b208ecb7bcb792b7 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 07:56:56 -0500 Subject: [PATCH 39/48] changed last gemsiot reference --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 9f391f0..270b1b9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -130,7 +130,7 @@ url = https://github.com/google/googletest.git [submodule "lib/FlightControl-platform-dependencies"] path = lib/FlightControl-platform-dependencies - url = https://github.com/gemsiot/FlightControl-platform-dependencies.git + url = https://github.com/RTGS-Lab/FlightControl-platform-dependencies.git [submodule "lib/FlightControl-hardware-dependencies"] path = lib/FlightControl-hardware-dependencies - url = https://github.com/gemsiot/FlightControl-hardware-dependencies + url = https://github.com/RTGS-Lab/FlightControl-hardware-dependencies From 558834c23e8d57c79032a852dbed4929c09ca8ae Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:04:01 -0500 Subject: [PATCH 40/48] changed VEML reference to master --- lib/VEML3328 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/VEML3328 b/lib/VEML3328 index 4bb84e7..39c1cc8 160000 --- a/lib/VEML3328 +++ b/lib/VEML3328 @@ -1 +1 @@ -Subproject commit 4bb84e70e4e2fb2abf3df0b3a60a7b32284b6891 +Subproject commit 39c1cc8a65f30284c2c7978458213eeb06d68921 From d11e2581ca47ebcb0be5f4d03bdaa5bfbf69c0f0 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:07:44 -0500 Subject: [PATCH 41/48] reference PCAL master --- lib/PCAL9535A_Library | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PCAL9535A_Library b/lib/PCAL9535A_Library index 708272b..a18f31f 160000 --- a/lib/PCAL9535A_Library +++ b/lib/PCAL9535A_Library @@ -1 +1 @@ -Subproject commit 708272b194d87050cf711e6d51ccb556d53dbdb2 +Subproject commit a18f31f866447159b6ac7189299f4073dc37a625 From 5681641b023b7eb849e74fd2bdc73c42bd339d62 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:09:52 -0500 Subject: [PATCH 42/48] referenced MXC to master --- lib/MXC6655 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/MXC6655 b/lib/MXC6655 index 593307d..9cbb9c4 160000 --- a/lib/MXC6655 +++ b/lib/MXC6655 @@ -1 +1 @@ -Subproject commit 593307d852c3f1e32d37ef8d7b9c300197b21c0c +Subproject commit 9cbb9c4d15cade31f3b72a28a58dfe9590e9f21f From 966deb3d7ab08b046d965edb80864da61b6c5450 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:19:08 -0500 Subject: [PATCH 43/48] referenced platform dependencies to master --- lib/FlightControl-platform-dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FlightControl-platform-dependencies b/lib/FlightControl-platform-dependencies index 8915df3..8844b22 160000 --- a/lib/FlightControl-platform-dependencies +++ b/lib/FlightControl-platform-dependencies @@ -1 +1 @@ -Subproject commit 8915df3abdc4a7d80989707fa56e67ba361b57ce +Subproject commit 8844b2280ec01d1398ae6ddc4783f2091f8ff210 From e2822a487a1b78ac04a95da1f6d4b14181a68acb Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:27:11 -0500 Subject: [PATCH 44/48] updated hardware deps to reference master --- lib/FlightControl-hardware-dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FlightControl-hardware-dependencies b/lib/FlightControl-hardware-dependencies index ae0397b..71518ce 160000 --- a/lib/FlightControl-hardware-dependencies +++ b/lib/FlightControl-hardware-dependencies @@ -1 +1 @@ -Subproject commit ae0397b1c07e7c15cdecbd83cfe1c535986fab2a +Subproject commit 71518ce14301a13934eb9d9159ca375887c4caaa From 9d74883f22f5a96a1d824d575d0ffecf831791fe Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:31:14 -0500 Subject: [PATCH 45/48] updated mcp to master --- lib/Driver_-_MCP79412 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Driver_-_MCP79412 b/lib/Driver_-_MCP79412 index 2e3fe8b..45abb14 160000 --- a/lib/Driver_-_MCP79412 +++ b/lib/Driver_-_MCP79412 @@ -1 +1 @@ -Subproject commit 2e3fe8b0558e0de9168f6f186b157e06f9e26668 +Subproject commit 45abb14ae26e4d28e16e6117524507e2a6d83d6f From 56cacde2a49bf35b26cee70faa7c07c202b1d7bc Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:34:02 -0500 Subject: [PATCH 46/48] moved kestrel to master --- lib/Driver_-_Kestrel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Driver_-_Kestrel b/lib/Driver_-_Kestrel index b78cf75..6ee4e49 160000 --- a/lib/Driver_-_Kestrel +++ b/lib/Driver_-_Kestrel @@ -1 +1 @@ -Subproject commit b78cf75150050f32f801e290e5145b832525ebcd +Subproject commit 6ee4e491ed35d5edfee389d9641acc1ffe85bead From f3c594d00c79e1e3221490f28a54fae4568d1ae3 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 08:37:19 -0500 Subject: [PATCH 47/48] updated LI710 to master --- lib/Driver_-_Li710 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Driver_-_Li710 b/lib/Driver_-_Li710 index d5616c6..d069e84 160000 --- a/lib/Driver_-_Li710 +++ b/lib/Driver_-_Li710 @@ -1 +1 @@ -Subproject commit d5616c60261d93517faf6d8b580ec932b9185ef2 +Subproject commit d069e84a7a34cfcd93b190f0911cd94ebe521e70 From 7e843c857c826e822c94e9756bdf3af3d5938cf2 Mon Sep 17 00:00:00 2001 From: Zach Radlicz Date: Fri, 9 May 2025 09:19:56 -0500 Subject: [PATCH 48/48] moved sensor submod to master --- lib/Driver_-_Sensor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Driver_-_Sensor b/lib/Driver_-_Sensor index afef963..f3cf454 160000 --- a/lib/Driver_-_Sensor +++ b/lib/Driver_-_Sensor @@ -1 +1 @@ -Subproject commit afef96338232e397656a907ce12338a4c9cad288 +Subproject commit f3cf4548da02ee763da53837507083c3843f9162