Fix listener memory allocation bug.
- 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.
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#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.
|
||||
@@ -242,6 +243,127 @@ TEST_CASE("FREE to Running transition without inertia", "[relay_chn][inertia]")
|
||||
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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user