153 lines
5.9 KiB
C
153 lines
5.9 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2025 Kozmotronik Tech
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "test_relay_chn_notify_common.h"
|
|
|
|
// This is a private header, but we need it for direct notification calls and queue length.
|
|
// It's included conditionally in the build via CMakeLists.txt when NVS is enabled.
|
|
#include "relay_chn_notify.h"
|
|
|
|
// ### Listener Functionality Tests
|
|
|
|
TEST_CASE("Listener is called on state change", "[relay_chn][notify]")
|
|
{
|
|
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][notify]")
|
|
{
|
|
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][notify]")
|
|
{
|
|
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][notify]")
|
|
{
|
|
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);
|
|
}
|
|
|
|
TEST_CASE("Notify queue full scenario is handled gracefully", "[relay_chn][notify]")
|
|
{
|
|
// 1. Setup
|
|
blocking_listener_sem = xSemaphoreCreateBinary();
|
|
log_check_sem = xSemaphoreCreateBinary();
|
|
blocking_listener_call_count = 0;
|
|
|
|
// Intercept logs to check for the "queue full" warning
|
|
original_vprintf = esp_log_set_vprintf(log_check_vprintf);
|
|
|
|
// 2. Register a listener that will block, allowing the queue to fill up
|
|
TEST_ESP_OK(relay_chn_register_listener(blocking_listener));
|
|
vTaskDelay(pdMS_TO_TICKS(TEST_DELAY_MARGIN_MS)); // Allow task to start
|
|
|
|
// 3. Fill the queue. The first event will be consumed immediately by the dispatcher,
|
|
// which will then call the blocking_listener and block. The remaining (LEN - 1)
|
|
// events will sit in the queue, leaving one empty slot.
|
|
for (int i = 0; i < TEST_RELAY_CHN_NOTIFY_QUEUE_LEN; i++) {
|
|
TEST_ESP_OK(relay_chn_notify_state_change(0, RELAY_CHN_STATE_IDLE, RELAY_CHN_STATE_FORWARD));
|
|
}
|
|
|
|
// 4. Send one more event to fill the last slot in the queue. This should succeed.
|
|
TEST_ESP_OK(relay_chn_notify_state_change(0, RELAY_CHN_STATE_IDLE, RELAY_CHN_STATE_FORWARD));
|
|
|
|
// 5. Now the queue is full. Trigger one more event to cause an overflow.
|
|
// This call should fail and log the warning.
|
|
TEST_ASSERT_EQUAL(ESP_FAIL, relay_chn_notify_state_change(0, RELAY_CHN_STATE_IDLE, RELAY_CHN_STATE_FORWARD));
|
|
|
|
// 6. Wait for the "queue full" log message to be captured by our vprintf hook
|
|
TEST_ASSERT_TRUE_MESSAGE(xSemaphoreTake(log_check_sem, pdMS_TO_TICKS(1000)) == pdTRUE, "Did not receive 'queue full' log message");
|
|
|
|
// 7. Unblock the listener so it can process all queued items.
|
|
// There was 1 initial event + QUEUE_LEN events that were successfully queued.
|
|
for (int i = 0; i < TEST_RELAY_CHN_NOTIFY_QUEUE_LEN + 1; i++) {
|
|
xSemaphoreGive(blocking_listener_sem);
|
|
// Give the dispatcher task a moment to process one item from the queue
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
}
|
|
|
|
// 8. Verify the listener was called exactly QUEUE_LEN + 1 times
|
|
TEST_ASSERT_EQUAL_INT(TEST_RELAY_CHN_NOTIFY_QUEUE_LEN + 1, blocking_listener_call_count);
|
|
|
|
// 9. Cleanup
|
|
esp_log_set_vprintf(original_vprintf);
|
|
relay_chn_unregister_listener(blocking_listener);
|
|
vSemaphoreDelete(blocking_listener_sem);
|
|
vSemaphoreDelete(log_check_sem);
|
|
blocking_listener_sem = NULL;
|
|
log_check_sem = NULL;
|
|
original_vprintf = NULL;
|
|
}
|