- Replaced the buggy, oldschool, plain pointer based list approach with more robust FreeRTOS linked list implementation for the listener API. Fixes #1049. - Added relevant test cases. Refs #1030.
560 lines
25 KiB
C
560 lines
25 KiB
C
#include "driver/gpio.h"
|
|
#include "unity.h"
|
|
#include "unity_test_utils.h"
|
|
#include "relay_chn.h" // Main header file for the relay_chn component
|
|
#include <esp_log.h>
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/task.h>
|
|
#include "sdkconfig.h" // For accessing CONFIG_* values
|
|
#include <string.h>
|
|
|
|
// Test GPIOs and channel IDs
|
|
// Please ensure these GPIOs are correct and suitable for your board.
|
|
// Two channels (4 GPIOs) are used as an example.
|
|
const gpio_num_t gpio_map[] = {GPIO_NUM_4, GPIO_NUM_5, GPIO_NUM_18, GPIO_NUM_19};
|
|
const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]);
|
|
// Assuming 2 GPIOs are used per channel
|
|
const uint8_t relay_chn_count = gpio_count / 2;
|
|
|
|
const uint32_t opposite_inertia_ms = CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS;
|
|
|
|
// Tolerant delay margin to ensure operations complete, especially after inertia.
|
|
const uint32_t test_delay_margin_ms = 50;
|
|
|
|
// --- Test Setup/Teardown Functions ---
|
|
void setUp(void) {
|
|
// Re-create the component before each test. relay_chn_create returns esp_err_t.
|
|
TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count));
|
|
}
|
|
|
|
void tearDown(void) {
|
|
relay_chn_destroy(); // Clean up after each test
|
|
}
|
|
|
|
|
|
// --- Basic Functionality Tests ---
|
|
|
|
// TEST_CASE 1: Test that relay channels initialize correctly to RELAY_CHN_STATE_FREE
|
|
TEST_CASE("Relay channels initialize correctly to FREE state", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 1: Test that relays do nothing when an invlid channel id given
|
|
TEST_CASE("Run forward does nothing if channel id is invalid", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
int invalid_id = relay_chn_count * 2 + i;
|
|
relay_chn_run_forward(invalid_id); // relay_chn_run_forward returns void
|
|
// Short delay for state to update
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 2: Test that relays run in the forward direction and update their state
|
|
TEST_CASE("Relay channels run forward and update state", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
relay_chn_run_forward(i); // relay_chn_run_forward returns void
|
|
// Short delay for state to update
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 3: Test that relays do nothing when an invlid channel id given
|
|
TEST_CASE("Run reverse does nothing if channel id is invalid", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
int invalid_id = relay_chn_count * 2 + i;
|
|
relay_chn_run_reverse(invalid_id); // relay_chn_run_forward returns void
|
|
// Short delay for state to update
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 4: Test that relays run in the reverse direction and update their state
|
|
TEST_CASE("Relay channels run reverse and update state", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
relay_chn_run_reverse(i); // relay_chn_run_reverse returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(i));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 5: Test that relays stop and transition to RELAY_CHN_STATE_FREE
|
|
// This test also verifies the transition to FREE state after a STOP command.
|
|
TEST_CASE("Relay channels stop and update to FREE state", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
// First, run forward to test stopping and transitioning to FREE state
|
|
relay_chn_run_forward(i); // relay_chn_run_forward returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(i));
|
|
|
|
// Now, issue the stop command
|
|
relay_chn_stop(i); // relay_chn_stop returns void
|
|
// Immediately after stop, state should be STOPPED
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(i));
|
|
|
|
// Then, wait for the inertia period for it to transition to RELAY_CHN_STATE_FREE
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 6: Get state should return UNDEFINED when id is not valid
|
|
TEST_CASE("Get state returns UNDEFINED when id is invalid", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
int invalid_id = relay_chn_count * 2 + i;
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_UNDEFINED, relay_chn_get_state(invalid_id));
|
|
}
|
|
// Test for running states also
|
|
relay_chn_run_forward(RELAY_CHN_ID_ALL);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
int invalid_id = relay_chn_count * 2 + i;
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_UNDEFINED, relay_chn_get_state(invalid_id));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 7: Get state string should return "UNKNOWN" when id is not valid
|
|
TEST_CASE("Get state string returns UNKNOWN when id is invalid", "[relay_chn]") {
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
int invalid_id = relay_chn_count * 2 + i;
|
|
TEST_ASSERT_EQUAL_STRING("UNKNOWN", relay_chn_get_state_str(invalid_id));
|
|
}
|
|
// Test for running states also
|
|
relay_chn_run_forward(RELAY_CHN_ID_ALL);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
for (uint8_t i = 0; i < relay_chn_count; i++) {
|
|
int invalid_id = relay_chn_count * 2 + i;
|
|
TEST_ASSERT_EQUAL_STRING("UNKNOWN", relay_chn_get_state_str(invalid_id));
|
|
}
|
|
}
|
|
|
|
// TEST_CASE 8: Test independent operation of multiple relay channels
|
|
TEST_CASE("Multiple channels can operate independently", "[relay_chn]") {
|
|
if (relay_chn_count >= 2) {
|
|
// Start Channel 0 in forward direction
|
|
relay_chn_run_forward(0); // relay_chn_run_forward returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(0));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(1)); // Other channel should not be affected
|
|
|
|
// Start Channel 1 in reverse direction
|
|
relay_chn_run_reverse(1); // relay_chn_run_reverse returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(0));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(1));
|
|
|
|
// Stop Channel 0 and wait for it to become FREE
|
|
relay_chn_stop(0); // relay_chn_stop returns void
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(0));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(1)); // Other channel should continue running
|
|
|
|
// Stop Channel 1 and wait for it to become FREE
|
|
relay_chn_stop(1); // relay_chn_stop returns void
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(0));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(1));
|
|
} else {
|
|
ESP_LOGW("TEST", "Skipping 'Multiple channels can operate independently' test: Not enough channels available.");
|
|
}
|
|
}
|
|
|
|
|
|
// ### Inertia and State Transition Tests
|
|
|
|
// This section specifically targets the inertia periods and complex state transitions as per the component's logic.
|
|
|
|
// TEST_CASE 7: Test transition from forward to reverse with inertia and state checks
|
|
// Scenario: RELAY_CHN_STATE_FORWARD -> (relay_chn_run_reverse) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_REVERSE
|
|
TEST_CASE("Forward to Reverse transition with opposite inertia", "[relay_chn][inertia]") {
|
|
uint8_t ch = 0; // Channel to test
|
|
|
|
// 1. Start in forward direction
|
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Short delay for state stabilization
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue reverse command
|
|
relay_chn_run_reverse(ch); // relay_chn_run_reverse returns void
|
|
// Immediately after the command, the motor should be stopped
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE_PENDING, relay_chn_get_state(ch));
|
|
|
|
// Wait for the inertia period (after which the reverse command will be dispatched)
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch)); // Should now be in reverse state
|
|
}
|
|
|
|
// TEST_CASE 8: Test transition from reverse to forward with inertia and state checks
|
|
// Scenario: RELAY_CHN_STATE_REVERSE -> (relay_chn_run_forward) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_FORWARD
|
|
TEST_CASE("Reverse to Forward transition with opposite inertia", "[relay_chn][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// 1. Start in reverse direction
|
|
relay_chn_run_reverse(ch); // relay_chn_run_reverse returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue forward command
|
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD_PENDING, relay_chn_get_state(ch));
|
|
|
|
// Wait for inertia
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 9: Test issuing the same run command while already running (no inertia expected)
|
|
// Scenario: RELAY_CHN_STATE_FORWARD -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD
|
|
TEST_CASE("Running in same direction does not incur inertia", "[relay_chn][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// 1. Start in forward direction
|
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue the same forward command again
|
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
|
// As per the code, is_direction_opposite_to_current_motion should return false, so no inertia.
|
|
// Just a short delay to check state remains the same.
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 10: Test transition from FREE state to running (no inertia expected)
|
|
// Scenario: RELAY_CHN_STATE_FREE -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD
|
|
TEST_CASE("FREE to Running transition without inertia", "[relay_chn][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// setUp() should have already brought the channel to FREE state
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch));
|
|
|
|
// Start in forward direction
|
|
relay_chn_run_forward(ch); // relay_chn_run_forward returns void
|
|
// No inertia is expected when starting from FREE state.
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// --- Listener Test Globals ---
|
|
typedef struct {
|
|
uint8_t chn_id;
|
|
relay_chn_state_t old_state;
|
|
relay_chn_state_t new_state;
|
|
int call_count;
|
|
} listener_callback_info_t;
|
|
|
|
static listener_callback_info_t listener1_info;
|
|
static listener_callback_info_t listener2_info;
|
|
|
|
// --- Listener Test Helper Functions ---
|
|
|
|
// Clear the memory from possible garbage values
|
|
static void reset_listener_info(listener_callback_info_t* info) {
|
|
memset(info, 0, sizeof(listener_callback_info_t));
|
|
}
|
|
|
|
static void test_listener_1(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state) {
|
|
listener1_info.chn_id = chn_id;
|
|
listener1_info.old_state = old_state;
|
|
listener1_info.new_state = new_state;
|
|
listener1_info.call_count++;
|
|
}
|
|
|
|
static void test_listener_2(uint8_t chn_id, relay_chn_state_t old_state, relay_chn_state_t new_state) {
|
|
listener2_info.chn_id = chn_id;
|
|
listener2_info.old_state = old_state;
|
|
listener2_info.new_state = new_state;
|
|
listener2_info.call_count++;
|
|
}
|
|
|
|
// ### Listener Functionality Tests
|
|
|
|
TEST_CASE("Listener is called on state change", "[relay_chn][listener]") {
|
|
uint8_t ch = 0;
|
|
reset_listener_info(&listener1_info);
|
|
|
|
// 1. Register the listener
|
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
|
|
|
// 2. Trigger a state change
|
|
relay_chn_run_forward(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Allow event to be processed
|
|
|
|
// 3. Verify the listener was called with correct parameters
|
|
TEST_ASSERT_EQUAL(1, listener1_info.call_count);
|
|
TEST_ASSERT_EQUAL(ch, listener1_info.chn_id);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, listener1_info.old_state);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener1_info.new_state);
|
|
|
|
// 4. Unregister to clean up
|
|
relay_chn_unregister_listener(test_listener_1);
|
|
}
|
|
|
|
TEST_CASE("Unregistered listener is not called", "[relay_chn][listener]") {
|
|
uint8_t ch = 0;
|
|
reset_listener_info(&listener1_info);
|
|
|
|
// 1. Register and then immediately unregister the listener
|
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
|
relay_chn_unregister_listener(test_listener_1);
|
|
|
|
// 2. Trigger a state change
|
|
relay_chn_run_forward(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
|
|
// 3. Verify the listener was NOT called
|
|
TEST_ASSERT_EQUAL(0, listener1_info.call_count);
|
|
}
|
|
|
|
TEST_CASE("Multiple listeners are called on state change", "[relay_chn][listener]") {
|
|
uint8_t ch = 0;
|
|
reset_listener_info(&listener1_info);
|
|
reset_listener_info(&listener2_info);
|
|
|
|
// 1. Register two different listeners
|
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_2));
|
|
|
|
// 2. Trigger a state change
|
|
relay_chn_run_forward(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
|
|
// 3. Verify listener 1 was called correctly
|
|
TEST_ASSERT_EQUAL(1, listener1_info.call_count);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, listener1_info.old_state);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener1_info.new_state);
|
|
|
|
// 4. Verify listener 2 was also called correctly
|
|
TEST_ASSERT_EQUAL(1, listener2_info.call_count);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, listener2_info.old_state);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener2_info.new_state);
|
|
|
|
// 5. Clean up
|
|
relay_chn_unregister_listener(test_listener_1);
|
|
relay_chn_unregister_listener(test_listener_2);
|
|
}
|
|
|
|
TEST_CASE("Listener registration handles invalid arguments and duplicates", "[relay_chn][listener]") {
|
|
reset_listener_info(&listener1_info);
|
|
|
|
// 1. Registering a NULL listener should fail
|
|
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_register_listener(NULL));
|
|
|
|
// 2. Unregistering a NULL listener should not crash
|
|
relay_chn_unregister_listener(NULL);
|
|
|
|
// 3. Registering the same listener twice should be handled gracefully
|
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1));
|
|
TEST_ESP_OK(relay_chn_register_listener(test_listener_1)); // Second call should be a no-op
|
|
|
|
// 4. Trigger a state change and verify the listener is only called ONCE
|
|
relay_chn_run_forward(0);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(1, listener1_info.call_count);
|
|
|
|
// 5. Clean up
|
|
relay_chn_unregister_listener(test_listener_1);
|
|
}
|
|
|
|
|
|
// ### Tilt Functionality Tests (Conditional)
|
|
|
|
// This section will only be compiled if **`CONFIG_RELAY_CHN_ENABLE_TILTING`** is defined as **`1`** in `sdkconfig`.
|
|
|
|
#if CONFIG_RELAY_CHN_ENABLE_TILTING == 1
|
|
|
|
#define RELAY_CHN_CMD_FORWARD 1
|
|
#define RELAY_CHN_CMD_REVERSE 2
|
|
|
|
// Helper function to prepare channel for tilt tests
|
|
void prepare_channel_for_tilt(uint8_t chn_id, int initial_cmd) {
|
|
// Ensure the channel has had a 'last_run_cmd'
|
|
if (initial_cmd == RELAY_CHN_CMD_FORWARD) {
|
|
relay_chn_run_forward(chn_id);
|
|
} else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE
|
|
relay_chn_run_reverse(chn_id);
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Allow command to process
|
|
relay_chn_stop(chn_id); // Stop it to set last_run_cmd but return to FREE for next test
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(chn_id));
|
|
}
|
|
|
|
// TEST_CASE 11: Test transition from running forward to tilt forward
|
|
// Scenario: RELAY_CHN_STATE_FORWARD -> (relay_chn_tilt_forward) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_FORWARD
|
|
TEST_CASE("Run Forward to Tilt Forward transition with inertia", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running forward first to set last_run_cmd
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
|
|
|
// 1. Start in forward direction
|
|
relay_chn_run_forward(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue tilt forward command
|
|
relay_chn_tilt_forward(ch);
|
|
// After tilt command, it should immediately stop and then trigger inertia.
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(ch));
|
|
|
|
// Wait for the inertia period (after which the tilt command will be dispatched)
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 12: Test transition from running reverse to tilt reverse
|
|
// Scenario: RELAY_CHN_STATE_REVERSE -> (relay_chn_tilt_reverse) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_REVERSE
|
|
TEST_CASE("Run Reverse to Tilt Reverse transition with inertia", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running reverse first to set last_run_cmd
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_REVERSE);
|
|
|
|
// 1. Start in reverse direction
|
|
relay_chn_run_reverse(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue tilt reverse command
|
|
relay_chn_tilt_reverse(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state(ch));
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 13: Test transition from FREE state to tilt forward (now with preparation)
|
|
// Scenario: RELAY_CHN_STATE_FREE -> (prepare) -> RELAY_CHN_STATE_FREE -> (relay_chn_tilt_forward) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_FORWARD
|
|
TEST_CASE("FREE to Tilt Forward transition with inertia (prepared)", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running forward first to set last_run_cmd
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch)); // Ensure we are back to FREE
|
|
|
|
// Issue tilt forward command
|
|
relay_chn_tilt_forward(ch);
|
|
// From FREE state, tilt command should still incur the inertia due to the internal timer logic
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 14: Test transition from FREE state to tilt reverse (now with preparation)
|
|
// Scenario: RELAY_CHN_STATE_FREE -> (prepare) -> RELAY_CHN_STATE_FREE -> (relay_chn_tilt_reverse) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_TILT_REVERSE
|
|
TEST_CASE("FREE to Tilt Reverse transition with inertia (prepared)", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running reverse first to set last_run_cmd
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_REVERSE);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch)); // Ensure we are back to FREE
|
|
|
|
// Issue tilt reverse command
|
|
relay_chn_tilt_reverse(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 15: Test transition from tilt forward to run forward (inertia expected for run)
|
|
// Scenario: RELAY_CHN_STATE_TILT_FORWARD -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD
|
|
TEST_CASE("Tilt Forward to Run Forward transition with inertia", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running forward first to set last_run_cmd, then tilt
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
|
relay_chn_tilt_forward(ch); // Go to tilt state
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue run forward command
|
|
relay_chn_run_forward(ch);
|
|
// From Tilt to Run in the same logical name but in the opposite direction, inertia is expected.
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD_PENDING, relay_chn_get_state(ch));
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 16: Test transition from tilt reverse to run reverse (no inertia expected for run)
|
|
// Scenario: RELAY_CHN_STATE_TILT_REVERSE -> (relay_chn_run_reverse) -> RELAY_CHN_STATE_REVERSE
|
|
TEST_CASE("Tilt Reverse to Run Reverse transition with inertia", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running reverse first to set last_run_cmd, then tilt
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_REVERSE);
|
|
relay_chn_tilt_reverse(ch); // Go to tilt state
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue run reverse command
|
|
relay_chn_run_reverse(ch);
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE_PENDING, relay_chn_get_state(ch));
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 17: Test transition from tilt forward to run reverse (without inertia)
|
|
// Scenario: RELAY_CHN_STATE_TILT_FORWARD -> (relay_chn_run_reverse) -> RELAY_CHN_STATE_REVERSE
|
|
TEST_CASE("Tilt Forward to Run Reverse transition without inertia", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running forward first to set last_run_cmd, then tilt
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
|
relay_chn_tilt_forward(ch); // Go to tilt state
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue run reverse command (opposite direction)
|
|
relay_chn_run_reverse(ch);
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state(ch));
|
|
}
|
|
|
|
// TEST_CASE 18: Test stopping from a tilt state (no inertia for stop command itself)
|
|
// Scenario: RELAY_CHN_STATE_TILT_FORWARD -> (relay_chn_stop) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_FREE
|
|
TEST_CASE("Tilt to Stop transition without immediate inertia for stop", "[relay_chn][tilt][inertia]") {
|
|
uint8_t ch = 0;
|
|
|
|
// Prepare channel by running forward first to set last_run_cmd, then tilt
|
|
prepare_channel_for_tilt(ch, RELAY_CHN_CMD_FORWARD);
|
|
relay_chn_tilt_forward(ch); // Go to tilt state
|
|
vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state(ch));
|
|
|
|
// 2. Issue stop command
|
|
relay_chn_stop(ch);
|
|
// Stop command should apply immediately, setting state to FREE since last state was tilt.
|
|
vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms));
|
|
TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch));
|
|
}
|
|
|
|
#else // CONFIG_RELAY_CHN_ENABLE_TILTING == 0
|
|
// If tilt functionality is disabled, these tests are skipped.
|
|
// A dummy test case is added to indicate this in the test output.
|
|
TEST_CASE("Tilt functionality is disabled, skipping tilt tests", "[relay_chn][tilt_disabled]") {
|
|
TEST_ASSERT_TRUE(true); // Just to ensure at least one test passes for visibility
|
|
}
|
|
#endif // CONFIG_RELAY_CHN_ENABLE_TILTING
|
|
|
|
|
|
// ### `app_main` Function
|
|
|
|
// --- app_main function ---
|
|
void app_main(void) {
|
|
// Run the Unity test runner
|
|
unity_run_all_tests();
|
|
|
|
// After tests complete, instead of restarting, the device will halt.
|
|
while (1) {
|
|
vTaskDelay(pdMS_TO_TICKS(1000)); // Wait with low power consumption
|
|
}
|
|
} |