diff --git a/test_apps/main/CMakeLists.txt b/test_apps/main/CMakeLists.txt index 2fec2dc..5ff1cfd 100644 --- a/test_apps/main/CMakeLists.txt +++ b/test_apps/main/CMakeLists.txt @@ -1,14 +1,23 @@ # === These files must be included in any case === set(srcs "test_common.c" - "test_app_main.c" - "test_relay_chn_core.c" - "test_relay_chn_listener.c") + "test_app_main.c") -if(CONFIG_RELAY_CHN_ENABLE_TILTING) - list(APPEND srcs "test_relay_chn_tilt.c") +# === Selective compilation based on channel count === +if(CONFIG_RELAY_CHN_COUNT GREATER 1) + list(APPEND srcs "test_relay_chn_core_multi.c" + "test_relay_chn_listener_multi.c") +else() + list(APPEND srcs "test_relay_chn_core_single.c" + "test_relay_chn_listener_single.c") endif() -message(STATUS "srcs=${srcs}") +if(CONFIG_RELAY_CHN_ENABLE_TILTING) + if(CONFIG_RELAY_CHN_COUNT GREATER 1) + list(APPEND srcs "test_relay_chn_tilt_multi.c") + else() + list(APPEND srcs "test_relay_chn_tilt_single.c") + endif() +endif() # In order for the cases defined by `TEST_CASE` to be linked into the final elf, # the component can be registered as WHOLE_ARCHIVE diff --git a/test_apps/main/test_common.c b/test_apps/main/test_common.c index c952b68..be7169c 100644 --- a/test_apps/main/test_common.c +++ b/test_apps/main/test_common.c @@ -2,16 +2,38 @@ const char *TEST_TAG = "RELAY_CHN_TEST"; -// GPIO eşlemesi (örn: GPIO_NUM_4 vs GPIO_NUM_5) -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]); -const uint8_t relay_chn_count = gpio_count / 2; - -// Konfigürasyon tabanlı inertia süresi +const uint8_t relay_chn_count = CONFIG_RELAY_CHN_COUNT; const uint32_t opposite_inertia_ms = CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS; const uint32_t test_delay_margin_ms = 50; // ms toleransı bool g_is_component_initialized = false; + +// Test-wide GPIO map +#if CONFIG_RELAY_CHN_COUNT > 1 +const uint8_t gpio_map[] = { + 0, 1, + 2, 3 +#if CONFIG_RELAY_CHN_COUNT > 2 + , 4, 5 +#if CONFIG_RELAY_CHN_COUNT > 3 + , 6, 7 +#if CONFIG_RELAY_CHN_COUNT > 4 + , 8, 9 +#if CONFIG_RELAY_CHN_COUNT > 5 + , 10, 11 +#if CONFIG_RELAY_CHN_COUNT > 6 + , 12, 13 +#if CONFIG_RELAY_CHN_COUNT > 7 + , 14, 15 +#endif +#endif +#endif +#endif +#endif +#endif +}; +#else +const uint8_t gpio_map[] = {4, 5}; +#endif + +const uint8_t gpio_count = sizeof(gpio_map) / sizeof(gpio_map[0]); \ No newline at end of file diff --git a/test_apps/main/test_common.h b/test_apps/main/test_common.h index 50d3f63..6c9c9eb 100644 --- a/test_apps/main/test_common.h +++ b/test_apps/main/test_common.h @@ -3,23 +3,21 @@ #include // For memset #include "unity.h" #include "relay_chn.h" -#include "driver/gpio.h" #include "esp_log.h" -#include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" // Test log tag extern const char *TEST_TAG; -// GPIO konfigürasyonları -extern const gpio_num_t gpio_map[]; +// GPIO configurations +extern const uint8_t gpio_map[]; extern const uint8_t gpio_count; extern const uint8_t relay_chn_count; -// Config parametreleri +// Config variables for tests extern const uint32_t opposite_inertia_ms; extern const uint32_t test_delay_margin_ms; -// Init durumu +// Init state extern bool g_is_component_initialized; diff --git a/test_apps/main/test_relay_chn_core.c b/test_apps/main/test_relay_chn_core_multi.c similarity index 94% rename from test_apps/main/test_relay_chn_core.c rename to test_apps/main/test_relay_chn_core_multi.c index 0f29c3e..8551f7c 100644 --- a/test_apps/main/test_relay_chn_core.c +++ b/test_apps/main/test_relay_chn_core_multi.c @@ -13,20 +13,20 @@ TEST_CASE("relay_chn_create handles invalid arguments", "[relay_chn][core]") TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, 1)); TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, 0)); - // 3. Test with invalid GPIO numbers (GPIO_NUM_MAX is an invalid GPIO for output) - gpio_num_t invalid_gpio_map[] = {GPIO_NUM_4, GPIO_NUM_MAX, GPIO_NUM_18, GPIO_NUM_19}; + // 3. Test with invalid GPIO numbers (127 is an invalid GPIO for output) + uint8_t invalid_gpio_map[] = {4, 127, 18, 19}; TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(invalid_gpio_map, gpio_count)); } // --- Basic Functionality Tests --- -// TEST_CASE: Test that relay channels initialize correctly to RELAY_CHN_STATE_FREE +// TEST_CASE: Test that relay channels initialize correctly to RELAY_CHN_STATE_IDLE TEST_CASE("Relay channels initialize correctly to FREE state", "[relay_chn][core]") { TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); g_is_component_initialized = true; for (uint8_t i = 0; i < relay_chn_count; i++) { - TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(i)); } } @@ -39,7 +39,7 @@ TEST_CASE("Run forward does nothing if channel id is invalid", "[relay_chn][core 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_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(i)); } } @@ -67,7 +67,7 @@ TEST_CASE("Run reverse does nothing if channel id is invalid", "[relay_chn][core // Call run_reverse with an invalid ID relay_chn_run_reverse(invalid_id); vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); - TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(i)); } } @@ -127,13 +127,13 @@ TEST_CASE("stop with ID_ALL stops all running channels", "[relay_chn][core][id_a // 3. Verify all channels have transitioned to the FREE state for (uint8_t i = 0; i < relay_chn_count; i++) { - TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(i)); } } -// TEST_CASE: Test that relays stop and transition to RELAY_CHN_STATE_FREE +// TEST_CASE: Test that relays stop and transition to RELAY_CHN_STATE_IDLE // 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][core]") { TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); @@ -151,9 +151,9 @@ TEST_CASE("Relay channels stop and update to FREE state", "[relay_chn][core]") { 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 + // Then, wait for the inertia period for it to transition to RELAY_CHN_STATE_IDLE vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); - TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(i)); } } @@ -203,7 +203,7 @@ TEST_CASE("Multiple channels can operate independently", "[relay_chn][core]") { 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 + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, 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 @@ -214,14 +214,14 @@ TEST_CASE("Multiple channels can operate independently", "[relay_chn][core]") { // 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_IDLE, 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)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(0)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(1)); } else { ESP_LOGW("TEST", "Skipping 'Multiple channels can operate independently' test: Not enough channels available."); } @@ -301,7 +301,7 @@ TEST_CASE("Running in same direction does not incur inertia", "[relay_chn][core] } // TEST_CASE: Test transition from FREE state to running (no inertia expected) -// Scenario: RELAY_CHN_STATE_FREE -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD +// Scenario: RELAY_CHN_STATE_IDLE -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD TEST_CASE("FREE to Running transition without inertia", "[relay_chn][core][inertia]") { uint8_t ch = 0; @@ -309,7 +309,7 @@ TEST_CASE("FREE to Running transition without inertia", "[relay_chn][core][inert g_is_component_initialized = true; // setUp() should have already brought the channel to FREE state - TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(ch)); // Start in forward direction relay_chn_run_forward(ch); // relay_chn_run_forward returns void @@ -388,7 +388,7 @@ TEST_CASE("Flipping a running channel stops it and flips direction", "[relay_chn // 4. Wait for the flip inertia to pass, after which it should be FREE and FLIPPED vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); - TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(ch)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(ch)); TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction(ch)); } diff --git a/test_apps/main/test_relay_chn_core_single.c b/test_apps/main/test_relay_chn_core_single.c new file mode 100644 index 0000000..734f4bf --- /dev/null +++ b/test_apps/main/test_relay_chn_core_single.c @@ -0,0 +1,204 @@ +#include "test_common.h" + + +// --- Initialization Tests --- + +TEST_CASE("relay_chn_create handles invalid arguments", "[relay_chn][core]") +{ + // 1. Test with NULL gpio_map + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(NULL, gpio_count)); + + // 2. Test with incorrect gpio_count (must be RELAY_CHN_COUNT * 2) + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, gpio_count - 1)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, 1)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(gpio_map, 0)); + + // 3. Test with invalid GPIO numbers (GPIO_NUM_MAX is an invalid GPIO for output) + uint8_t invalid_gpio_map[] = {4, 127}; + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, relay_chn_create(invalid_gpio_map, gpio_count)); +} + +// --- Basic Functionality Tests --- + +// TEST_CASE: Test that relay channels initialize correctly to RELAY_CHN_STATE_IDLE +TEST_CASE("Relay channels initialize correctly to IDLE state", "[relay_chn][core]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); +} + +// TEST_CASE: Test that relays run in the forward direction and update their state +TEST_CASE("Relay channels run forward and update state", "[relay_chn][core]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + relay_chn_run_forward(); + // 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()); +} + +// TEST_CASE: Test that relays run in the reverse direction and update their state +TEST_CASE("Relay channels run reverse and update state", "[relay_chn][core]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + relay_chn_run_reverse(); // 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()); +} + + +// TEST_CASE: Test that relays stop and transition to RELAY_CHN_STATE_IDLE +// This test also verifies the transition to IDLE state after a STOP command. +TEST_CASE("Relay channels stop and update to IDLE state", "[relay_chn][core]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // First, run forward to test stopping and transitioning to IDLE state + relay_chn_run_forward(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state()); + + // Now, issue the stop command + relay_chn_stop(); // 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()); + + // Then, wait for the inertia period for it to transition to RELAY_CHN_STATE_IDLE + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); +} + + +// ### Inertia and State Transition Tests + +// This section specifically targets the inertia periods and complex state transitions as per the component's logic. + +// TEST_CASE: 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][core][inertia]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // 1. Start in forward direction + relay_chn_run_forward(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Short delay for state stabilization + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state()); + + // 2. Issue reverse command + relay_chn_run_reverse(); // 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()); + + // 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()); // Should now be in reverse state +} + +// TEST_CASE: 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][core][inertia]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // 1. Start in reverse direction + relay_chn_run_reverse(); // 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()); + + // 2. Issue forward command + relay_chn_run_forward(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD_PENDING, relay_chn_get_state()); + + // 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()); +} + +// TEST_CASE: 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][core][inertia]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // 1. Start in forward direction + relay_chn_run_forward(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state()); + + // 2. Issue the same forward command again + relay_chn_run_forward(); + // 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()); +} + +// TEST_CASE: Test transition from IDLE state to running (no inertia expected) +// Scenario: RELAY_CHN_STATE_IDLE -> (relay_chn_run_forward) -> RELAY_CHN_STATE_FORWARD +TEST_CASE("IDLE to Running transition without inertia", "[relay_chn][core][inertia]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // setUp() should have already brought the channel to IDLE state + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); + + // Start in forward direction + relay_chn_run_forward(); + // No inertia is expected when starting from IDLE state. + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state()); +} + +// ### Direction Flipping Tests + +TEST_CASE("Single channel direction can be flipped", "[relay_chn][core][direction]") +{ + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // 1. Initial direction should be default + TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction()); + + // 2. Flip the direction + relay_chn_flip_direction(); + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); // Wait for flip inertia + + // 3. Verify direction is flipped + TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction()); + + // 4. Flip back + relay_chn_flip_direction(); + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); // Wait for flip inertia + + // 5. Verify direction is back to default + TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_DEFAULT, relay_chn_get_direction()); +} + +TEST_CASE("Flipping a running channel stops it and flips direction", "[relay_chn][core][direction]") +{ + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // 1. Start channel running and verify state + relay_chn_run_forward(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state()); + + // 2. Flip the direction while running + relay_chn_flip_direction(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Give time for events to process + + // 3. The channel should stop as part of the flip process + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state()); + + // 4. Wait for the flip inertia to pass, after which it should be IDLE and FLIPPED + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); + TEST_ASSERT_EQUAL(RELAY_CHN_DIRECTION_FLIPPED, relay_chn_get_direction()); +} \ No newline at end of file diff --git a/test_apps/main/test_relay_chn_listener.c b/test_apps/main/test_relay_chn_listener_multi.c similarity index 95% rename from test_apps/main/test_relay_chn_listener.c rename to test_apps/main/test_relay_chn_listener_multi.c index 9415068..1d368d5 100644 --- a/test_apps/main/test_relay_chn_listener.c +++ b/test_apps/main/test_relay_chn_listener_multi.c @@ -52,7 +52,7 @@ TEST_CASE("Listener is called on state change", "[relay_chn][listener]") { // 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_IDLE, listener1_info.old_state); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener1_info.new_state); // 4. Unregister to clean up @@ -96,12 +96,12 @@ TEST_CASE("Multiple listeners are called on state change", "[relay_chn][listener // 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_IDLE, 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_IDLE, listener2_info.old_state); TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, listener2_info.new_state); // 5. Clean up diff --git a/test_apps/main/test_relay_chn_listener_single.c b/test_apps/main/test_relay_chn_listener_single.c new file mode 100644 index 0000000..4346967 --- /dev/null +++ b/test_apps/main/test_relay_chn_listener_single.c @@ -0,0 +1,130 @@ +#include "test_common.h" + + +// --- Listener Test Globals --- +typedef struct { + 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) { + /* Just ignore the channel 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) { + /* Just ignore the channel 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + 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(); + 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(RELAY_CHN_STATE_IDLE, 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + 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(); + 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + 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(); + 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_IDLE, 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_IDLE, 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + 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(); + 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); +} diff --git a/test_apps/main/test_relay_chn_tilt.c b/test_apps/main/test_relay_chn_tilt_multi.c similarity index 93% rename from test_apps/main/test_relay_chn_tilt.c rename to test_apps/main/test_relay_chn_tilt_multi.c index 47b7f32..916afc3 100644 --- a/test_apps/main/test_relay_chn_tilt.c +++ b/test_apps/main/test_relay_chn_tilt_multi.c @@ -23,7 +23,7 @@ void prepare_channel_for_tilt(uint8_t chn_id, int initial_cmd) { 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_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(chn_id)); } // TEST_CASE: Test transition from running forward to tilt forward @@ -79,7 +79,7 @@ TEST_CASE("Run Reverse to Tilt Reverse transition with inertia", "[relay_chn][ti } // TEST_CASE: 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 +// Scenario: RELAY_CHN_STATE_IDLE -> (prepare) -> RELAY_CHN_STATE_IDLE -> (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; @@ -88,7 +88,7 @@ TEST_CASE("FREE to Tilt Forward transition with inertia (prepared)", "[relay_chn // 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 + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(ch)); // Ensure we are back to FREE // Issue tilt forward command relay_chn_tilt_forward(ch); @@ -98,7 +98,7 @@ TEST_CASE("FREE to Tilt Forward transition with inertia (prepared)", "[relay_chn } // TEST_CASE: 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 +// Scenario: RELAY_CHN_STATE_IDLE -> (prepare) -> RELAY_CHN_STATE_IDLE -> (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; @@ -107,7 +107,7 @@ TEST_CASE("FREE to Tilt Reverse transition with inertia (prepared)", "[relay_chn // 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 + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(ch)); // Ensure we are back to FREE // Issue tilt reverse command relay_chn_tilt_reverse(ch); @@ -179,7 +179,7 @@ TEST_CASE("Tilt Forward to Run Reverse transition without inertia", "[relay_chn] } // TEST_CASE: 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 +// Scenario: RELAY_CHN_STATE_TILT_FORWARD -> (relay_chn_stop) -> RELAY_CHN_STATE_STOPPED -> (inertia) -> RELAY_CHN_STATE_IDLE TEST_CASE("Tilt to Stop transition without immediate inertia for stop", "[relay_chn][tilt][inertia]") { uint8_t ch = 0; @@ -196,7 +196,7 @@ TEST_CASE("Tilt to Stop transition without immediate inertia for stop", "[relay_ 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)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(ch)); } // ### Tilt Broadcast Command (RELAY_CHN_ID_ALL) Tests @@ -259,7 +259,7 @@ TEST_CASE("tilt_stop with ID_ALL stops all tilting channels", "[relay_chn][tilt] // 3. Verify all channels are free for (uint8_t i = 0; i < relay_chn_count; i++) { - TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FREE, relay_chn_get_state(i)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state(i)); } } @@ -306,28 +306,28 @@ TEST_CASE("relay_chn_tilt_auto chooses correct direction", "[relay_chn][tilt][au } // Test sensitivity set/get -TEST_CASE("relay_chn_tilt_sensitivity_set and get", "[relay_chn][tilt][sensitivity]") { +TEST_CASE("relay_chn_tilt_set_sensitivity and get", "[relay_chn][tilt][sensitivity]") { uint8_t ch = 0; uint8_t val = 0; TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); g_is_component_initialized = true; - relay_chn_tilt_sensitivity_set(ch, 0); - TEST_ESP_OK(relay_chn_tilt_sensitivity_get(ch, &val, 1)); + relay_chn_tilt_set_sensitivity(ch, 0); + TEST_ESP_OK(relay_chn_tilt_get_sensitivity(ch, &val, 1)); TEST_ASSERT_EQUAL_UINT8(0, val); - relay_chn_tilt_sensitivity_set(ch, 50); - TEST_ESP_OK(relay_chn_tilt_sensitivity_get(ch, &val, 1)); + relay_chn_tilt_set_sensitivity(ch, 50); + TEST_ESP_OK(relay_chn_tilt_get_sensitivity(ch, &val, 1)); TEST_ASSERT_EQUAL_UINT8(50, val); - relay_chn_tilt_sensitivity_set(ch, 100); - TEST_ESP_OK(relay_chn_tilt_sensitivity_get(ch, &val, 1)); + relay_chn_tilt_set_sensitivity(ch, 100); + TEST_ESP_OK(relay_chn_tilt_get_sensitivity(ch, &val, 1)); TEST_ASSERT_EQUAL_UINT8(100, val); // Set all channels - relay_chn_tilt_sensitivity_set(RELAY_CHN_ID_ALL, 42); + relay_chn_tilt_set_sensitivity(RELAY_CHN_ID_ALL, 42); uint8_t vals[CONFIG_RELAY_CHN_COUNT] = {0}; - TEST_ESP_OK(relay_chn_tilt_sensitivity_get(RELAY_CHN_ID_ALL, vals, relay_chn_count)); + TEST_ESP_OK(relay_chn_tilt_get_sensitivity(RELAY_CHN_ID_ALL, vals, relay_chn_count)); for (int i = 0; i < relay_chn_count; ++i) { TEST_ASSERT_EQUAL_UINT8(42, vals[i]); } diff --git a/test_apps/main/test_relay_chn_tilt_single.c b/test_apps/main/test_relay_chn_tilt_single.c new file mode 100644 index 0000000..7a0b8b0 --- /dev/null +++ b/test_apps/main/test_relay_chn_tilt_single.c @@ -0,0 +1,275 @@ +#include "test_common.h" + + +// ### Tilt Functionality Tests (Conditional) + +// This section will only be compiled if **`CONFIG_RELAY_CHN_ENABLE_TILTING`** is defined as **`1`** in `sdkconfig`. + +#ifndef CONFIG_RELAY_CHN_ENABLE_TILTING +#error "This test requires CONFIG_RELAY_CHN_ENABLE_TILTING" +#endif + +#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(int initial_cmd) { + // Ensure the channel has had a 'last_run_cmd' + if (initial_cmd == RELAY_CHN_CMD_FORWARD) { + relay_chn_run_forward(); + } else { // Assuming initial_cmd is RELAY_CHN_CMD_REVERSE + relay_chn_run_reverse(); + } + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); // Allow command to process + relay_chn_stop(); // 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_IDLE, relay_chn_get_state()); +} + +// TEST_CASE: 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running forward first to set last_run_cmd + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + + // 1. Start in forward direction + relay_chn_run_forward(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state()); + + // 2. Issue tilt forward command + relay_chn_tilt_forward(); + // 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()); + + // 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()); +} + +// TEST_CASE: 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running reverse first to set last_run_cmd + prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE); + + // 1. Start in reverse direction + relay_chn_run_reverse(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state()); + + // 2. Issue tilt reverse command + relay_chn_tilt_reverse(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_STOPPED, relay_chn_get_state()); + + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state()); +} + +// TEST_CASE: Test transition from FREE state to tilt forward (now with preparation) +// Scenario: RELAY_CHN_STATE_IDLE -> (prepare) -> RELAY_CHN_STATE_IDLE -> (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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running forward first to set last_run_cmd + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); // Ensure we are back to FREE + + // Issue tilt forward command + relay_chn_tilt_forward(); + // 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()); +} + +// TEST_CASE: Test transition from FREE state to tilt reverse (now with preparation) +// Scenario: RELAY_CHN_STATE_IDLE -> (prepare) -> RELAY_CHN_STATE_IDLE -> (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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running reverse first to set last_run_cmd + prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_IDLE, relay_chn_get_state()); // Ensure we are back to FREE + + // Issue tilt reverse command + relay_chn_tilt_reverse(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state()); +} + +// TEST_CASE: 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running forward first to set last_run_cmd, then tilt + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + relay_chn_tilt_forward(); // 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()); + + // 2. Issue run forward command + relay_chn_run_forward(); + // 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()); + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_FORWARD, relay_chn_get_state()); +} + +// TEST_CASE: 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running reverse first to set last_run_cmd, then tilt + prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE); + relay_chn_tilt_reverse(); // 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()); + + // 2. Issue run reverse command + relay_chn_run_reverse(); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE_PENDING, relay_chn_get_state()); + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state()); +} + +// TEST_CASE: 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]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running forward first to set last_run_cmd, then tilt + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + relay_chn_tilt_forward(); // 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()); + + // 2. Issue run reverse command (opposite direction) + relay_chn_run_reverse(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_REVERSE, relay_chn_get_state()); +} + +// TEST_CASE: 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_IDLE +TEST_CASE("Tilt to Stop transition without immediate inertia for stop", "[relay_chn][tilt][inertia]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare channel by running forward first to set last_run_cmd, then tilt + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + relay_chn_tilt_forward(); // 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()); + + // 2. Issue stop command + relay_chn_stop(); + // 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_IDLE, relay_chn_get_state()); +} + +// Test relay_chn_tilt_auto() chooses correct tilt direction +TEST_CASE("relay_chn_tilt_auto chooses correct direction", "[relay_chn][tilt][auto]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + // Prepare FORWARD + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + relay_chn_tilt_auto(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state()); + relay_chn_tilt_stop(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + + // Prepare REVERSE + prepare_channel_for_tilt(RELAY_CHN_CMD_REVERSE); + relay_chn_tilt_auto(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state()); +} + +// Test sensitivity set/get +TEST_CASE("relay_chn_tilt_set_sensitivity and get", "[relay_chn][tilt][sensitivity]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + relay_chn_tilt_set_sensitivity(0); + TEST_ASSERT_EQUAL_UINT8(0, relay_chn_tilt_get_sensitivity()); + + relay_chn_tilt_set_sensitivity(50); + TEST_ASSERT_EQUAL_UINT8(50, relay_chn_tilt_get_sensitivity()); + + relay_chn_tilt_set_sensitivity(100); + TEST_ASSERT_EQUAL_UINT8(100, relay_chn_tilt_get_sensitivity()); + + relay_chn_tilt_set_sensitivity(42); + TEST_ASSERT_EQUAL_UINT8(42, relay_chn_tilt_get_sensitivity()); +} + +// Test tilt counter logic: forward x3, reverse x3, extra reverse fails +TEST_CASE("tilt counter logic: forward and reverse consumption", "[relay_chn][tilt][counter]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + + // Tilt forward 3 times + for (int i = 0; i < 3; ++i) { + relay_chn_tilt_forward(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state()); + relay_chn_tilt_stop(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + } + + // Now tilt reverse 3 times (should succeed) + for (int i = 0; i < 3; ++i) { + relay_chn_tilt_reverse(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + if (i < 3) { + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_REVERSE, relay_chn_get_state()); + relay_chn_tilt_stop(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + } + } + + // Extra reverse tilt should fail (counter exhausted) + relay_chn_tilt_reverse(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + // Should not enter TILT_REVERSE, should remain FREE or STOPPED + relay_chn_state_t state = relay_chn_get_state(); + TEST_ASSERT(state != RELAY_CHN_STATE_TILT_REVERSE); +} + +// Test run command during TILT state +TEST_CASE("run command during TILT state transitions correctly", "[relay_chn][tilt][run-during-tilt]") { + TEST_ESP_OK(relay_chn_create(gpio_map, gpio_count)); + g_is_component_initialized = true; + + prepare_channel_for_tilt(RELAY_CHN_CMD_FORWARD); + relay_chn_tilt_forward(); + vTaskDelay(pdMS_TO_TICKS(opposite_inertia_ms + test_delay_margin_ms)); + TEST_ASSERT_EQUAL(RELAY_CHN_STATE_TILT_FORWARD, relay_chn_get_state()); + + // Issue run reverse while in TILT_FORWARD + relay_chn_run_reverse(); + vTaskDelay(pdMS_TO_TICKS(test_delay_margin_ms)); + // Should transition to REVERSE or REVERSE_PENDING depending on inertia logic + relay_chn_state_t state = relay_chn_get_state(); + TEST_ASSERT(state == RELAY_CHN_STATE_REVERSE || state == RELAY_CHN_STATE_REVERSE_PENDING); +} \ No newline at end of file diff --git a/test_apps/partition_table/partitionTable.csv b/test_apps/partition_table/partitionTable.csv new file mode 100644 index 0000000..4f87a1f --- /dev/null +++ b/test_apps/partition_table/partitionTable.csv @@ -0,0 +1,5 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +nvs,data,nvs,0xa000,24K, +phy_init,data,phy,0x10000,4K, +factory,app,factory,0x20000,1M, diff --git a/test_apps/sdkconfig b/test_apps/sdkconfig index e62945e..a20e982 100644 --- a/test_apps/sdkconfig +++ b/test_apps/sdkconfig @@ -1261,7 +1261,7 @@ CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y # Relay Channel Driver Configuration # CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200 -CONFIG_RELAY_CHN_COUNT=2 +CONFIG_RELAY_CHN_COUNT=1 CONFIG_RELAY_CHN_ENABLE_TILTING=y # end of Relay Channel Driver Configuration # end of Component config diff --git a/test_apps/sdkconfig.defaults.single b/test_apps/sdkconfig.defaults.single new file mode 100644 index 0000000..13ad935 --- /dev/null +++ b/test_apps/sdkconfig.defaults.single @@ -0,0 +1,8 @@ +# Disable task WDT for tests +CONFIG_ESP_TASK_WDT_INIT=n + +# Relay Channel Driver Default Configuration for Testing +# Keep this as short as possible for tests +CONFIG_RELAY_CHN_OPPOSITE_INERTIA_MS=200 +CONFIG_RELAY_CHN_COUNT=1 +CONFIG_RELAY_CHN_ENABLE_TILTING=y \ No newline at end of file diff --git a/test_apps/sdkconfig.old b/test_apps/sdkconfig.old index e62945e..74f5cce 100644 --- a/test_apps/sdkconfig.old +++ b/test_apps/sdkconfig.old @@ -1,6 +1,6 @@ # # Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) 5.4.2 Project Configuration +# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Configuration # CONFIG_SOC_BROWNOUT_RESET_SUPPORTED="Not determined" CONFIG_SOC_TWAI_BRP_DIV_SUPPORTED="Not determined" @@ -98,7 +98,6 @@ CONFIG_SOC_I2C_FIFO_LEN=32 CONFIG_SOC_I2C_CMD_REG_NUM=16 CONFIG_SOC_I2C_SUPPORT_SLAVE=y CONFIG_SOC_I2C_SUPPORT_APB=y -CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y CONFIG_SOC_I2C_STOP_INDEPENDENT=y CONFIG_SOC_I2S_NUM=2 CONFIG_SOC_I2S_HW_VERSION_1=y @@ -176,8 +175,6 @@ CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=64 CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y -CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 -CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 CONFIG_SOC_TOUCH_SENSOR_VERSION=1 CONFIG_SOC_TOUCH_SENSOR_NUM=10 CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 @@ -247,7 +244,7 @@ CONFIG_IDF_TOOLCHAIN_GCC=y CONFIG_IDF_TARGET_ARCH_XTENSA=y CONFIG_IDF_TARGET_ARCH="xtensa" CONFIG_IDF_TARGET="esp32" -CONFIG_IDF_INIT_VERSION="5.4.2" +CONFIG_IDF_INIT_VERSION="$IDF_INIT_VERSION" CONFIG_IDF_TARGET_ESP32=y CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 @@ -288,10 +285,10 @@ CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y # CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_INFO is not set +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG=y # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set -CONFIG_BOOTLOADER_LOG_LEVEL=3 +CONFIG_BOOTLOADER_LOG_LEVEL=4 # # Format @@ -402,7 +399,7 @@ CONFIG_PARTITION_TABLE_SINGLE_APP=y # CONFIG_PARTITION_TABLE_CUSTOM is not set CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" -CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_OFFSET=0x9000 CONFIG_PARTITION_TABLE_MD5=y # end of Partition Table @@ -465,7 +462,6 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y # CONFIG_ADC_DISABLE_DAC=y # CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set # # Legacy ADC Calibration Configuration @@ -481,55 +477,42 @@ CONFIG_ADC_CAL_LUT_ENABLE=y # Legacy DAC Driver Configurations # # CONFIG_DAC_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_DAC_SKIP_LEGACY_CONFLICT_CHECK is not set # end of Legacy DAC Driver Configurations # # Legacy MCPWM Driver Configurations # # CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set # end of Legacy MCPWM Driver Configurations # # Legacy Timer Group Driver Configurations # # CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set # end of Legacy Timer Group Driver Configurations # # Legacy RMT Driver Configurations # # CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set # end of Legacy RMT Driver Configurations # # Legacy I2S Driver Configurations # # CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set # end of Legacy I2S Driver Configurations -# -# Legacy I2C Driver Configurations -# -# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set -# end of Legacy I2C Driver Configurations - # # Legacy PCNT Driver Configurations # # CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set # end of Legacy PCNT Driver Configurations # # Legacy SDM Driver Configurations # # CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set -# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set # end of Legacy SDM Driver Configurations # end of Driver Configurations @@ -572,7 +555,6 @@ CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=y CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y # CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set # CONFIG_GPTIMER_ISR_IRAM_SAFE is not set -CONFIG_GPTIMER_OBJ_CACHE_SAFE=y # CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set # end of ESP-Driver:GPTimer Configurations @@ -724,7 +706,7 @@ CONFIG_RTC_CLK_CAL_CYCLES=1024 # # Peripheral Control # -# CONFIG_PERIPH_CTRL_FUNC_IN_IRAM is not set +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y # end of Peripheral Control # @@ -954,6 +936,7 @@ CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 CONFIG_HAL_SPI_MASTER_FUNC_IN_IRAM=y CONFIG_HAL_SPI_SLAVE_FUNC_IN_IRAM=y +# CONFIG_HAL_ECDSA_GEN_SIG_CM is not set # end of Hardware Abstraction Layer (HAL) and Low Level (LL) # @@ -1062,7 +1045,6 @@ CONFIG_MBEDTLS_HAVE_TIME=y # CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y -CONFIG_MBEDTLS_SHA1_C=y CONFIG_MBEDTLS_SHA512_C=y # CONFIG_MBEDTLS_SHA3_C is not set CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y @@ -1143,7 +1125,6 @@ CONFIG_MBEDTLS_ECP_NIST_OPTIM=y # CONFIG_MBEDTLS_HKDF_C is not set # CONFIG_MBEDTLS_THREADING_C is not set CONFIG_MBEDTLS_ERROR_STRINGS=y -# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set # end of mbedTLS # @@ -1203,7 +1184,6 @@ CONFIG_SPI_FLASH_BROWNOUT_RESET=y # CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 # CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set -# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set # end of Optional and Experimental Features (READ DOCS FIRST) # end of Main Flash configuration @@ -1277,10 +1257,10 @@ CONFIG_RELAY_CHN_ENABLE_TILTING=y # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set -CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y -# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_INFO is not set +CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG=y # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set -CONFIG_LOG_BOOTLOADER_LEVEL=3 +CONFIG_LOG_BOOTLOADER_LEVEL=4 # CONFIG_APP_ROLLBACK_ENABLE is not set # CONFIG_FLASH_ENCRYPTION_ENABLED is not set # CONFIG_FLASHMODE_QIO is not set