290 lines
7.4 KiB
C++
290 lines
7.4 KiB
C++
#include <unity.h>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include "../src/RocketController.h"
|
|
#include "../src/ArduinoInterface.h"
|
|
|
|
// Simple mock Arduino interface for testing
|
|
class MockArduinoInterface : public ArduinoInterface
|
|
{
|
|
private:
|
|
// Mock state
|
|
mutable uint32_t mock_millis = 0;
|
|
uint8_t mock_pin_states[20] = {0};
|
|
bool mock_tone_active = false;
|
|
uint16_t mock_tone_freq = 0;
|
|
char mock_lcd_line1[32] = "";
|
|
char mock_lcd_line2[32] = "";
|
|
|
|
// Mock button states
|
|
bool mock_arm_pressed = false;
|
|
bool mock_reset_pressed = false;
|
|
bool mock_launch_pressed = false;
|
|
|
|
public:
|
|
// Pin control
|
|
void digitalWrite(uint8_t pin, uint8_t state) override
|
|
{
|
|
mock_pin_states[pin] = state;
|
|
}
|
|
|
|
uint8_t digitalRead(uint8_t pin) const override
|
|
{
|
|
return mock_pin_states[pin];
|
|
}
|
|
|
|
void pinMode(uint8_t pin, uint8_t mode) override
|
|
{
|
|
(void)pin; // Suppress unused parameter warning
|
|
(void)mode; // Suppress unused parameter warning
|
|
// Mock implementation
|
|
}
|
|
|
|
// Time functions
|
|
uint32_t millis() const override
|
|
{
|
|
return mock_millis;
|
|
}
|
|
|
|
void delay(uint32_t ms) override
|
|
{
|
|
mock_millis += ms;
|
|
}
|
|
|
|
// Audio functions
|
|
void tone(uint8_t pin, uint16_t freq) override
|
|
{
|
|
(void)pin; // Suppress unused parameter warning
|
|
mock_tone_active = true;
|
|
mock_tone_freq = freq;
|
|
}
|
|
|
|
void tone(uint8_t pin, uint16_t freq, uint32_t duration) override
|
|
{
|
|
(void)pin; // Suppress unused parameter warning
|
|
(void)duration; // Suppress unused parameter warning
|
|
mock_tone_active = true;
|
|
mock_tone_freq = freq;
|
|
}
|
|
|
|
void noTone(uint8_t pin) override
|
|
{
|
|
(void)pin; // Suppress unused parameter warning
|
|
mock_tone_active = false;
|
|
mock_tone_freq = 0;
|
|
}
|
|
|
|
// LCD functions
|
|
void lcdClear() override
|
|
{
|
|
mock_lcd_line1[0] = '\0';
|
|
mock_lcd_line2[0] = '\0';
|
|
}
|
|
|
|
void lcdSetCursor(uint8_t col, uint8_t row) override
|
|
{
|
|
(void)col; // Suppress unused parameter warning
|
|
(void)row; // Suppress unused parameter warning
|
|
// Mock implementation
|
|
}
|
|
|
|
void lcdPrint(const char* text) override
|
|
{
|
|
if (strlen(mock_lcd_line1) == 0)
|
|
{
|
|
strncpy(mock_lcd_line1, text, 31);
|
|
}
|
|
else
|
|
{
|
|
strncpy(mock_lcd_line2, text, 31);
|
|
}
|
|
}
|
|
|
|
void lcdPrint(int number) override
|
|
{
|
|
if (strlen(mock_lcd_line1) == 0)
|
|
{
|
|
snprintf(mock_lcd_line1, 32, "%d", number);
|
|
}
|
|
else
|
|
{
|
|
snprintf(mock_lcd_line2, 32, "%d", number);
|
|
}
|
|
}
|
|
|
|
// Button debouncing
|
|
void updateDebouncers() override
|
|
{
|
|
// Mock implementation
|
|
}
|
|
|
|
bool isArmPressed() const override
|
|
{
|
|
return mock_arm_pressed;
|
|
}
|
|
|
|
bool isResetPressed() const override
|
|
{
|
|
return mock_reset_pressed;
|
|
}
|
|
|
|
bool isLaunchPressed() const override
|
|
{
|
|
return mock_launch_pressed;
|
|
}
|
|
|
|
// Test helper methods
|
|
void setMockTime(uint32_t time) { mock_millis = time; }
|
|
void advanceTime(uint32_t ms) { mock_millis += ms; }
|
|
void setArmPressed(bool pressed) { mock_arm_pressed = pressed; }
|
|
void setResetPressed(bool pressed) { mock_reset_pressed = pressed; }
|
|
void setLaunchPressed(bool pressed) { mock_launch_pressed = pressed; }
|
|
|
|
// State query methods
|
|
uint8_t getPinState(uint8_t pin) const { return mock_pin_states[pin]; }
|
|
bool isToneActive() const { return mock_tone_active; }
|
|
uint16_t getToneFreq() const { return mock_tone_freq; }
|
|
const char* getLCDLine1() const { return mock_lcd_line1; }
|
|
const char* getLCDLine2() const { return mock_lcd_line2; }
|
|
|
|
// Reset mock state
|
|
void reset()
|
|
{
|
|
mock_millis = 0;
|
|
mock_tone_active = false;
|
|
mock_tone_freq = 0;
|
|
mock_lcd_line1[0] = '\0';
|
|
mock_lcd_line2[0] = '\0';
|
|
mock_arm_pressed = false;
|
|
mock_reset_pressed = false;
|
|
mock_launch_pressed = false;
|
|
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
mock_pin_states[i] = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
// Test fixture
|
|
MockArduinoInterface* mockInterface;
|
|
RocketController* controller;
|
|
|
|
void setUp(void)
|
|
{
|
|
mockInterface = new MockArduinoInterface();
|
|
controller = new RocketController(mockInterface);
|
|
mockInterface->reset();
|
|
}
|
|
|
|
void tearDown(void)
|
|
{
|
|
delete controller;
|
|
delete mockInterface;
|
|
}
|
|
|
|
// Test 1: Basic Rocket Controller Creation and Initial State
|
|
void test_rocket_controller_initial_state(void)
|
|
{
|
|
// Test that controller starts in STARTUP state
|
|
TEST_ASSERT_EQUAL(State::STARTUP, controller->getState());
|
|
|
|
// Test that system is initially locked
|
|
TEST_ASSERT_TRUE(controller->isSystemLocked());
|
|
|
|
// Test that controller is not armed initially
|
|
TEST_ASSERT_FALSE(controller->isArmed());
|
|
|
|
// Test that controller is not launching initially
|
|
TEST_ASSERT_FALSE(controller->isLaunching());
|
|
}
|
|
|
|
// Test 2: State Transition from STARTUP to READY
|
|
void test_startup_to_ready_transition(void)
|
|
{
|
|
// Verify we start in STARTUP
|
|
TEST_ASSERT_EQUAL(State::STARTUP, controller->getState());
|
|
|
|
// Simulate startup completion by advancing time
|
|
mockInterface->setMockTime(RocketController::STARTUP_TOTAL_TIME_MS + 1000);
|
|
|
|
// Update the controller
|
|
controller->update(mockInterface->millis());
|
|
|
|
// Should transition to READY state
|
|
TEST_ASSERT_EQUAL(State::READY, controller->getState());
|
|
|
|
// System should no longer be locked
|
|
TEST_ASSERT_FALSE(controller->isSystemLocked());
|
|
}
|
|
|
|
// Test 3: Button Input Handling
|
|
void test_button_input_handling(void)
|
|
{
|
|
// Test ARM button state
|
|
mockInterface->setArmPressed(true);
|
|
TEST_ASSERT_TRUE(mockInterface->isArmPressed());
|
|
|
|
mockInterface->setArmPressed(false);
|
|
TEST_ASSERT_FALSE(mockInterface->isArmPressed());
|
|
|
|
// Test RESET button state
|
|
mockInterface->setResetPressed(true);
|
|
TEST_ASSERT_TRUE(mockInterface->isResetPressed());
|
|
|
|
// Test LAUNCH button state
|
|
mockInterface->setLaunchPressed(true);
|
|
TEST_ASSERT_TRUE(mockInterface->isLaunchPressed());
|
|
}
|
|
|
|
// Test 4: Manual State Management
|
|
void test_manual_state_management(void)
|
|
{
|
|
// Test that we can manually set states
|
|
controller->enter(State::READY);
|
|
TEST_ASSERT_EQUAL(State::READY, controller->getState());
|
|
|
|
// Test that system is unlocked in READY state
|
|
TEST_ASSERT_FALSE(controller->isSystemLocked());
|
|
|
|
// Test ARMED state
|
|
controller->enter(State::ARMED);
|
|
TEST_ASSERT_EQUAL(State::ARMED, controller->getState());
|
|
TEST_ASSERT_TRUE(controller->isArmed());
|
|
TEST_ASSERT_FALSE(controller->isLaunching());
|
|
|
|
// Test LAUNCHING state
|
|
controller->enter(State::LAUNCHING);
|
|
TEST_ASSERT_EQUAL(State::LAUNCHING, controller->getState());
|
|
TEST_ASSERT_TRUE(controller->isLaunching());
|
|
TEST_ASSERT_FALSE(controller->isArmed());
|
|
|
|
// Test return to READY
|
|
controller->enter(State::READY);
|
|
TEST_ASSERT_EQUAL(State::READY, controller->getState());
|
|
TEST_ASSERT_FALSE(controller->isArmed());
|
|
TEST_ASSERT_FALSE(controller->isLaunching());
|
|
}
|
|
|
|
// Main test runner
|
|
void RUN_UNITY_TESTS()
|
|
{
|
|
UNITY_BEGIN();
|
|
|
|
// Rocket Controller Tests
|
|
RUN_TEST(test_rocket_controller_initial_state);
|
|
RUN_TEST(test_startup_to_ready_transition);
|
|
RUN_TEST(test_button_input_handling);
|
|
RUN_TEST(test_manual_state_management);
|
|
|
|
UNITY_END();
|
|
}
|
|
|
|
// Main function for native testing
|
|
int main()
|
|
{
|
|
// Initialize Unity test framework
|
|
RUN_UNITY_TESTS();
|
|
return 0;
|
|
}
|