From 8df4625c8427fcc28918d4b60da2286dccb1ca6c Mon Sep 17 00:00:00 2001 From: Jakob Hasse Date: Wed, 19 Jul 2023 19:18:11 +0800 Subject: [PATCH] refactor(esp_event): improved esp_event unit tests * Decomposed tests into atomic unit tests * Made tests less time-dependent, hence more robust on different platforms (ESP32, QEMU, Linux) * Ported most of the tests to linux * Removed some redundant tests * Fixed bug the tests discovered * Simplified parts of the tests to be more clear * Partially used C++ to simplify setup/teardown * Unified setup/teardown in general --- components/esp_event/.build-test-rules.yml | 2 +- components/esp_event/esp_event.c | 2 +- components/esp_event/test_apps/README.md | 6 +- .../esp_event/test_apps/main/CMakeLists.txt | 19 +- .../test_apps/main/test_default_loop.c | 158 -- .../esp_event/test_apps/main/test_event.c | 2097 ----------------- .../test_apps/main/test_event_common.cpp | 1573 +++++++++++++ .../test_apps/main/test_event_main.c | 31 +- .../test_apps/main/test_event_target.c | 765 ++++++ .../esp_event/test_apps/pytest_esp_event.py | 8 + .../esp_event/test_apps/sdkconfig.defaults | 1 - .../test_apps/sdkconfig.defaults.linux | 1 + 12 files changed, 2372 insertions(+), 2291 deletions(-) delete mode 100644 components/esp_event/test_apps/main/test_default_loop.c delete mode 100644 components/esp_event/test_apps/main/test_event.c create mode 100644 components/esp_event/test_apps/main/test_event_common.cpp create mode 100644 components/esp_event/test_apps/main/test_event_target.c create mode 100644 components/esp_event/test_apps/sdkconfig.defaults.linux diff --git a/components/esp_event/.build-test-rules.yml b/components/esp_event/.build-test-rules.yml index eb6a06f0c2..5534c82a31 100644 --- a/components/esp_event/.build-test-rules.yml +++ b/components/esp_event/.build-test-rules.yml @@ -2,5 +2,5 @@ components/esp_event/test_apps: enable: - - if: IDF_TARGET in ["esp32", "esp32s2", "esp32c3"] + - if: IDF_TARGET in ["esp32", "esp32s2", "esp32c3", "linux"] reason: covers all major arch types, xtensa vs riscv, single vs dual-core diff --git a/components/esp_event/esp_event.c b/components/esp_event/esp_event.c index 6808c887d4..d1b0e7bb4e 100644 --- a/components/esp_event/esp_event.c +++ b/components/esp_event/esp_event.c @@ -778,7 +778,7 @@ esp_err_t esp_event_handler_unregister_with_internal(esp_event_loop_handle_t eve if (event_base == ESP_EVENT_ANY_BASE && event_id != ESP_EVENT_ANY_ID) { ESP_LOGE(TAG, "unregistering to any event base with specific id unsupported"); - return ESP_FAIL; + return ESP_ERR_INVALID_ARG; } if (event_base == ESP_EVENT_ANY_BASE) { diff --git a/components/esp_event/test_apps/README.md b/components/esp_event/test_apps/README.md index b4f36a2334..bc090eb7ff 100644 --- a/components/esp_event/test_apps/README.md +++ b/components/esp_event/test_apps/README.md @@ -1,2 +1,4 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | -| ----------------- | ----- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C3 | ESP32-S2 | Linux | +| ----------------- | ----- | -------- | -------- | ----- | + +For testing on linux, please deactivate the task stack overflow watchpoint diff --git a/components/esp_event/test_apps/main/CMakeLists.txt b/components/esp_event/test_apps/main/CMakeLists.txt index d39e29e14a..2db0197b0f 100644 --- a/components/esp_event/test_apps/main/CMakeLists.txt +++ b/components/esp_event/test_apps/main/CMakeLists.txt @@ -1,8 +1,19 @@ -set(srcs "test_event_main.c" - "test_default_loop.c" - "test_event.c") +idf_build_get_property(target IDF_TARGET) + +set(srcs "test_event_main.c" "test_event_common.cpp") +set(priv_requires "esp_event unity") + +if(NOT ${target} STREQUAL "linux") + list(APPEND srcs + "test_event_target.c") + + list(APPEND priv_requires + "driver" + "esp_timer" + "test_utils") +endif() idf_component_register(SRCS ${srcs} PRIV_INCLUDE_DIRS . ../../private_include - PRIV_REQUIRES esp_event driver esp_timer unity test_utils + PRIV_REQUIRES ${priv_requires} WHOLE_ARCHIVE) diff --git a/components/esp_event/test_apps/main/test_default_loop.c b/components/esp_event/test_apps/main/test_default_loop.c deleted file mode 100644 index b7bb1faac9..0000000000 --- a/components/esp_event/test_apps/main/test_default_loop.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#include -#include - -#include "esp_event.h" -#include "sdkconfig.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" - -#include "esp_event.h" -#include "esp_event_private.h" -#include "esp_event_internal.h" - -#include "esp_heap_caps.h" - -#include "sdkconfig.h" -#include "unity.h" - -typedef struct { - void* data; - SemaphoreHandle_t mutex; -} simple_arg_t; - -static const char* TAG = "test_event"; - -ESP_EVENT_DECLARE_BASE(s_default_test_base1); -ESP_EVENT_DECLARE_BASE(s_default_test_base2); - -ESP_EVENT_DEFINE_BASE(s_default_test_base1); -ESP_EVENT_DEFINE_BASE(s_default_test_base2); - -enum { - TEST_EVENT_BASE1_EV1, - TEST_EVENT_BASE1_EV2, - TEST_EVENT_BASE1_MAX -}; - -enum { - TEST_EVENT_BASE2_EV1, - TEST_EVENT_BASE2_EV2, - TEST_EVENT_BASE2_MAX -}; - -// The initial logging "initializing test" is to ensure mutex allocation is not counted against memory not being freed -// during teardown. -#define TEST_SETUP() \ - ESP_LOGI(TAG, "initializing test"); - -static void test_event_simple_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - if (!event_handler_arg) { - return; - } - simple_arg_t* arg = (simple_arg_t*) event_handler_arg; - xSemaphoreTake(arg->mutex, portMAX_DELAY); - - int* count = (int*) arg->data; - - if (event_data == NULL) { - (*count)++; - } else { - (*count) += *((int*) event_data); - } - - xSemaphoreGive(arg->mutex); -} - -TEST_CASE("default loop: can create and delete loop", "[event]") -{ - TEST_SETUP(); - - TEST_ESP_OK(esp_event_loop_create_default()); - - TEST_ESP_OK(esp_event_loop_delete_default()); -} - -TEST_CASE("default loop: registering fails on uninitialized default loop", "[event]") -{ - TEST_SETUP(); - - esp_event_handler_instance_t instance; - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_event_handler_instance_register(s_default_test_base1, - TEST_EVENT_BASE1_EV1, - test_event_simple_handler, - NULL, - &instance)); -} - -TEST_CASE("default loop: registering/unregistering event works", "[event]") -{ - TEST_SETUP(); - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - TEST_ESP_OK(esp_event_loop_create_default()); - - esp_event_handler_instance_t instance; - TEST_ESP_OK(esp_event_handler_instance_register(s_default_test_base1, - TEST_EVENT_BASE1_EV1, - test_event_simple_handler, - &arg, - &instance)); - TEST_ASSERT(instance); - TEST_ESP_OK(esp_event_post(s_default_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - vTaskDelay(10); - - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_handler_instance_unregister(s_default_test_base1, - TEST_EVENT_BASE1_EV1, - &instance)); - - vTaskDelay(10); - - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_loop_delete_default()); - - vSemaphoreDelete(arg.mutex); -} - -TEST_CASE("default event loop: registering event handler instance without instance context works", "[event]") { - TEST_SETUP(); - - int count_1 = 0; - - simple_arg_t arg_1 = { - .data = &count_1, - .mutex = xSemaphoreCreateMutex() - }; - - TEST_ESP_OK(esp_event_loop_create_default()); - - TEST_ESP_OK(esp_event_handler_instance_register(ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg_1, NULL)); - - TEST_ESP_OK(esp_event_post(s_default_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - vTaskDelay(10); - - TEST_ASSERT_EQUAL(1, count_1); - - TEST_ESP_OK(esp_event_loop_delete_default()); - - vSemaphoreDelete(arg_1.mutex); -} diff --git a/components/esp_event/test_apps/main/test_event.c b/components/esp_event/test_apps/main/test_event.c deleted file mode 100644 index 7e6b58ac10..0000000000 --- a/components/esp_event/test_apps/main/test_event.c +++ /dev/null @@ -1,2097 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ -#include -#include - -#include "esp_event.h" -#include "sdkconfig.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/gptimer.h" - -#include "esp_event.h" -#include "esp_event_private.h" -#include "esp_event_internal.h" - -#include "esp_heap_caps.h" -#include "esp_timer.h" - -#include "sdkconfig.h" -#include "unity.h" - -#include "test_utils.h" - - -static const char* TAG = "test_event"; - -#define TEST_CONFIG_ITEMS_TO_REGISTER 5 -#define TEST_CONFIG_TASKS_TO_SPAWN 2 -_Static_assert(TEST_CONFIG_TASKS_TO_SPAWN >= 2); // some tests test simultaneous posting of events, etc. - -/* General wait time for tests, e.g. waiting for background task to finish, - expected timeout, etc. */ -#define TEST_CONFIG_WAIT_TIME 30 - -/* Time used in tearDown function to wait for cleaning up memory in background tasks */ -#define TEST_CONFIG_TEARDOWN_WAIT 30 - -// The initial logging "initializing test" is to ensure mutex allocation is not counted against memory not being freed -// during teardown. -#define TEST_SETUP() \ - ESP_LOGI(TAG, "initializing test"); \ - size_t free_mem_before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); \ - s_test_core_id = xPortGetCoreID(); \ - s_test_priority = uxTaskPriorityGet(NULL); - -#define TEST_TEARDOWN() \ - vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); \ - TEST_ASSERT_EQUAL(free_mem_before, heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); - -typedef struct { - void* data; - SemaphoreHandle_t start; - SemaphoreHandle_t done; -} task_arg_t; - -typedef struct { - esp_event_base_t base; - int32_t id; - esp_event_handler_t* handles; - int32_t num; - esp_event_loop_handle_t loop; - bool is_registration; -} handler_registration_data_t; - -typedef struct { - esp_event_base_t base; - int32_t id; - esp_event_loop_handle_t loop; - int32_t num; -} post_event_data_t; - -typedef struct { - int performed; - int expected; - SemaphoreHandle_t done; -} performance_data_t; - -typedef struct { - void* data; - SemaphoreHandle_t mutex; -} simple_arg_t; - -typedef struct { - esp_event_handler_instance_t *context; - void* data; -} instance_unregister_data_t; - -typedef struct { - int *arr; - int index; -} ordered_data_t; - -static BaseType_t s_test_core_id; -static UBaseType_t s_test_priority; - -ESP_EVENT_DECLARE_BASE(s_test_base1); -ESP_EVENT_DECLARE_BASE(s_test_base2); -ESP_EVENT_DECLARE_BASE(s_semphr_base); - -ESP_EVENT_DEFINE_BASE(s_test_base1); -ESP_EVENT_DEFINE_BASE(s_test_base2); -ESP_EVENT_DEFINE_BASE(s_semphr_base); - -enum { - TEST_EVENT_BASE1_EV1, - TEST_EVENT_BASE1_EV2, - TEST_EVENT_BASE1_EV3, - TEST_EVENT_BASE1_MAX -}; - -enum { - TEST_EVENT_BASE2_EV1, - TEST_EVENT_BASE2_EV2, - TEST_EVENT_BASE2_MAX -}; - -extern void set_leak_threshold(int threshold); - -static BaseType_t test_event_get_core(void) -{ - static int calls = 0; - - if (portNUM_PROCESSORS > 1) { - return (s_test_core_id + calls++) % portNUM_PROCESSORS; - } else { - return s_test_core_id; - } -} - -static esp_event_loop_args_t test_event_get_default_loop_args(void) -{ - esp_event_loop_args_t loop_config = { - .queue_size = CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE, - .task_name = "loop", - .task_priority = s_test_priority, - .task_stack_size = 2048, - .task_core_id = test_event_get_core() - }; - - return loop_config; -} - -static void pass_4byte_event(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - uint32_t* target = (uint32_t*) event_handler_arg; - uint32_t data = *((uint32_t*) event_data); - *target = data; -} - -static void test_event_simple_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - if (!event_handler_arg) { - return; - } - simple_arg_t* arg = (simple_arg_t*) event_handler_arg; - xSemaphoreTake(arg->mutex, portMAX_DELAY); - - int* count = (int*) arg->data; - - if (event_data == NULL) { - (*count)++; - } else { - (*count) += *((int*) event_data); - } - - xSemaphoreGive(arg->mutex); -} - -static void post_sem(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - SemaphoreHandle_t *sem = (SemaphoreHandle_t*) event_handler_arg; - xSemaphoreGive(*sem); -} - -static void test_event_ordered_dispatch(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - int *arg = (int*) event_handler_arg; - ordered_data_t *data = *((ordered_data_t**) (event_data)); - - data->arr[data->index++] = *arg; -} - -static void test_event_performance_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - performance_data_t* data = (performance_data_t*) event_handler_arg; - - data->performed++; - - if (data->performed >= data->expected) { - xSemaphoreGive(data->done); - } -} - -static void test_event_post_task(void* args) -{ - task_arg_t* arg = (task_arg_t*) args; - post_event_data_t* data = arg->data; - - xSemaphoreTake(arg->start, portMAX_DELAY); - - for (int i = 0; i < data->num; i++) { - TEST_ESP_OK(esp_event_post_to(data->loop, data->base, data->id, NULL, 0, portMAX_DELAY)); - vTaskDelay(1); - } - - xSemaphoreGive(arg->done); - - vTaskDelete(NULL); -} - -static void test_event_simple_handler_registration_task(void* args) -{ - task_arg_t* arg = (task_arg_t*) args; - handler_registration_data_t* data = (handler_registration_data_t*) arg->data; - - xSemaphoreTake(arg->start, portMAX_DELAY); - - for(int i = 0; i < data->num; i++) { - if (data->is_registration) { - TEST_ESP_OK(esp_event_handler_register_with(data->loop, data->base, data->id, data->handles[i], NULL)); - } else { - TEST_ESP_OK(esp_event_handler_unregister_with(data->loop, data->base, data->id, data->handles[i])); - } - vTaskDelay(1); - } - - xSemaphoreGive(arg->done); - - vTaskDelete(NULL); -} - -static void test_handler_post_w_task(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - simple_arg_t* arg = (simple_arg_t*) event_handler_arg; - - esp_event_loop_handle_t* loop = (esp_event_loop_handle_t*) event_data; - int* count = (int*) arg->data; - - (*count)++; - - if (*count <= 2) { - if (event_base == s_test_base1 && event_id == TEST_EVENT_BASE1_EV1) { - TEST_ESP_OK(esp_event_post_to(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - } else{ - xSemaphoreGive((SemaphoreHandle_t) arg->mutex); - } - } else { - // Test that once the queue is full and the handler attempts to post to the same loop, - // posting does not block indefinitely. - if (event_base == s_test_base1 && event_id == TEST_EVENT_BASE1_EV1) { - xSemaphoreTake((SemaphoreHandle_t) arg->mutex, portMAX_DELAY); - TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - } - } -} - -static void test_handler_post_wo_task(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - simple_arg_t* arg = (simple_arg_t*) event_handler_arg; - - esp_event_loop_handle_t* loop = (esp_event_loop_handle_t*) event_data; - int* count = (int*) arg->data; - - (*count)++; - - if (*count <= 2) { - if (event_base == s_test_base1 && event_id == TEST_EVENT_BASE1_EV1) { - TEST_ESP_OK(esp_event_post_to(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - } else{ - xSemaphoreGive((SemaphoreHandle_t) arg->mutex); - } - } else { - // Test that once the queue is full and the handler attempts to post to the same loop, - // posting does not block indefinitely. - if (event_base == s_test_base1 && event_id == TEST_EVENT_BASE1_EV1) { - xSemaphoreTake((SemaphoreHandle_t) arg->mutex, portMAX_DELAY); - TEST_ESP_OK(esp_event_post_to(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - } - } -} - -static void test_handler_unregister_itself(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - esp_event_loop_handle_t* loop = (esp_event_loop_handle_t*) event_data; - int* unregistered = (int*) event_handler_arg; - - (*unregistered) += (event_base == s_test_base1 ? 0 : 10) + event_id + 1; - - // Unregister this handler for this event - TEST_ESP_OK(esp_event_handler_unregister_with(*loop, event_base, event_id, test_handler_unregister_itself)); -} - -static void test_handler_instance_unregister_itself(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - esp_event_loop_handle_t* loop = (esp_event_loop_handle_t*) event_data; - instance_unregister_data_t *unregister_data = (instance_unregister_data_t*) event_handler_arg; - esp_event_handler_instance_t *context = (esp_event_handler_instance_t*) unregister_data->context; - int *count = (int*) unregister_data->data; - - (*count)++; - - // Unregister this handler for this event - TEST_ESP_OK(esp_event_handler_instance_unregister_with(*loop, event_base, event_id, *context)); -} - -static void test_post_from_handler_loop_task(void* args) -{ - esp_event_loop_handle_t event_loop = (esp_event_loop_handle_t) args; - - while(1) { - TEST_ESP_OK(esp_event_loop_run(event_loop, portMAX_DELAY)); - } -} - -TEST_CASE("can create and delete event loops", "[event]") -{ - /* this test aims to verify that: - * - creating loops with and without a task succeeds - * - event queue can accomodate the set queue size, and drops the post when exceeded - * - deleting loops with unconsumed posts and unregistered handlers (when unregistration is enabled) does not leak memory */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop1; // with dedicated task - esp_event_loop_handle_t loop2; // without dedicated task - esp_event_loop_handle_t loop3; // with leftover post and handlers - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop1)); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop2)); - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop3)); - - TEST_ESP_OK(esp_event_handler_register_with(loop3, s_test_base1, TEST_EVENT_BASE1_EV1, (void*) 0x00000001, NULL)); - TEST_ESP_OK(esp_event_handler_register_with(loop3, s_test_base1, TEST_EVENT_BASE1_EV2, (void*) 0x00000002, NULL)); - TEST_ESP_OK(esp_event_handler_register_with(loop3, s_test_base2, TEST_EVENT_BASE1_EV1, (void*) 0x00000003, NULL)); - - for (int i = 0; i < loop_args.queue_size; i++) { - int mod = i % 4; - - switch(mod) { - case 0: - TEST_ESP_OK(esp_event_post_to(loop3, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - break; - case 1: - TEST_ESP_OK(esp_event_post_to(loop3, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - break; - case 2: - TEST_ESP_OK(esp_event_post_to(loop3, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - break; - case 3: - TEST_ESP_OK(esp_event_post_to(loop3, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - break; - default: - break; - } - } - - TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop3, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, pdMS_TO_TICKS(10))); - - TEST_ESP_OK(esp_event_loop_delete(loop1)); - TEST_ESP_OK(esp_event_loop_delete(loop2)); - TEST_ESP_OK(esp_event_loop_delete(loop3)); - - TEST_TEARDOWN(); -} - -TEST_CASE("registering event handler instance without instance context works", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count_1 = 0; - - simple_arg_t arg_1 = { - .data = &count_1, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg_1, NULL)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(1, count_1); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg_1.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("registering event twice with same handler yields updated handler arg", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - /* Register the handler twice to the same base and id but with a different argument (expects to return ESP_OK and log a warning) - * This aims to verify: 1) Handler's argument to be updated - * 2) Registration not to leak memory - */ - TEST_ESP_OK(esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, NULL)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - // exec loop, no handler data: count stays 0 - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // overriding the former registration of the same event - TEST_ESP_OK(esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - // exec loop, registered handler data exists: count increases - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("registering event handler instance twice works", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count_1 = 0; - int count_2 = 0; - - simple_arg_t arg_1 = { - .data = &count_1, - .mutex = xSemaphoreCreateMutex() - }; - - simple_arg_t arg_2 = { - .data = &count_2, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_handler_instance_t ctx_1; - esp_event_handler_instance_t ctx_2; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg_1, &ctx_1)); - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg_2, &ctx_2)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(1, count_1); - TEST_ASSERT_EQUAL(1, count_2); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg_1.mutex); - vSemaphoreDelete(arg_2.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("registering with ANY_BASE but specific ID fails", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(0, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("registering instance with ANY_BASE but specific ID fails", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - esp_event_handler_instance_t ctx; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_instance_register_with(loop, ESP_EVENT_ANY_BASE, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg, &ctx)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(0, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("Check registering ANY_ID", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg)); - - // handler shouldn't be triggered with different base - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // for all events with correct base, it should be triggered - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(2, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("Check registering instance with ANY_ID", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_handler_instance_t ctx; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, s_test_base1, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg, &ctx)); - - // handler shouldn't be triggered with different base - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // for all events with correct base, it should be triggered - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(2, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("Check registering specific event", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); - - // handler should not be triggered with different base - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // for incorrect id, it should not be triggered - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // for correct event and base, it should be triggered - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("Check registering instance with specific event", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_handler_instance_t ctx; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg, &ctx)); - - // handler should not be triggered with different base - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // for incorrect id, it should not be triggered - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // for correct event and base, it should be triggered - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("Specific event is not called when no correct events are posted", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); - - // handler should not be triggered by any of these postings - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // Post unknown events. - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_MAX, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_MAX, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("Specific event instance is not called when no correct events are posted", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_handler_instance_t ctx; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg, &ctx)); - - // handler should not be triggered by any of these postings - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - // Post unknown events. - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_MAX, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_MAX, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(0, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("can register/unregister handlers for all events/all events for a specific base", "[event]") -{ - /* this test aims to verify that handlers can be registered to be called on all events - * or for all events with specific bases */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - SemaphoreHandle_t sem = xSemaphoreCreateBinary(); - TEST_ASSERT(sem); - - /* Register the handler twice to the same base and id but with a different argument (expects to return ESP_OK and log a warning) - * This aims to verify: 1) Handler's argument to be updated - * 2) Registration not to leak memory - */ - TEST_ESP_OK(esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, NULL)); - TEST_ESP_OK(esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg)); - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, ESP_EVENT_ANY_ID, test_event_simple_handler, &arg)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler, &arg)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); - - // Will only be called after all the main tests have finished - TEST_ESP_OK(esp_event_handler_register_with(loop, s_semphr_base, TEST_EVENT_BASE1_EV3, post_sem, &sem)); - - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_post_to(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, NULL, 0, portMAX_DELAY)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_post_to(loop, s_test_base1, ESP_EVENT_ANY_ID, NULL, 0, portMAX_DELAY)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_post_to(loop, ESP_EVENT_ANY_BASE, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); // exec loop, base and id level (+3) - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); // exec loop, base and id level (+3) - - // Post unknown events. Respective loop level and base level handlers should still execute. - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_MAX, NULL, 0, portMAX_DELAY)); // exec loop and base level (+2) - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_MAX, NULL, 0, portMAX_DELAY)); // exec loop level (+1) - TEST_ESP_OK(esp_event_post_to(loop, s_semphr_base, TEST_EVENT_BASE1_EV3, NULL, 0, portMAX_DELAY)); - - while (xSemaphoreTake(sem, 0) != pdTRUE) { - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - } - - TEST_ASSERT_EQUAL(10, count); // 3 + 3 + 2 + 1 + 1 - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - vSemaphoreDelete(sem); - - TEST_TEARDOWN(); -} - -TEST_CASE("can unregister handler", "[event]") -{ - /* this test aims to verify that unregistered handlers no longer execute when events are raised */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - SemaphoreHandle_t sem = xSemaphoreCreateBinary(); - TEST_ASSERT(sem); - - uint32_t handler_0_record; - uint32_t handler_1_record; - - uint32_t a = 0xA; - uint32_t b = 0xB; - uint32_t c = 0xC; - uint32_t d = 0xD; - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, pass_4byte_event, &handler_0_record)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE1_EV1, pass_4byte_event, &handler_1_record)); - - // Will only be called after all the main tests have finished - TEST_ESP_OK(esp_event_handler_register_with(loop, s_semphr_base, TEST_EVENT_BASE1_EV3, post_sem, &sem)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &a, sizeof(uint32_t), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, &b, sizeof(uint32_t), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_semphr_base, TEST_EVENT_BASE1_EV3, &b, sizeof(uint32_t), portMAX_DELAY)); - - while (xSemaphoreTake(sem, 0) != pdTRUE) { - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - } - - TEST_ASSERT_EQUAL(0xA, handler_0_record); - TEST_ASSERT_EQUAL(0xB, handler_1_record); - - TEST_ESP_OK(esp_event_handler_unregister_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, pass_4byte_event)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &c, sizeof(uint32_t), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, &d, sizeof(uint32_t), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_semphr_base, TEST_EVENT_BASE1_EV3, &b, sizeof(uint32_t), portMAX_DELAY)); - - while (xSemaphoreTake(sem, 0) != pdTRUE) { - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - } - - - TEST_ASSERT_EQUAL(0xA, handler_0_record); - TEST_ASSERT_EQUAL(0xD, handler_1_record); - - vSemaphoreDelete(sem); - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -TEST_CASE("can unregister handler instance", "[event]") -{ - /* this test aims to verify that unregistered handlers no longer execute when events are raised */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_handler_instance_t ctx; - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg, &ctx)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_handler_instance_unregister_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, ctx)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("handler can unregister itself", "[event]") -{ - /* this test aims to verify that handlers can unregister themselves */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - int unregistered = 0; - - /* - * s_test_base1, ev1 = 1 - * s_test_base1, ev2 = 2 - * s_test_base2, ev1 = 11 - * s_test_base2, ev2 = 12 - */ - int expected_unregistered = 0; - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_handler_unregister_itself, &unregistered)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_handler_unregister_itself, &unregistered)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV1, test_handler_unregister_itself, &unregistered)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV2, test_handler_unregister_itself, &unregistered)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - expected_unregistered = 2; // base1, ev2 - TEST_ASSERT_EQUAL(expected_unregistered, unregistered); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_EV1, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - expected_unregistered += 1 + 11; // base1, ev1 + base2, ev1 - TEST_ASSERT_EQUAL(expected_unregistered, unregistered); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_EV2, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - expected_unregistered += 12; // base2, ev2 - TEST_ASSERT_EQUAL(expected_unregistered, unregistered); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_EV1, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_EV2, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(expected_unregistered, unregistered); // all handlers unregistered - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -TEST_CASE("handler instance can unregister itself", "[event]") -{ - /* this test aims to verify that handlers can unregister themselves */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - esp_event_handler_instance_t ctx; - - int count = 0; - - instance_unregister_data_t instance_data = { - .context = &ctx, - .data = &count - }; - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_handler_instance_unregister_itself, &instance_data, &ctx)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &loop, sizeof(loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - TEST_ASSERT_EQUAL(1, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -// Ignore this test on QEMU for now since it relies on esp_timer which is based on the host run time on ESP32-QEMU -TEST_CASE("can exit running loop at approximately the set amount of time", "[event][qemu-ignore]") -{ - /* this test aims to verify that running loop does not block indefinitely in cases where - * events are posted frequently */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - performance_data_t handler_data = { - .performed = 0, - .expected = INT32_MAX, - .done = xSemaphoreCreateBinary() - }; - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_performance_handler, &handler_data)); - - post_event_data_t post_event_data = { - .base = s_test_base1, - .id = TEST_EVENT_BASE1_EV1, - .loop = loop, - .num = INT32_MAX - }; - - task_arg_t post_event_arg = { - .data = &post_event_data, - .done = xSemaphoreCreateBinary(), - .start = xSemaphoreCreateBinary() - }; - - TaskHandle_t post_task; - - xTaskCreatePinnedToCore(test_event_post_task, "post", 2048, &post_event_arg, s_test_priority, &post_task, test_event_get_core()); - - int runtime_ms = 10; - int runtime_us = runtime_ms * 1000; - - int64_t start, diff; - start = esp_timer_get_time(); - - xSemaphoreGive(post_event_arg.start); - - // Run the loop for the runtime_ms set amount of time, regardless of whether events - // are still being posted to the loop. - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(runtime_ms))); - - diff = (esp_timer_get_time() - start); - - // Threshold is 25 percent. - TEST_ASSERT_LESS_THAN_INT(runtime_us * 1.25f, diff); - - // Verify that the post task still continues - TEST_ASSERT_NOT_EQUAL(pdTRUE, xSemaphoreTake(post_event_arg.done, pdMS_TO_TICKS(10))); - - vSemaphoreDelete(post_event_arg.done); - vSemaphoreDelete(post_event_arg.start); - vSemaphoreDelete(handler_data.done); - vTaskDelete(post_task); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -TEST_CASE("can register/unregister handlers simultaneously", "[event]") -{ - /* this test aims to verify that the event handlers list remains consistent despite - * simultaneous access by differenct tasks */ - - TEST_SETUP(); - - const char* base = "base"; - int32_t id = 0; - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - ESP_LOGI(TAG, "registering handlers"); - - handler_registration_data_t* registration_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*registration_data)); - task_arg_t* registration_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*registration_arg)); - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - registration_data[i].base = base; - registration_data[i].id = id; - registration_data[i].loop = loop; - registration_data[i].handles = calloc(TEST_CONFIG_ITEMS_TO_REGISTER, sizeof(esp_event_handler_t)); - registration_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; - registration_data[i].is_registration = true; - - for (int j = 0; j < TEST_CONFIG_ITEMS_TO_REGISTER; j++) { - registration_data[i].handles[j] = (void*) (i * TEST_CONFIG_ITEMS_TO_REGISTER) + (j + TEST_CONFIG_ITEMS_TO_REGISTER); - } - - registration_arg[i].start = xSemaphoreCreateBinary(); - registration_arg[i].done = xSemaphoreCreateBinary(); - registration_arg[i].data = ®istration_data[i]; - - xTaskCreatePinnedToCore(test_event_simple_handler_registration_task, "register", 2048, ®istration_arg[i], s_test_priority, NULL, test_event_get_core()); - } - - // Give the semaphores to the spawned registration task - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreGive(registration_arg[i].start); - } - - // Take the same semaphores in order to proceed - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreTake(registration_arg[i].done, portMAX_DELAY); - } - - ESP_LOGI(TAG, "checking consistency of handlers list"); - - // Check consistency of events list - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - for (int j = 0; j < TEST_CONFIG_ITEMS_TO_REGISTER; j++) { - TEST_ASSERT_TRUE(esp_event_is_handler_registered(loop, base, id, registration_data[i].handles[j])); - } - } - - ESP_LOGI(TAG, "unregistering handlers"); - - /* Test if tasks can unregister simultaneously */ - - // Unregister registered events - handler_registration_data_t* unregistration_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*unregistration_data)); - task_arg_t* unregistration_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*unregistration_arg)); - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - unregistration_data[i].base = base; - unregistration_data[i].id = id; - unregistration_data[i].loop = loop; - unregistration_data[i].handles = calloc(TEST_CONFIG_ITEMS_TO_REGISTER, sizeof(esp_event_handler_t)); - unregistration_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; - unregistration_data[i].is_registration = false; - - memcpy(unregistration_data[i].handles, registration_data[i].handles, TEST_CONFIG_ITEMS_TO_REGISTER * sizeof(esp_event_handler_t)); - - unregistration_arg[i].data = &unregistration_data[i]; - unregistration_arg[i].start = xSemaphoreCreateBinary(); - unregistration_arg[i].done = xSemaphoreCreateBinary(); - - xTaskCreatePinnedToCore(test_event_simple_handler_registration_task, "unregister", 2048, &unregistration_arg[i], s_test_priority, NULL, test_event_get_core()); - } - - // Give the semaphores to the spawned unregistration task - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreGive(unregistration_arg[i].start); - } - - // Take the same semaphores in order to proceed - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreTake(unregistration_arg[i].done, portMAX_DELAY); - } - - ESP_LOGI(TAG, "checking consistency of handlers list"); - - // Check consistency of events list - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - for (int j = 0; j < TEST_CONFIG_ITEMS_TO_REGISTER; j++) { - TEST_ASSERT_FALSE(esp_event_is_handler_registered(loop, base, id, registration_data[i].handles[j])); - } - } - - // Do cleanup - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - free(registration_data[i].handles); - vSemaphoreDelete(registration_arg[i].start); - vSemaphoreDelete(registration_arg[i].done); - - free(unregistration_data[i].handles); - vSemaphoreDelete(unregistration_arg[i].start); - vSemaphoreDelete(unregistration_arg[i].done); - } - - free(registration_data); - free(unregistration_data); - free(registration_arg); - free(unregistration_arg); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -TEST_CASE("posting ANY_EVENT or ANY_ID fails", "[event]") { - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_post_to(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, NULL, 0, portMAX_DELAY)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_post_to(loop, s_test_base1, ESP_EVENT_ANY_ID, NULL, 0, portMAX_DELAY)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_post_to(loop, ESP_EVENT_ANY_BASE, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(0, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("can post and run events", "[event]") -{ - /* this test aims to verify that: - * - multiple tasks can post to the queue simultaneously - * - handlers recieve the appropriate handler arg and associated event data */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - loop_args.queue_size = TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); - - post_event_data_t* post_event_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_data)); - task_arg_t* post_event_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_arg)); - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) - { - post_event_data[i].base = s_test_base1; - post_event_data[i].id = TEST_EVENT_BASE1_EV1; - post_event_data[i].loop = loop; - post_event_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; - - post_event_arg[i].data = &post_event_data[i]; - post_event_arg[i].start = xSemaphoreCreateBinary(); - post_event_arg[i].done = xSemaphoreCreateBinary(); - - xTaskCreatePinnedToCore(test_event_post_task, "post", 2048, &post_event_arg[i], s_test_priority, NULL, test_event_get_core()); - } - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreGive(post_event_arg[i].start); - } - - // Execute some events as they are posted - for (int i = 0; i < (TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER) / 2; i++) { - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - } - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreTake(post_event_arg[i].done, portMAX_DELAY); - } - - // Execute the rest - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER, count); - - // Cleanup - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - vSemaphoreDelete(post_event_arg[i].start); - vSemaphoreDelete(post_event_arg[i].done); - } - - free(post_event_data); - free(post_event_arg); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("can post and run events with instances", "[event]") -{ - /* this test aims to verify that: - * - multiple tasks can post to the queue simultaneously - * - handlers recieve the appropriate handler arg and associated event data */ - - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - loop_args.queue_size = TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - int count = 0; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateMutex() - }; - - esp_event_handler_instance_t ctx; - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg, &ctx)); - - post_event_data_t* post_event_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_data)); - task_arg_t* post_event_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_arg)); - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) - { - post_event_data[i].base = s_test_base1; - post_event_data[i].id = TEST_EVENT_BASE1_EV1; - post_event_data[i].loop = loop; - post_event_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; - - post_event_arg[i].data = &post_event_data[i]; - post_event_arg[i].start = xSemaphoreCreateBinary(); - post_event_arg[i].done = xSemaphoreCreateBinary(); - - xTaskCreatePinnedToCore(test_event_post_task, "post", 2048, &post_event_arg[i], s_test_priority, NULL, test_event_get_core()); - } - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreGive(post_event_arg[i].start); - } - - // Execute some events as they are posted - for (int i = 0; i < (TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER) / 2; i++) { - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - } - - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - xSemaphoreTake(post_event_arg[i].done, portMAX_DELAY); - } - - // Execute the rest - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER, count); - - // Cleanup - for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { - vSemaphoreDelete(post_event_arg[i].start); - vSemaphoreDelete(post_event_arg[i].done); - } - - free(post_event_data); - free(post_event_arg); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -static void loop_run_task(void* args) -{ - esp_event_loop_handle_t event_loop = (esp_event_loop_handle_t) args; - - while(1) { - esp_event_loop_run(event_loop, portMAX_DELAY); - } -} - -static void performance_test(bool dedicated_task) -{ - // rand() seems to do a one-time allocation. Call it here so that the memory it allocates - // is not counted as a leak. - unsigned int _rand __attribute__((unused)) = rand(); - - TEST_SETUP(); - - const char test_base[] = "qwertyuiopasdfghjklzxvbnmmnbvcxz"; - - #define TEST_CONFIG_BASES (sizeof(test_base) - 1) - #define TEST_CONFIG_IDS (TEST_CONFIG_BASES / 2) - - // Create loop - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - esp_event_loop_handle_t loop; - - if (!dedicated_task) { - loop_args.task_name = NULL; - } - - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - performance_data_t data; - - // Register the handlers - for (int base = 0; base < TEST_CONFIG_BASES; base++) { - for (int id = 0; id < TEST_CONFIG_IDS; id++) { - TEST_ESP_OK(esp_event_handler_register_with(loop, test_base + base, id, test_event_performance_handler, &data)); - } - } - - TaskHandle_t mtask = NULL; - - if (!dedicated_task) { - xTaskCreate(loop_run_task, "loop_run", loop_args.task_stack_size, (void*) loop, loop_args.task_priority, &mtask); - } - - // Perform performance test - float running_sum = 0; - float running_count = 0; - - for (int bases = 1; bases <= TEST_CONFIG_BASES; bases *= 2) { - for (int ids = 1; ids <= TEST_CONFIG_IDS; ids *= 2) { - - data.performed = 0; - data.expected = bases * ids; - data.done = xSemaphoreCreateBinary(); - - // Generate randomized list of posts - int post_bases[TEST_CONFIG_BASES]; - int post_ids[TEST_CONFIG_IDS]; - - for (int i = 0; i < bases; i++) { - post_bases[i] = i; - } - - for (int i = 0; i < ids; i++) { - post_ids[i] = i; - } - - for (int i = 0; i < bases; i++) { - int rand_a = rand() % bases; - int rand_b = rand() % bases; - - int temp = post_bases[rand_a]; - post_bases[rand_a]= post_bases[rand_b]; - post_bases[rand_b] = temp; - } - - for (int i = 0; i < ids; i++) { - int rand_a = rand() % ids; - int rand_b = rand() % ids; - - int temp = post_ids[rand_a]; - post_ids[rand_a]= post_ids[rand_b]; - post_ids[rand_b] = temp; - } - - // Post the events - int64_t start = esp_timer_get_time(); - for (int base = 0; base < bases; base++) { - for (int id = 0; id < ids; id++) { - TEST_ESP_OK(esp_event_post_to(loop, test_base + post_bases[base], post_ids[id], NULL, 0, portMAX_DELAY)); - } - } - - xSemaphoreTake(data.done, portMAX_DELAY); - int64_t elapsed = esp_timer_get_time() - start; - - // Record data - TEST_ASSERT_EQUAL(data.expected, data.performed); - - running_count++; - running_sum += data.performed / (elapsed / (1000000.0)); - - vSemaphoreDelete(data.done); - } - } - - int average = (int) (running_sum / (running_count)); - - if (!dedicated_task) { - ((esp_event_loop_instance_t*) loop)->task = mtask; - } - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); - -#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING - ESP_LOGI(TAG, "events dispatched/second with profiling enabled: %d", average); - // Enabling profiling will slow down event dispatch, so the set threshold - // is not valid when it is enabled. -#else -#ifndef CONFIG_SPIRAM - TEST_PERFORMANCE_GREATER_THAN(EVENT_DISPATCH, "%d", average); -#else - TEST_PERFORMANCE_GREATER_THAN(EVENT_DISPATCH_PSRAM, "%d", average); -#endif // CONFIG_SPIRAM -#endif // CONFIG_ESP_EVENT_LOOP_PROFILING -} - -TEST_CASE("performance test - dedicated task", "[event][qemu-ignore]") -{ - performance_test(true); -} - -TEST_CASE("performance test - no dedicated task", "[event][qemu-ignore]") -{ - performance_test(false); -} - -static SemaphoreHandle_t pending_sem = NULL; - -static void test_handler_pending(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - xSemaphoreGive(pending_sem); -} - -TEST_CASE("can post to loop from handler - dedicated task", "[event][long]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop_w_task; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - int count; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateBinary() - }; - - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop_w_task)); - - pending_sem = xSemaphoreCreateCounting(loop_args.queue_size, 0); - count = 0; - - TEST_ESP_OK(esp_event_handler_register_with(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV1, test_handler_post_w_task, &arg)); - TEST_ESP_OK(esp_event_handler_register_with(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV2, test_handler_post_w_task, &arg)); - TEST_ESP_OK(esp_event_handler_register_with(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV3, test_handler_pending, &arg)); - - // Test that a handler can post to a different loop while there is still slots on the queue - TEST_ESP_OK(esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_w_task, sizeof(&loop_w_task), portMAX_DELAY)); - - xSemaphoreTake(arg.mutex, portMAX_DELAY); - - TEST_ASSERT_EQUAL(2, count); - - // Test that other tasks can still post while there is still slots in the queue, while handler is executing - count = 100; - - TEST_ESP_OK(esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_w_task, sizeof(&loop_w_task), portMAX_DELAY)); - - for (int i = 0; i < loop_args.queue_size; i++) { - TEST_ESP_OK(esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV3, NULL, 0, portMAX_DELAY)); - } - - TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, - pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME))); - - xSemaphoreGive(arg.mutex); - - for (int i = 0; i < loop_args.queue_size; i++) { - TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(pending_sem, pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME))); - } - - TEST_ESP_OK(esp_event_loop_delete(loop_w_task)); - - vSemaphoreDelete(pending_sem); - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("can post to loop from handler instance - dedicated task", "[event][long]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop_w_task; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - pending_sem = xSemaphoreCreateCounting(loop_args.queue_size, 0); - int count; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateBinary() - }; - - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop_w_task)); - - count = 0; - - esp_event_handler_instance_t ctx_1; - esp_event_handler_instance_t ctx_2; - esp_event_handler_instance_t ctx_3; - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV1, test_handler_post_w_task, &arg, &ctx_1)); - TEST_ESP_OK(esp_event_handler_instance_register_with(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV2, test_handler_post_w_task, &arg, &ctx_2)); - TEST_ESP_OK(esp_event_handler_instance_register_with(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV3, test_handler_pending, &arg, &ctx_3)); - - // Test that a handler can post to a different loop while there is still slots on the queue - TEST_ESP_OK(esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_w_task, sizeof(&loop_w_task), portMAX_DELAY)); - - xSemaphoreTake(arg.mutex, portMAX_DELAY); - - TEST_ASSERT_EQUAL(2, count); - - // Test that other tasks can still post while there is still slots in the queue, while handler is executing - count = 100; - - TEST_ESP_OK(esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_w_task, sizeof(&loop_w_task), portMAX_DELAY)); - - for (int i = 0; i < loop_args.queue_size; i++) { - TEST_ESP_OK(esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV3, NULL, 0, portMAX_DELAY)); - } - - TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop_w_task, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, - pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME))); - - xSemaphoreGive(arg.mutex); - - for (int i = 0; i < loop_args.queue_size; i++) { - TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(pending_sem, pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME))); - } - - TEST_ESP_OK(esp_event_loop_delete(loop_w_task)); - - vSemaphoreDelete(pending_sem); - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("can post to loop from handler - no dedicated task", "[event][long]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop_wo_task; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - int count; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateBinary() - }; - - count = 0; - - loop_args.queue_size = 1; - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop_wo_task)); - - TaskHandle_t mtask; - - xTaskCreate(test_post_from_handler_loop_task, "task", 2584, (void*) loop_wo_task, s_test_priority, &mtask); - - TEST_ESP_OK(esp_event_handler_register_with(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV1, test_handler_post_wo_task, &arg)); - TEST_ESP_OK(esp_event_handler_register_with(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV2, test_handler_post_wo_task, &arg)); - - // Test that a handler can post to a different loop while there is still slots on the queue - TEST_ESP_OK(esp_event_post_to(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_wo_task, sizeof(&loop_wo_task), portMAX_DELAY)); - - xSemaphoreTake(arg.mutex, portMAX_DELAY); - - TEST_ASSERT_EQUAL(2, count); - - count = 100; - - TEST_ESP_OK(esp_event_post_to(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_wo_task, sizeof(&loop_wo_task), portMAX_DELAY)); - - vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME)); - - // For loop without tasks, posting is more restrictive. Posting should wait until execution of handler finishes - TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, - pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME))); - - xSemaphoreGive(arg.mutex); - - vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME)); - - vTaskDelete(mtask); - - TEST_ESP_OK(esp_event_loop_delete(loop_wo_task)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -TEST_CASE("can post to loop from handler instance - no dedicated task", "[event][long]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop_wo_task; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - int count; - - simple_arg_t arg = { - .data = &count, - .mutex = xSemaphoreCreateBinary() - }; - - count = 0; - - esp_event_handler_instance_t ctx_1; - esp_event_handler_instance_t ctx_2; - - loop_args.queue_size = 1; - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop_wo_task)); - - TaskHandle_t mtask; - - xTaskCreate(test_post_from_handler_loop_task, "task", 2584, (void*) loop_wo_task, s_test_priority, &mtask); - - TEST_ESP_OK(esp_event_handler_instance_register_with(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV1, test_handler_post_wo_task, &arg, &ctx_1)); - TEST_ESP_OK(esp_event_handler_instance_register_with(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV2, test_handler_post_wo_task, &arg, &ctx_2)); - - // Test that a handler can post to a different loop while there is still slots on the queue - TEST_ESP_OK(esp_event_post_to(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_wo_task, sizeof(&loop_wo_task), portMAX_DELAY)); - - xSemaphoreTake(arg.mutex, portMAX_DELAY); - - TEST_ASSERT_EQUAL(2, count); - - count = 100; - - TEST_ESP_OK(esp_event_post_to(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV1, &loop_wo_task, sizeof(&loop_wo_task), portMAX_DELAY)); - - vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME)); - - // For loop without tasks, posting is more restrictive. Posting should wait until execution of handler finishes - TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop_wo_task, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, - pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME))); - - xSemaphoreGive(arg.mutex); - - vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_WAIT_TIME)); - - vTaskDelete(mtask); - - TEST_ESP_OK(esp_event_loop_delete(loop_wo_task)); - - vSemaphoreDelete(arg.mutex); - - TEST_TEARDOWN(); -} - -static void test_event_simple_handler_template(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) -{ - int* count = (int*) handler_arg; - (*count)++; -} - -static void test_event_simple_handler_1(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) -{ - test_event_simple_handler_template(handler_arg, base, id, event_arg); -} - -static void test_event_simple_handler_3(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) -{ - test_event_simple_handler_template(handler_arg, base, id, event_arg); -} - -static void test_event_simple_handler_2(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) -{ - test_event_simple_handler_template(handler_arg, base, id, event_arg); -} - -static void test_registration_from_handler_hdlr(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) -{ - esp_event_loop_handle_t* loop = (esp_event_loop_handle_t*) event_arg; - TEST_ESP_OK(esp_event_handler_register_with(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler_1, handler_arg)); - TEST_ESP_OK(esp_event_handler_register_with(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler_2, handler_arg)); - TEST_ESP_OK(esp_event_handler_register_with(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler_3, handler_arg)); -} - -static void test_unregistration_from_handler_hdlr(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) -{ - esp_event_loop_handle_t* loop = (esp_event_loop_handle_t*) event_arg; - TEST_ESP_OK(esp_event_handler_unregister_with(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler_1)); - TEST_ESP_OK(esp_event_handler_unregister_with(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler_2)); - TEST_ESP_OK(esp_event_handler_unregister_with(*loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_simple_handler_3)); -} - -TEST_CASE("can register from handler", "[event]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - int count = 0; - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_registration_from_handler_hdlr, &count)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV1, test_unregistration_from_handler_hdlr, &count)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &loop, sizeof(&loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(3, count); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE2_EV1, &loop, sizeof(&loop), portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ASSERT_EQUAL(3, count); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -static void test_create_loop_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data) -{ - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - if (id == TEST_EVENT_BASE1_EV1) { - TEST_ESP_OK(esp_event_loop_create(&loop_args, (esp_event_loop_handle_t*) handler_args)); - } else { - TEST_ESP_OK(esp_event_loop_delete(*((esp_event_loop_handle_t*) handler_args))); - } -} - -TEST_CASE("can create and delete loop from handler", "[event]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_handle_t test_loop; - - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_create_loop_handler, &test_loop)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_create_loop_handler, &test_loop)); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -TEST_CASE("events are dispatched in the order they are registered", "[event]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - int id_arr[7]; - - for (int i = 0; i < 7; i++) { - id_arr[i] = i; - } - - int data_arr[12] = {0}; - - SemaphoreHandle_t sem = xSemaphoreCreateBinary(); - TEST_ASSERT(sem); - - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV1, test_event_ordered_dispatch, id_arr + 0)); - TEST_ESP_OK(esp_event_handler_register_with(loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_event_ordered_dispatch, id_arr + 1)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, ESP_EVENT_ANY_ID, test_event_ordered_dispatch, id_arr + 2)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE2_EV2, test_event_ordered_dispatch, id_arr + 3)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_ordered_dispatch, id_arr + 4)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, ESP_EVENT_ANY_ID, test_event_ordered_dispatch, id_arr + 5)); - TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV2, test_event_ordered_dispatch, id_arr + 6)); - - // Will only be called after all the main tests have finished - TEST_ESP_OK(esp_event_handler_register_with(loop, s_semphr_base, TEST_EVENT_BASE1_EV3, post_sem, &sem)); - - esp_event_dump(stdout); - - ordered_data_t data = { - .arr = data_arr, - .index = 0 - }; - - ordered_data_t* dptr = &data; - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV2, &dptr, sizeof(dptr), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &dptr, sizeof(dptr), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV2, &dptr, sizeof(dptr), portMAX_DELAY)); - TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, &dptr, sizeof(dptr), portMAX_DELAY)); - - // This is just to signal that all test handlers have run - TEST_ESP_OK(esp_event_post_to(loop, s_semphr_base, TEST_EVENT_BASE1_EV3, &dptr, sizeof(dptr), portMAX_DELAY)); - - while (xSemaphoreTake(sem, 0) != pdTRUE) { - TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); - } - - // Expected data executing the posts above - int ref_arr[13] = {1, 3, 5, 1, 2, 4, 1, 2, 6, 0, 1, 5, 0}; // the last element is not checked anymore because - // we send one event to post the semaphore - - TEST_ASSERT_EQUAL_INT_ARRAY(ref_arr, data_arr, 12); - - vSemaphoreDelete(sem); - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -#if CONFIG_ESP_EVENT_POST_FROM_ISR -TEST_CASE("can properly prepare event data posted to loop", "[event]") -{ - TEST_SETUP(); - - esp_event_loop_handle_t loop; - esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); - - loop_args.task_name = NULL; - TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); - - esp_event_post_instance_t post; - esp_event_loop_instance_t* loop_def = (esp_event_loop_instance_t*) loop; - - TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); - TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(loop_def->queue, &post, portMAX_DELAY)); - TEST_ASSERT_EQUAL(false, post.data_set); - TEST_ASSERT_EQUAL(false, post.data_allocated); - TEST_ASSERT_EQUAL(NULL, post.data.ptr); - - int sample = 0; - TEST_ESP_OK(esp_event_isr_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &sample, sizeof(sample), NULL)); - TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(loop_def->queue, &post, portMAX_DELAY)); - TEST_ASSERT_EQUAL(true, post.data_set); - TEST_ASSERT_EQUAL(false, post.data_allocated); - TEST_ASSERT_EQUAL(false, post.data.val); - - TEST_ESP_OK(esp_event_loop_delete(loop)); - - TEST_TEARDOWN(); -} - -static void test_handler_post_from_isr(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - SemaphoreHandle_t *sem = (SemaphoreHandle_t*) event_handler_arg; - // Event data is just the address value (maybe have been truncated due to casting). - int *data = (int*) event_data; - TEST_ASSERT_EQUAL(*data, (int) (*sem)); - xSemaphoreGive(*sem); -} - -bool test_event_on_timer_alarm(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) -{ - int data = (int)user_ctx; - gptimer_stop(timer); - // Posting events with data more than 4 bytes should fail. - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_isr_post(s_test_base1, TEST_EVENT_BASE1_EV1, &data, 5, NULL)); - // This should succeedd, as data is int-sized. The handler for the event checks that the passed event data - // is correct. - BaseType_t task_unblocked; - TEST_ESP_OK(esp_event_isr_post(s_test_base1, TEST_EVENT_BASE1_EV1, &data, sizeof(data), &task_unblocked)); - return task_unblocked == pdTRUE; -} - -TEST_CASE("can post events from interrupt handler", "[event]") -{ - TEST_ESP_OK(esp_event_loop_create_default()); - - /* Lazy allocated resources in gptimer/intr_alloc */ - set_leak_threshold(-150); - - SemaphoreHandle_t sem = xSemaphoreCreateBinary(); - gptimer_handle_t gptimer = NULL; - /* Select and initialize basic parameters of the timer */ - gptimer_config_t config = { - .clk_src = GPTIMER_CLK_SRC_DEFAULT, - .direction = GPTIMER_COUNT_UP, - .resolution_hz = 1000000, // 1MHz, 1 tick = 1us - }; - TEST_ESP_OK(gptimer_new_timer(&config, &gptimer)); - - gptimer_alarm_config_t alarm_config = { - .reload_count = 0, - .alarm_count = 500000, - }; - gptimer_event_callbacks_t cbs = { - .on_alarm = test_event_on_timer_alarm - }; - TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, sem)); - TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config)); - TEST_ESP_OK(gptimer_enable(gptimer)); - TEST_ESP_OK(gptimer_start(gptimer)); - - TEST_ESP_OK(esp_event_handler_register(s_test_base1, TEST_EVENT_BASE1_EV1, - test_handler_post_from_isr, &sem)); - - xSemaphoreTake(sem, portMAX_DELAY); - - vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); - - vSemaphoreDelete(sem); - TEST_ESP_OK(gptimer_disable(gptimer)); - TEST_ESP_OK(gptimer_del_timer(gptimer)); - - TEST_ESP_OK(esp_event_loop_delete_default()); -} - -#endif // CONFIG_ESP_EVENT_POST_FROM_ISR diff --git a/components/esp_event/test_apps/main/test_event_common.cpp b/components/esp_event/test_apps/main/test_event_common.cpp new file mode 100644 index 0000000000..9b327cd599 --- /dev/null +++ b/components/esp_event/test_apps/main/test_event_common.cpp @@ -0,0 +1,1573 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* This file contains all tests runnable on targets as well as on the host */ + +#include +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_event.h" +#include "unity.h" + +ESP_EVENT_DECLARE_BASE(s_test_base1); +ESP_EVENT_DECLARE_BASE(s_test_base2); + +ESP_EVENT_DEFINE_BASE(s_test_base1); +ESP_EVENT_DEFINE_BASE(s_test_base2); + +enum { + TEST_EVENT_BASE1_EV1, + TEST_EVENT_BASE1_EV2, + TEST_EVENT_BASE1_MAX +}; + +enum { + TEST_EVENT_BASE2_EV1, + TEST_EVENT_BASE2_MAX +}; + +static const TickType_t ZERO_DELAY = 0; + +static esp_event_loop_args_t test_event_get_default_loop_args(void) +{ + esp_event_loop_args_t loop_config = { + .queue_size = CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE, + .task_name = "loop", + .task_priority = uxTaskPriorityGet(NULL), + .task_stack_size = 2048, + .task_core_id = 0 // Linux simulator is single core only + }; + + return loop_config; +} + +struct EV_LoopFix { +public: + EV_LoopFix(size_t queue_size = CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE, const char *task_name = NULL) { + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + loop_args.task_name = task_name; + loop_args.queue_size = queue_size; + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + } + + ~EV_LoopFix() { + esp_event_loop_delete(loop); + } + + esp_event_loop_handle_t loop; +}; + +TEST_CASE("can create and delete event loop without task", "[event][linux]") +{ + esp_event_loop_handle_t loop; + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + loop_args.task_name = NULL; + + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + TEST_ESP_OK(esp_event_loop_delete(loop)); +} + +TEST_CASE("can create and delete event loop with task", "[event][linux]") +{ + esp_event_loop_handle_t loop; + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + TEST_ESP_OK(esp_event_loop_delete(loop)); +} + +TEST_CASE("can post events up to loop's max queue size", "[event][linux]") +{ + EV_LoopFix loop_fix(2); + + TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + NULL, + 0, + pdMS_TO_TICKS(10))); + TEST_ASSERT_EQUAL(ESP_OK, esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + NULL, + 0, + pdMS_TO_TICKS(10))); +} + +TEST_CASE("posting to full event loop times out", "[event][linux]") +{ + EV_LoopFix loop_fix(1); + + TEST_ASSERT_EQUAL(ESP_OK, + esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, pdMS_TO_TICKS(0))); + TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, + esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, pdMS_TO_TICKS(0))); + TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, + esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, pdMS_TO_TICKS(10))); +} + +TEST_CASE("can post to loop and run loop without registration", "[event][linux]") +{ + EV_LoopFix loop_fix; + + TEST_ASSERT_EQUAL(ESP_OK, + esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, pdMS_TO_TICKS(10))); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); +} + +static void test_handler_inc(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + int *target = (int*) event_handler_arg; + (*target)++; +} + +TEST_CASE("registering event handler instance without instance context works", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count, + NULL)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, count); +} + +/* Register the handler twice to the same base and id but with a different argument + * (expects to return ESP_OK and log a warning) + * This aims to verify: 1) Handler's argument to be updated + * 2) Registration not to leak memory + */ +TEST_CASE("registering event twice with same handler yields updated handler arg", "[event][linux]") { + EV_LoopFix loop_fix; + + int count_first = 0; + int count_second = 0; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_first)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(count_first, 1); + + // overriding the former registration of the same event + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_second)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, count_first); + TEST_ASSERT_EQUAL(1, count_second); +} + +TEST_CASE("registering event handler instance twice works", "[event][linux]") { + EV_LoopFix loop_fix; + int count_1 = 0; + int count_2 = 0; + esp_event_handler_instance_t ctx_1; + esp_event_handler_instance_t ctx_2; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_1, + &ctx_1)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_2, + &ctx_2)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, pdMS_TO_TICKS(10))); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, count_1); + TEST_ASSERT_EQUAL(1, count_2); +} + +TEST_CASE("registering with ANY_BASE but specific ID fails", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count_instance, + &ctx)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(0, count); + TEST_ASSERT_EQUAL(0, count_instance); +} + +TEST_CASE("registration of ANY_BASE and ANY_ID always called", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_instance, + &ctx)); + + // handlers should always be triggered + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + for (size_t i = 0; i < 4; i++) { + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + } + + TEST_ASSERT_EQUAL(4, count); + TEST_ASSERT_EQUAL(4, count_instance); +} + +TEST_CASE("registration of ANY_ID not called from wrong base", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_instance, + &ctx)); + + // handlers shouldn't be triggered with different base + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(0, count); + TEST_ASSERT_EQUAL(0, count_instance); +} + +TEST_CASE("registration of ANY_ID called from correct base", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_instance, + &ctx)); + + // for all events with correct base, it should be triggered + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(2, count); + TEST_ASSERT_EQUAL(2, count_instance); +} + +TEST_CASE("registering specific event posting different base and different event ID", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count_instance, + &ctx)); + + // handlers should not be triggered with different base and different ID + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(0, count); + TEST_ASSERT_EQUAL(0, count_instance); +} + +TEST_CASE("registering specific event posting different base", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count_instance, + &ctx)); + + // handlers should not be triggered with different base + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(0, count); + TEST_ASSERT_EQUAL(0, count_instance); +} + +TEST_CASE("registering specific event posting incorrect event ID", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count_instance, + &ctx)); + + // for incorrect id, it should not be triggered with different ID + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(0, count); + TEST_ASSERT_EQUAL(0, count_instance); +} + +TEST_CASE("registering specific event posting correct event and base", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count_instance, + &ctx)); + + // for correct event and base, it should be triggered + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, count); + TEST_ASSERT_EQUAL(1, count_instance); +} + +TEST_CASE("posting ANY_EVENT or ANY_ID fails", "[event][linux]") { + EV_LoopFix loop_fix; + int count = 0; + int count_instance = 0; + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count)); + + // normal and "instance" registration are decently close to each other, don't exercise all cases here + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_instance, + &ctx)); + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + esp_event_post_to(loop_fix.loop, ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, NULL, 0, portMAX_DELAY)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + esp_event_post_to(loop_fix.loop, s_test_base1, ESP_EVENT_ANY_ID, NULL, 0, portMAX_DELAY)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, + esp_event_post_to(loop_fix.loop, ESP_EVENT_ANY_BASE, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(0, count); + TEST_ASSERT_EQUAL(0, count_instance); +} + +TEST_CASE("posting to loop with multiple registrations at every level", "[event][linux]") { + EV_LoopFix loop_fix; + int count_loop = 0; + int count_loop_inst = 0; + int count_base = 0; + int count_base_inst = 0; + int count_id = 0; // without instance, no more than one registration for a specific event ID + int count_id_inst_1 = 0; + int count_id_inst_2 = 0; + esp_event_handler_instance_t loop_ctx; + esp_event_handler_instance_t base_ctx; + esp_event_handler_instance_t id_ctx_1; + esp_event_handler_instance_t id_ctx_2; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_loop)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_base)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &count_id)); + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_loop_inst, + &loop_ctx)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_base_inst, + &base_ctx)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_id_inst_1, + &id_ctx_1)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &count_id_inst_2, + &id_ctx_2)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(count_loop, 1); + TEST_ASSERT_EQUAL(count_loop_inst, 1); + TEST_ASSERT_EQUAL(count_base, 1); + TEST_ASSERT_EQUAL(count_base_inst, 1); + TEST_ASSERT_EQUAL(count_id, 1); + TEST_ASSERT_EQUAL(count_id_inst_1, 1); + TEST_ASSERT_EQUAL(count_id_inst_2, 1); +} + +TEST_CASE("unregistering ANY_BASE and specific ID fails", "[event][linux]") +{ + EV_LoopFix loop_fix; + esp_event_handler_instance_t handler_inst = (esp_event_handler_instance_t) 1; // avoid triggering NULL check + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_unregister_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + TEST_EVENT_BASE1_EV1, + test_handler_inc)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_instance_unregister_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + TEST_EVENT_BASE1_EV1, + handler_inst)); +} + +TEST_CASE("unregistering NULL instance fails", "[event][linux]") +{ + EV_LoopFix loop_fix; + esp_event_handler_instance_t handler_inst = NULL; + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_handler_instance_unregister_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + handler_inst)); +} + +TEST_CASE("unregistered handler is not executed", "[event][linux]") +{ + EV_LoopFix loop_fix; + int loop_count = 0; + int base_count = 0; + int id_count = 0; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &loop_count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &base_count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &id_count)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(loop_count, 1); + TEST_ASSERT_EQUAL(base_count, 1); + TEST_ASSERT_EQUAL(id_count, 1); + + TEST_ESP_OK(esp_event_handler_unregister_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc)); + TEST_ESP_OK(esp_event_handler_unregister_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc)); + TEST_ESP_OK(esp_event_handler_unregister_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(loop_count, 1); + TEST_ASSERT_EQUAL(base_count, 1); + TEST_ASSERT_EQUAL(id_count, 1); +} + +TEST_CASE("unregistered handler instance is not executed", "[event][linux]") +{ + EV_LoopFix loop_fix; + int loop_count = 0; + int base_count = 0; + int id_count = 0; + esp_event_handler_instance_t loop_handler_inst; + esp_event_handler_instance_t base_handler_inst; + esp_event_handler_instance_t id_handler_inst; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &loop_count, + &loop_handler_inst)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &base_count, + &base_handler_inst)); + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &id_count, + &id_handler_inst)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(loop_count, 1); + TEST_ASSERT_EQUAL(base_count, 1); + TEST_ASSERT_EQUAL(id_count, 1); + + TEST_ESP_OK(esp_event_handler_instance_unregister_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + loop_handler_inst)); + TEST_ESP_OK(esp_event_handler_instance_unregister_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + base_handler_inst)); + TEST_ESP_OK(esp_event_handler_instance_unregister_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + id_handler_inst)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(loop_count, 1); + TEST_ASSERT_EQUAL(base_count, 1); + TEST_ASSERT_EQUAL(id_count, 1); +} + +TEST_CASE("unregistering handler does not influence other handlers", "[event][linux]") +{ + EV_LoopFix loop_fix; + int unregister_count = 0; + int different_id_count = 0; + int different_base_count = 0; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &unregister_count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base2, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &different_id_count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + test_handler_inc, + &different_base_count)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + for (size_t i = 0; i < 3; i++) { + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + } + + TEST_ASSERT_EQUAL(unregister_count, 1); + TEST_ASSERT_EQUAL(different_id_count, 1); + TEST_ASSERT_EQUAL(different_base_count, 1); + + TEST_ESP_OK(esp_event_handler_unregister_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + for (size_t i = 0; i < 3; i++) { + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + } + + TEST_ASSERT_EQUAL(unregister_count, 1); + TEST_ASSERT_EQUAL(different_id_count, 2); + TEST_ASSERT_EQUAL(different_base_count, 2); +} + +TEST_CASE("unregistering ESP_EVENT_ANY_ID does not affect other handlers with same base", "[event][linux]") +{ + EV_LoopFix loop_fix; + int any_id_count = 0; + int specific_id_count = 0; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &any_id_count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_inc, + &specific_id_count)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(any_id_count, 1); + TEST_ASSERT_EQUAL(specific_id_count, 1); + + TEST_ESP_OK(esp_event_handler_unregister_with(loop_fix.loop, s_test_base1, ESP_EVENT_ANY_ID, test_handler_inc)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(any_id_count, 1); + TEST_ASSERT_EQUAL(specific_id_count, 2); +} + +TEST_CASE("unregistering ESP_EVENT_ANY_BASE does not affect handlers with specific base", "[event][linux]") +{ + EV_LoopFix loop_fix; + int any_base_count = 0; + int any_id_count = 0; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc, + &any_base_count)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_handler_inc, + &any_id_count)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(any_base_count, 1); + TEST_ASSERT_EQUAL(any_id_count, 1); + + TEST_ESP_OK(esp_event_handler_unregister_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_handler_inc)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(any_base_count, 1); + TEST_ASSERT_EQUAL(any_id_count, 2); +} + +typedef struct { + esp_event_handler_instance_t context; + esp_event_loop_handle_t loop; + int count; +} unregister_test_data_t; + +static void test_handler_unregister_itself(void* event_handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data) +{ + unregister_test_data_t *test_data = (unregister_test_data_t*) event_handler_arg; + + (test_data->count)++; + + // Unregister this handler for this event + TEST_ESP_OK(esp_event_handler_unregister_with(test_data->loop, + event_base, + event_id, + test_handler_unregister_itself)); +} + +TEST_CASE("handler can unregister itself", "[event][linux]") +{ + EV_LoopFix loop_fix; + + unregister_test_data_t test_data = { + .context = NULL, + .loop = loop_fix.loop, + .count = 0, + }; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_unregister_itself, &test_data)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, test_data.count); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + &loop_fix.loop, + sizeof(&loop_fix.loop), + portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, pdMS_TO_TICKS(10))); + + TEST_ASSERT_EQUAL(1, test_data.count); +} + +static void test_handler_instance_unregister_itself(void* event_handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data) +{ + unregister_test_data_t *test_data = (unregister_test_data_t*) event_handler_arg; + + (test_data->count)++; + + // Unregister this handler for this event + TEST_ESP_OK(esp_event_handler_instance_unregister_with(test_data->loop, event_base, event_id, test_data->context)); +} + +TEST_CASE("handler instance can unregister itself", "[event][linux]") +{ + EV_LoopFix loop_fix; + + unregister_test_data_t test_data = { + .context = NULL, + .loop = loop_fix.loop, + .count = 0, + }; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_instance_unregister_itself, + &test_data, + &(test_data.context))); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, test_data.count); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + &loop_fix.loop, + sizeof(&loop_fix.loop), + portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, test_data.count); +} + +typedef struct { + size_t counter; + size_t test_data[4]; +} ordered_dispatch_test_data_t; + +static void test_event_ordered_dispatch(void* event_handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data, + size_t handler_index) +{ + ordered_dispatch_test_data_t *test_data = (ordered_dispatch_test_data_t*) event_handler_arg; + test_data->test_data[handler_index] = test_data->counter; + (test_data->counter)++; +} + +static void test_event_ordered_dispatch_0(void* event_handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data) +{ + test_event_ordered_dispatch(event_handler_arg, event_base, event_id, event_data, 0); +} + +static void test_event_ordered_dispatch_1(void* event_handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data) +{ + test_event_ordered_dispatch(event_handler_arg, event_base, event_id, event_data, 1); +} + +static void test_event_ordered_dispatch_2(void* event_handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data) +{ + test_event_ordered_dispatch(event_handler_arg, event_base, event_id, event_data, 2); +} + +static void test_event_ordered_dispatch_3(void* event_handler_arg, + esp_event_base_t event_base, + int32_t event_id, + void* event_data) +{ + test_event_ordered_dispatch(event_handler_arg, event_base, event_id, event_data, 3); +} + +TEST_CASE("events handlers for specific ID are dispatched in the order they are registered", "[event][linux]") +{ + EV_LoopFix loop_fix; + + ordered_dispatch_test_data_t test_data = { + .counter = 0, + .test_data = {}, + }; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_event_ordered_dispatch_0, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_event_ordered_dispatch_1, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_event_ordered_dispatch_2, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_event_ordered_dispatch_3, + &test_data)); + + esp_event_dump(stdout); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + // Expected data executing the posts above + size_t ref_arr[4]; + for (size_t i = 0; i < 4; i++) { + ref_arr[i] = i; + } + + TEST_ASSERT_EQUAL_INT_ARRAY(ref_arr, test_data.test_data, 4); +} + +TEST_CASE("events handlers for specific base are dispatched in the order they are registered", "[event][linux]") +{ + EV_LoopFix loop_fix; + + ordered_dispatch_test_data_t test_data = { + .counter = 0, + .test_data = {}, + }; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_0, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_1, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_2, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_3, + &test_data)); + + esp_event_dump(stdout); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + // Expected data executing the posts above + size_t ref_arr[4]; + for (size_t i = 0; i < 4; i++) { + ref_arr[i] = i; + } + + TEST_ASSERT_EQUAL_INT_ARRAY(ref_arr, test_data.test_data, 4); +} + +TEST_CASE("events handlers for any base are dispatched in the order they are registered", "[event][linux]") +{ + EV_LoopFix loop_fix; + + ordered_dispatch_test_data_t test_data = { + .counter = 0, + .test_data = {}, + }; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_0, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_1, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_2, + &test_data)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + ESP_EVENT_ANY_BASE, + ESP_EVENT_ANY_ID, + test_event_ordered_dispatch_3, + &test_data)); + + esp_event_dump(stdout); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + // Expected data executing the posts above + size_t ref_arr[4]; + for (size_t i = 0; i < 4; i++) { + ref_arr[i] = i; + } + + TEST_ASSERT_EQUAL_INT_ARRAY(ref_arr, test_data.test_data, 4); +} + +static void test_create_loop_handler(void* handler_args, esp_event_base_t base, int32_t id, void* event_data) +{ + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + if (id == TEST_EVENT_BASE1_EV1) { + TEST_ESP_OK(esp_event_loop_create(&loop_args, (esp_event_loop_handle_t*) handler_args)); + } else { + TEST_ESP_OK(esp_event_loop_delete(*((esp_event_loop_handle_t*) handler_args))); + } +} + +TEST_CASE("can create and delete loop from handler", "[event][linux]") +{ + EV_LoopFix loop_fix; + esp_event_loop_handle_t test_loop; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_create_loop_handler, + &test_loop)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + test_create_loop_handler, + &test_loop)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); +} + +typedef struct { + esp_event_loop_handle_t loop; + int count; +} posting_handler_data_t; + +static void test_handler_post(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) +{ + posting_handler_data_t* data = (posting_handler_data_t*) handler_arg; + TEST_ESP_OK(esp_event_post_to(data->loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + data->count++; +} + +TEST_CASE("can post to loop from handler", "[event][linux]") +{ + EV_LoopFix loop_fix; + + posting_handler_data_t arg = { + .loop = loop_fix.loop, + .count = 0, + }; + int secondary_handler_count = 0; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_post, + &arg)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + test_handler_inc, + &secondary_handler_count)); + + // Test that a handler can post to a different loop while there is still slots on the queue + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, arg.count); + TEST_ASSERT_EQUAL(1, secondary_handler_count); +} + +TEST_CASE("can post to loop from handler instance", "[event][linux]") +{ + EV_LoopFix loop_fix; + esp_event_handler_instance_t ctx; + + posting_handler_data_t arg = { + .loop = loop_fix.loop, + .count = 0, + }; + int secondary_handler_count = 0; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_post, + &arg, + &ctx)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + test_handler_inc, + &secondary_handler_count)); + + // Test that a handler can post to a different loop while there is still slots on the queue + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, arg.count); + TEST_ASSERT_EQUAL(1, secondary_handler_count); +} + +static void test_handler_post_timeout(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) +{ + posting_handler_data_t* data = (posting_handler_data_t*) handler_arg; + TEST_ESP_OK(esp_event_post_to(data->loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(data->loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + NULL, + 0, + 1)); + data->count++; +} + +TEST_CASE("posting to loop from handler times out", "[event][linux]") +{ + EV_LoopFix loop_fix(1); + + posting_handler_data_t arg = { + .loop = loop_fix.loop, + .count = 0, + }; + int secondary_handler_count = 0; + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_post_timeout, + &arg)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + test_handler_inc, + &secondary_handler_count)); + + // Test that a handler can post to a different loop while there is still slots on the queue + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(1, arg.count); + TEST_ASSERT_EQUAL(1, secondary_handler_count); +} + +static void test_handler_take_sem(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) +{ + SemaphoreHandle_t sem = (SemaphoreHandle_t) handler_arg; + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(sem, portMAX_DELAY)); +} + +static void test_handler_give_sem(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) +{ + SemaphoreHandle_t sem = (SemaphoreHandle_t) handler_arg; + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreGive(sem)); +} + +TEST_CASE("can post while handler is executing - dedicated task", "[event][linux]") +{ + EV_LoopFix loop_fix(1, "loop_task"); + + SemaphoreHandle_t ev1_sem = xSemaphoreCreateBinary(); + SemaphoreHandle_t ev2_sem = xSemaphoreCreateBinary(); + TEST_ASSERT(ev1_sem); + TEST_ASSERT(ev2_sem); + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_take_sem, + ev1_sem)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + test_handler_give_sem, + ev2_sem)); + + // Trigger waiting by sending first event + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + + // Check that event can be posted while handler is running (waiting on the semaphore) + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV2, NULL, 0, portMAX_DELAY)); + + // Now the event queue has to be full, expect timeout + TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + NULL, + 0, + 1)); + + // Run all events + xSemaphoreGive(ev1_sem); + + // verify that test_handler_give_sem() has run, too + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(ev2_sem, portMAX_DELAY)); + + vSemaphoreDelete(ev1_sem); + vSemaphoreDelete(ev2_sem); +} + +static void test_post_from_handler_loop_task(void* args) +{ + esp_event_loop_handle_t event_loop = (esp_event_loop_handle_t) args; + + TEST_ESP_OK(esp_event_loop_run(event_loop, portMAX_DELAY)); +} + +static void never_run(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) +{ + TEST_ASSERT(false); +} + +TEST_CASE("can not post while handler is executing - no dedicated task", "[event][linux]") +{ + EV_LoopFix loop_fix(1); + + SemaphoreHandle_t sem = xSemaphoreCreateBinary(); + SemaphoreHandle_t wait_done_sem = xSemaphoreCreateBinary(); + TEST_ASSERT(sem); + + TaskHandle_t mtask; + xTaskCreate(test_post_from_handler_loop_task, + "task", + 2584, + (void*) loop_fix.loop, + uxTaskPriorityGet(NULL) + 1, + &mtask); + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_take_sem, + sem)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + never_run, + NULL)); + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base2, + TEST_EVENT_BASE1_EV1, + test_handler_give_sem, + wait_done_sem)); + + // Trigger waiting by sending first event + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + + // Wait until semaphore is actually taken, which means handler is running and blocked + while (xSemaphoreTake(sem, 1) == pdTRUE) { + xSemaphoreGive(sem); + vTaskDelay(2); + } + + // For loop without tasks, posting is more restrictive. Posting should wait until execution of handler finishes + TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV2, + NULL, + 0, + 1)); + + // Being running all events + xSemaphoreGive(sem); + + // wait until all events have run + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + xSemaphoreTake(wait_done_sem, portMAX_DELAY); + + // Clean up + vTaskDelete(mtask); + vSemaphoreDelete(wait_done_sem); + vSemaphoreDelete(sem); +} + +struct EventData { + EventData(size_t expected_event_data_size) : expected_size(expected_event_data_size) { } + constexpr static size_t MAX_SIZE = 16; + void *event_arg; // only test for nullptr! + size_t expected_size; + uint8_t event_data[MAX_SIZE]; +}; + +static void save_ev_data(void* handler_arg, esp_event_base_t base, int32_t id, void* event_arg) +{ + EventData *ev_data = (EventData *) handler_arg; + ev_data->event_arg = event_arg; + if (ev_data->expected_size > 0) { + memcpy(ev_data->event_data, event_arg, ev_data->expected_size); + } +} + +TEST_CASE("event data null", "[event][linux]") +{ + EV_LoopFix loop_fix; + EventData saved_ev_data(0); + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + save_ev_data, + &saved_ev_data)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_EQUAL(NULL, saved_ev_data.event_arg); +} + +TEST_CASE("event data one nonzero byte", "[event][linux]") +{ + EV_LoopFix loop_fix; + uint8_t ev_data = 47; + EventData saved_ev_data(1); + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + save_ev_data, + &saved_ev_data)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + &ev_data, + sizeof(ev_data), + portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_NOT_EQUAL(NULL, saved_ev_data.event_arg); + TEST_ASSERT_EQUAL(47, saved_ev_data.event_data[0]); +} + +TEST_CASE("event data one zero byte", "[event][linux]") +{ + EV_LoopFix loop_fix; + uint8_t ev_data = 0; + EventData saved_ev_data(1); + saved_ev_data.event_data[0] = 47; + + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base2, + TEST_EVENT_BASE1_EV1, + save_ev_data, + &saved_ev_data, + &ctx)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, + s_test_base2, + TEST_EVENT_BASE1_EV1, + &ev_data, + sizeof(ev_data), + portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_NOT_EQUAL(NULL, saved_ev_data.event_arg); + TEST_ASSERT_EQUAL(0, saved_ev_data.event_data[0]); +} + +TEST_CASE("event data many bytes", "[event][linux]") +{ + EV_LoopFix loop_fix; + uint8_t ev_data[EventData::MAX_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + EventData saved_ev_data(16); + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + save_ev_data, + &saved_ev_data)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + &ev_data, + sizeof(ev_data), + portMAX_DELAY)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_NOT_EQUAL(NULL, saved_ev_data.event_arg); + TEST_ASSERT_EQUAL_HEX8_ARRAY(ev_data, saved_ev_data.event_data, EventData::MAX_SIZE); +} + +TEST_CASE("event data one byte is copied on post", "[event][linux]") +{ + EV_LoopFix loop_fix; + uint8_t ev_data = 47; + EventData saved_ev_data(1); + + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop_fix.loop, + s_test_base2, + TEST_EVENT_BASE1_EV1, + save_ev_data, + &saved_ev_data, + &ctx)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, + s_test_base2, + TEST_EVENT_BASE1_EV1, + &ev_data, + sizeof(ev_data), + portMAX_DELAY)); + ev_data = 42; + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_NOT_EQUAL(NULL, saved_ev_data.event_arg); + TEST_ASSERT_EQUAL(47, saved_ev_data.event_data[0]); +} + +TEST_CASE("event data many bytes are copied on post", "[event][linux]") +{ + EV_LoopFix loop_fix; + uint8_t ev_data[EventData::MAX_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + uint8_t ev_data_expected[EventData::MAX_SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + EventData saved_ev_data(16); + + TEST_ESP_OK(esp_event_handler_register_with(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + save_ev_data, + &saved_ev_data)); + + TEST_ESP_OK(esp_event_post_to(loop_fix.loop, + s_test_base1, + TEST_EVENT_BASE1_EV1, + &ev_data, + sizeof(ev_data), + portMAX_DELAY)); + memset(ev_data, 0, sizeof(ev_data)); + TEST_ESP_OK(esp_event_loop_run(loop_fix.loop, ZERO_DELAY)); + + TEST_ASSERT_NOT_EQUAL(NULL, saved_ev_data.event_arg); + TEST_ASSERT_EQUAL_HEX8_ARRAY(ev_data_expected, saved_ev_data.event_data, EventData::MAX_SIZE); +} + +TEST_CASE("default loop: registering fails on uninitialized default loop", "[event][default][linux]") +{ + esp_event_handler_instance_t instance; + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_event_handler_instance_register(s_test_base1, + TEST_EVENT_BASE1_EV1, + never_run, + NULL, + &instance)); +} + +TEST_CASE("default loop: can create and delete loop", "[event][default][linux]") +{ + TEST_ESP_OK(esp_event_loop_create_default()); + + TEST_ESP_OK(esp_event_loop_delete_default()); +} + +TEST_CASE("default loop: registering/unregistering event works", "[event][default][linux]") +{ + TEST_ESP_OK(esp_event_loop_create_default()); + + if (TEST_PROTECT()) { + SemaphoreHandle_t waiter = xSemaphoreCreateBinary(); + + esp_event_handler_instance_t instance = NULL; + TEST_ESP_OK(esp_event_handler_instance_register(s_test_base1, + TEST_EVENT_BASE1_EV1, + test_handler_give_sem, + waiter, + &instance)); + TEST_ASSERT(instance); + TEST_ESP_OK(esp_event_post(s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(waiter, pdMS_TO_TICKS(50))); + + TEST_ESP_OK(esp_event_handler_instance_unregister(s_test_base1, + TEST_EVENT_BASE1_EV1, + instance)); + TEST_ESP_OK(esp_event_post(s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + + TEST_ASSERT_EQUAL(pdFALSE, xSemaphoreTake(waiter, pdMS_TO_TICKS(50))); + + vSemaphoreDelete(waiter); + } + + TEST_ESP_OK(esp_event_loop_delete_default()); +} + +TEST_CASE("default event loop: registering event handler instance without instance context works", "[event][default][linux]") { + TEST_ESP_OK(esp_event_loop_create_default()); + + if (TEST_PROTECT()) { + SemaphoreHandle_t waiter = xSemaphoreCreateBinary(); + + TEST_ESP_OK(esp_event_handler_instance_register(ESP_EVENT_ANY_BASE, ESP_EVENT_ANY_ID, test_handler_give_sem, waiter, NULL)); + TEST_ESP_OK(esp_event_post(s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(waiter, pdMS_TO_TICKS(50))); + + vSemaphoreDelete(waiter); + } + + TEST_ESP_OK(esp_event_loop_delete_default()); +} diff --git a/components/esp_event/test_apps/main/test_event_main.c b/components/esp_event/test_apps/main/test_event_main.c index f18811e9eb..293a363123 100644 --- a/components/esp_event/test_apps/main/test_event_main.c +++ b/components/esp_event/test_apps/main/test_event_main.c @@ -6,32 +6,15 @@ #include "unity.h" #include "unity_test_runner.h" -#include "esp_heap_caps.h" +#include "unity_test_utils_memory.h" #include "esp_log.h" #ifdef CONFIG_HEAP_TRACING #include "memory_checks.h" #include "esp_heap_trace.h" #endif -#define TEST_MEMORY_LEAK_THRESHOLD_DEFAULT 0 -static int leak_threshold = TEST_MEMORY_LEAK_THRESHOLD_DEFAULT; - -void set_leak_threshold(int threshold) -{ - leak_threshold = threshold; -} - -static size_t before_free_8bit; -static size_t before_free_32bit; static const char* TAG = "event_test_app"; -static void check_leak(size_t before_free, size_t after_free, const char *type) -{ - ssize_t delta = after_free - before_free; - printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); - TEST_ASSERT_MESSAGE(delta >= leak_threshold, "memory leak"); -} - void setUp(void) { // If heap tracing is enabled in kconfig, leak trace the test @@ -40,10 +23,8 @@ void setUp(void) heap_trace_start(HEAP_TRACE_LEAKS); #endif - leak_threshold = TEST_MEMORY_LEAK_THRESHOLD_DEFAULT; - - before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_set_leak_level(0); + unity_utils_record_free_mem(); } void tearDown(void) @@ -53,11 +34,7 @@ void tearDown(void) heap_trace_dump(); #endif - size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); - check_leak(before_free_8bit, after_free_8bit, "8BIT"); - check_leak(before_free_32bit, after_free_32bit, "32BIT"); - + unity_utils_evaluate_leaks(); } void app_main(void) diff --git a/components/esp_event/test_apps/main/test_event_target.c b/components/esp_event/test_apps/main/test_event_target.c new file mode 100644 index 0000000000..f949319b7f --- /dev/null +++ b/components/esp_event/test_apps/main/test_event_target.c @@ -0,0 +1,765 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* This file contains tests only runnable on the chip targets */ + +#include +#include + +#include "esp_event.h" +#include "sdkconfig.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gptimer.h" + +#include "esp_event.h" +#include "esp_event_private.h" +#include "esp_event_internal.h" + +#include "esp_heap_caps.h" +#include "esp_timer.h" + +#include "sdkconfig.h" +#include "unity.h" +#include "unity_test_utils_memory.h" + +#include "test_utils.h" + + +static const char* TAG = "test_event"; + +static const TickType_t ZERO_DELAY = 0; + +#define TEST_CONFIG_ITEMS_TO_REGISTER 5 +#define TEST_CONFIG_TASKS_TO_SPAWN 2 +_Static_assert(TEST_CONFIG_TASKS_TO_SPAWN >= 2); // some tests test simultaneous posting of events, etc. + +/* Time used in tearDown function to wait for cleaning up memory in background tasks */ +#define TEST_CONFIG_TEARDOWN_WAIT 30 + +typedef struct { + void* data; + SemaphoreHandle_t start; + SemaphoreHandle_t done; +} task_arg_t; + +typedef struct { + esp_event_base_t base; + int32_t id; + esp_event_handler_t* handles; + int32_t num; + esp_event_loop_handle_t loop; + bool is_registration; +} handler_registration_data_t; + +typedef struct { + esp_event_base_t base; + int32_t id; + esp_event_loop_handle_t loop; + int32_t num; +} post_event_data_t; + +typedef struct { + int performed; + int expected; + SemaphoreHandle_t done; +} performance_data_t; + +typedef struct { + void* data; + SemaphoreHandle_t mutex; +} simple_arg_t; + +ESP_EVENT_DECLARE_BASE(s_test_base1); + +enum { + TEST_EVENT_BASE1_EV1, + TEST_EVENT_BASE1_MAX +}; + +static BaseType_t get_other_core(void) +{ + return (xPortGetCoreID() + 1) % portNUM_PROCESSORS; +} + +static esp_event_loop_args_t test_event_get_default_loop_args(void) +{ + esp_event_loop_args_t loop_config = { + .queue_size = CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE, + .task_name = "loop", + .task_priority = uxTaskPriorityGet(NULL), + .task_stack_size = 2048, + .task_core_id = get_other_core() + }; + + return loop_config; +} + +static void test_event_simple_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + if (!event_handler_arg) { + return; + } + simple_arg_t* arg = (simple_arg_t*) event_handler_arg; + xSemaphoreTake(arg->mutex, portMAX_DELAY); + + int* count = (int*) arg->data; + + if (event_data == NULL) { + (*count)++; + } else { + (*count) += *((int*) event_data); + } + + xSemaphoreGive(arg->mutex); +} + +static void test_event_performance_handler(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + performance_data_t* data = (performance_data_t*) event_handler_arg; + + data->performed++; + + if (data->performed >= data->expected) { + xSemaphoreGive(data->done); + } +} + +static void test_event_post_task(void* args) +{ + task_arg_t* arg = (task_arg_t*) args; + post_event_data_t* data = arg->data; + + xSemaphoreTake(arg->start, portMAX_DELAY); + + for (int i = 0; i < data->num; i++) { + TEST_ESP_OK(esp_event_post_to(data->loop, data->base, data->id, NULL, 0, portMAX_DELAY)); + vTaskDelay(1); + } + + xSemaphoreGive(arg->done); + + vTaskDelete(NULL); +} + +static void test_event_simple_handler_registration_task(void* args) +{ + task_arg_t* arg = (task_arg_t*) args; + handler_registration_data_t* data = (handler_registration_data_t*) arg->data; + + xSemaphoreTake(arg->start, portMAX_DELAY); + + for(int i = 0; i < data->num; i++) { + if (data->is_registration) { + TEST_ESP_OK(esp_event_handler_register_with(data->loop, data->base, data->id, data->handles[i], NULL)); + } else { + TEST_ESP_OK(esp_event_handler_unregister_with(data->loop, data->base, data->id, data->handles[i])); + } + vTaskDelay(1); + } + + xSemaphoreGive(arg->done); + + vTaskDelete(NULL); +} + +// Ignore this test on QEMU for now since it relies on esp_timer which is based on the host run time on ESP32-QEMU +TEST_CASE("can exit running loop at approximately the set amount of time", "[event][qemu-ignore]") +{ + /* this test aims to verify that running loop does not block indefinitely in cases where + * events are posted frequently */ + + esp_event_loop_handle_t loop; + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + loop_args.task_name = NULL; + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + + performance_data_t handler_data = { + .performed = 0, + .expected = INT32_MAX, + .done = xSemaphoreCreateBinary() + }; + + TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_performance_handler, &handler_data)); + + post_event_data_t post_event_data = { + .base = s_test_base1, + .id = TEST_EVENT_BASE1_EV1, + .loop = loop, + .num = INT32_MAX + }; + + task_arg_t post_event_arg = { + .data = &post_event_data, + .done = xSemaphoreCreateBinary(), + .start = xSemaphoreCreateBinary() + }; + + TaskHandle_t post_task; + xTaskCreatePinnedToCore(test_event_post_task, "post", 2048, &post_event_arg, uxTaskPriorityGet(NULL), &post_task, get_other_core()); + + int runtime_ms = 10; + int runtime_us = runtime_ms * 1000; + + int64_t start, diff; + start = esp_timer_get_time(); + + xSemaphoreGive(post_event_arg.start); + + // Run the loop for the runtime_ms set amount of time, regardless of whether events + // are still being posted to the loop. + TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(runtime_ms))); + + diff = (esp_timer_get_time() - start); + + // Threshold is 25 percent. + TEST_ASSERT_LESS_THAN_INT(runtime_us * 1.25f, diff); + + // Verify that the post task still continues + TEST_ASSERT_NOT_EQUAL(pdTRUE, xSemaphoreTake(post_event_arg.done, pdMS_TO_TICKS(10))); + + vSemaphoreDelete(post_event_arg.done); + vSemaphoreDelete(post_event_arg.start); + vSemaphoreDelete(handler_data.done); + vTaskDelete(post_task); + + TEST_ESP_OK(esp_event_loop_delete(loop)); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); +} + +TEST_CASE("can register/unregister handlers simultaneously", "[event]") +{ + /* this test aims to verify that the event handlers list remains consistent despite + * simultaneous access by differenct tasks */ + + const char* base = "base"; + int32_t id = 0; + + esp_event_loop_handle_t loop; + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + + ESP_LOGI(TAG, "registering handlers"); + + handler_registration_data_t* registration_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*registration_data)); + task_arg_t* registration_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*registration_arg)); + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + registration_data[i].base = base; + registration_data[i].id = id; + registration_data[i].loop = loop; + registration_data[i].handles = calloc(TEST_CONFIG_ITEMS_TO_REGISTER, sizeof(esp_event_handler_t)); + registration_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; + registration_data[i].is_registration = true; + + for (int j = 0; j < TEST_CONFIG_ITEMS_TO_REGISTER; j++) { + registration_data[i].handles[j] = (void*) (i * TEST_CONFIG_ITEMS_TO_REGISTER) + (j + TEST_CONFIG_ITEMS_TO_REGISTER); + } + + registration_arg[i].start = xSemaphoreCreateBinary(); + registration_arg[i].done = xSemaphoreCreateBinary(); + registration_arg[i].data = ®istration_data[i]; + + xTaskCreatePinnedToCore(test_event_simple_handler_registration_task, "register", 2048, ®istration_arg[i], uxTaskPriorityGet(NULL), NULL, i % portNUM_PROCESSORS); + } + + // Give the semaphores to the spawned registration task + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreGive(registration_arg[i].start); + } + + // Take the same semaphores in order to proceed + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreTake(registration_arg[i].done, portMAX_DELAY); + } + + ESP_LOGI(TAG, "checking consistency of handlers list"); + + // Check consistency of events list + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + for (int j = 0; j < TEST_CONFIG_ITEMS_TO_REGISTER; j++) { + TEST_ASSERT_TRUE(esp_event_is_handler_registered(loop, base, id, registration_data[i].handles[j])); + } + } + + ESP_LOGI(TAG, "unregistering handlers"); + + /* Test if tasks can unregister simultaneously */ + + // Unregister registered events + handler_registration_data_t* unregistration_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*unregistration_data)); + task_arg_t* unregistration_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*unregistration_arg)); + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + unregistration_data[i].base = base; + unregistration_data[i].id = id; + unregistration_data[i].loop = loop; + unregistration_data[i].handles = calloc(TEST_CONFIG_ITEMS_TO_REGISTER, sizeof(esp_event_handler_t)); + unregistration_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; + unregistration_data[i].is_registration = false; + + memcpy(unregistration_data[i].handles, registration_data[i].handles, TEST_CONFIG_ITEMS_TO_REGISTER * sizeof(esp_event_handler_t)); + + unregistration_arg[i].data = &unregistration_data[i]; + unregistration_arg[i].start = xSemaphoreCreateBinary(); + unregistration_arg[i].done = xSemaphoreCreateBinary(); + + xTaskCreatePinnedToCore(test_event_simple_handler_registration_task, "unregister", 2048, &unregistration_arg[i], uxTaskPriorityGet(NULL), NULL, i % portNUM_PROCESSORS); + } + + // Give the semaphores to the spawned unregistration task + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreGive(unregistration_arg[i].start); + } + + // Take the same semaphores in order to proceed + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreTake(unregistration_arg[i].done, portMAX_DELAY); + } + + ESP_LOGI(TAG, "checking consistency of handlers list"); + + // Check consistency of events list + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + for (int j = 0; j < TEST_CONFIG_ITEMS_TO_REGISTER; j++) { + TEST_ASSERT_FALSE(esp_event_is_handler_registered(loop, base, id, registration_data[i].handles[j])); + } + } + + // Do cleanup + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + free(registration_data[i].handles); + vSemaphoreDelete(registration_arg[i].start); + vSemaphoreDelete(registration_arg[i].done); + + free(unregistration_data[i].handles); + vSemaphoreDelete(unregistration_arg[i].start); + vSemaphoreDelete(unregistration_arg[i].done); + } + + free(registration_data); + free(unregistration_data); + free(registration_arg); + free(unregistration_arg); + + TEST_ESP_OK(esp_event_loop_delete(loop)); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); +} + +TEST_CASE("can post and run events simultaneously", "[event]") +{ + /* this test aims to verify that: + * - multiple tasks can post to the queue simultaneously + * - handlers recieve the appropriate handler arg and associated event data */ + + esp_event_loop_handle_t loop; + + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + loop_args.task_name = NULL; + loop_args.queue_size = TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER; + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + + int count = 0; + + simple_arg_t arg = { + .data = &count, + .mutex = xSemaphoreCreateMutex() + }; + + TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); + + post_event_data_t* post_event_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_data)); + task_arg_t* post_event_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_arg)); + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) + { + post_event_data[i].base = s_test_base1; + post_event_data[i].id = TEST_EVENT_BASE1_EV1; + post_event_data[i].loop = loop; + post_event_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; + + post_event_arg[i].data = &post_event_data[i]; + post_event_arg[i].start = xSemaphoreCreateBinary(); + post_event_arg[i].done = xSemaphoreCreateBinary(); + + xTaskCreatePinnedToCore(test_event_post_task, "post", 2048, &post_event_arg[i], uxTaskPriorityGet(NULL), NULL, i % portNUM_PROCESSORS); + } + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreGive(post_event_arg[i].start); + } + + // Execute some events as they are posted + for (int i = 0; i < (TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER) / 2; i++) { + TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); + } + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreTake(post_event_arg[i].done, portMAX_DELAY); + } + + // Execute the rest + for (size_t i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER; i++) { + TEST_ESP_OK(esp_event_loop_run(loop, ZERO_DELAY)); + } + + TEST_ASSERT_EQUAL(TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER, count); + + // Cleanup + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + vSemaphoreDelete(post_event_arg[i].start); + vSemaphoreDelete(post_event_arg[i].done); + } + + free(post_event_data); + free(post_event_arg); + + TEST_ESP_OK(esp_event_loop_delete(loop)); + + vSemaphoreDelete(arg.mutex); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); +} + +TEST_CASE("can post and run events simultaneously with instances", "[event]") +{ + /* this test aims to verify that: + * - multiple tasks can post to the queue simultaneously + * - handlers recieve the appropriate handler arg and associated event data */ + + esp_event_loop_handle_t loop; + + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + loop_args.task_name = NULL; + loop_args.queue_size = TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER; + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + + int count = 0; + + simple_arg_t arg = { + .data = &count, + .mutex = xSemaphoreCreateMutex() + }; + + esp_event_handler_instance_t ctx; + + TEST_ESP_OK(esp_event_handler_instance_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg, &ctx)); + + post_event_data_t* post_event_data = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_data)); + task_arg_t* post_event_arg = calloc(TEST_CONFIG_TASKS_TO_SPAWN, sizeof(*post_event_arg)); + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) + { + post_event_data[i].base = s_test_base1; + post_event_data[i].id = TEST_EVENT_BASE1_EV1; + post_event_data[i].loop = loop; + post_event_data[i].num = TEST_CONFIG_ITEMS_TO_REGISTER; + + post_event_arg[i].data = &post_event_data[i]; + post_event_arg[i].start = xSemaphoreCreateBinary(); + post_event_arg[i].done = xSemaphoreCreateBinary(); + + xTaskCreatePinnedToCore(test_event_post_task, "post", 2048, &post_event_arg[i], uxTaskPriorityGet(NULL), NULL, i % portNUM_PROCESSORS); + } + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreGive(post_event_arg[i].start); + } + + // Execute some events as they are posted + for (int i = 0; i < (TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER) / 2; i++) { + TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); + } + + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + xSemaphoreTake(post_event_arg[i].done, portMAX_DELAY); + } + + // Execute the rest, we use the maximum number of events because we don't know + // if any events have been dispatched before + for (size_t i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER; i++) { + TEST_ESP_OK(esp_event_loop_run(loop, ZERO_DELAY)); + } + + TEST_ASSERT_EQUAL(TEST_CONFIG_TASKS_TO_SPAWN * TEST_CONFIG_ITEMS_TO_REGISTER, count); + + // Cleanup + for (int i = 0; i < TEST_CONFIG_TASKS_TO_SPAWN; i++) { + vSemaphoreDelete(post_event_arg[i].start); + vSemaphoreDelete(post_event_arg[i].done); + } + + free(post_event_data); + free(post_event_arg); + + TEST_ESP_OK(esp_event_loop_delete(loop)); + + vSemaphoreDelete(arg.mutex); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); +} + +static void loop_run_task(void* args) +{ + esp_event_loop_handle_t event_loop = (esp_event_loop_handle_t) args; + + while(1) { + esp_event_loop_run(event_loop, portMAX_DELAY); + } +} + +static void performance_test(bool dedicated_task) +{ + // rand() seems to do a one-time allocation. Call it here so that the memory it allocates + // is not counted as a leak. + unsigned int _rand __attribute__((unused)) = rand(); + + const char test_base[] = "qwertyuiopasdfghjklzxvbnmmnbvcxz"; + + #define TEST_CONFIG_BASES (sizeof(test_base) - 1) + #define TEST_CONFIG_IDS (TEST_CONFIG_BASES / 2) + + // Create loop + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + esp_event_loop_handle_t loop; + + if (!dedicated_task) { + loop_args.task_name = NULL; + } + + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + + performance_data_t data; + + // Register the handlers + for (int base = 0; base < TEST_CONFIG_BASES; base++) { + for (int id = 0; id < TEST_CONFIG_IDS; id++) { + TEST_ESP_OK(esp_event_handler_register_with(loop, test_base + base, id, test_event_performance_handler, &data)); + } + } + + TaskHandle_t mtask = NULL; + + if (!dedicated_task) { + xTaskCreate(loop_run_task, "loop_run", loop_args.task_stack_size, (void*) loop, loop_args.task_priority, &mtask); + } + + // Perform performance test + float running_sum = 0; + float running_count = 0; + + for (int bases = 1; bases <= TEST_CONFIG_BASES; bases *= 2) { + for (int ids = 1; ids <= TEST_CONFIG_IDS; ids *= 2) { + + data.performed = 0; + data.expected = bases * ids; + data.done = xSemaphoreCreateBinary(); + + // Generate randomized list of posts + int post_bases[TEST_CONFIG_BASES]; + int post_ids[TEST_CONFIG_IDS]; + + for (int i = 0; i < bases; i++) { + post_bases[i] = i; + } + + for (int i = 0; i < ids; i++) { + post_ids[i] = i; + } + + for (int i = 0; i < bases; i++) { + int rand_a = rand() % bases; + int rand_b = rand() % bases; + + int temp = post_bases[rand_a]; + post_bases[rand_a]= post_bases[rand_b]; + post_bases[rand_b] = temp; + } + + for (int i = 0; i < ids; i++) { + int rand_a = rand() % ids; + int rand_b = rand() % ids; + + int temp = post_ids[rand_a]; + post_ids[rand_a]= post_ids[rand_b]; + post_ids[rand_b] = temp; + } + + // Post the events + int64_t start = esp_timer_get_time(); + for (int base = 0; base < bases; base++) { + for (int id = 0; id < ids; id++) { + TEST_ESP_OK(esp_event_post_to(loop, test_base + post_bases[base], post_ids[id], NULL, 0, portMAX_DELAY)); + } + } + + xSemaphoreTake(data.done, portMAX_DELAY); + int64_t elapsed = esp_timer_get_time() - start; + + // Record data + TEST_ASSERT_EQUAL(data.expected, data.performed); + + running_count++; + running_sum += data.performed / (elapsed / (1000000.0)); + + vSemaphoreDelete(data.done); + } + } + + int average = (int) (running_sum / (running_count)); + + if (!dedicated_task) { + ((esp_event_loop_instance_t*) loop)->task = mtask; + } + + TEST_ESP_OK(esp_event_loop_delete(loop)); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); + +#ifdef CONFIG_ESP_EVENT_LOOP_PROFILING + ESP_LOGI(TAG, "events dispatched/second with profiling enabled: %d", average); + // Enabling profiling will slow down event dispatch, so the set threshold + // is not valid when it is enabled. +#else +#ifndef CONFIG_SPIRAM + TEST_PERFORMANCE_GREATER_THAN(EVENT_DISPATCH, "%d", average); +#else + TEST_PERFORMANCE_GREATER_THAN(EVENT_DISPATCH_PSRAM, "%d", average); +#endif // CONFIG_SPIRAM +#endif // CONFIG_ESP_EVENT_LOOP_PROFILING +} + +TEST_CASE("performance test - dedicated task", "[event][qemu-ignore]") +{ + performance_test(true); +} + +TEST_CASE("performance test - no dedicated task", "[event][qemu-ignore]") +{ + performance_test(false); +} + +#if CONFIG_ESP_EVENT_POST_FROM_ISR +TEST_CASE("data posted normally is correctly set internally", "[event][intr]") +{ + esp_event_loop_handle_t loop; + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + loop_args.task_name = NULL; + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + + esp_event_post_instance_t post; + esp_event_loop_instance_t* loop_def = (esp_event_loop_instance_t*) loop; + + TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); + TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(loop_def->queue, &post, portMAX_DELAY)); + TEST_ASSERT_EQUAL(false, post.data_set); + TEST_ASSERT_EQUAL(false, post.data_allocated); + TEST_ASSERT_EQUAL(NULL, post.data.ptr); + + TEST_ESP_OK(esp_event_loop_delete(loop)); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); +} + +TEST_CASE("data posted from ISR is correctly set internally", "[event][intr]") +{ + esp_event_loop_handle_t loop; + esp_event_loop_args_t loop_args = test_event_get_default_loop_args(); + + loop_args.task_name = NULL; + TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); + + esp_event_post_instance_t post; + esp_event_loop_instance_t* loop_def = (esp_event_loop_instance_t*) loop; + int sample = 0; + TEST_ESP_OK(esp_event_isr_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, &sample, sizeof(sample), NULL)); + TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(loop_def->queue, &post, portMAX_DELAY)); + TEST_ASSERT_EQUAL(true, post.data_set); + TEST_ASSERT_EQUAL(false, post.data_allocated); + TEST_ASSERT_EQUAL(false, post.data.val); + + TEST_ESP_OK(esp_event_loop_delete(loop)); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); +} + +static void test_handler_post_from_isr(void* event_handler_arg, esp_event_base_t event_base, int32_t event_id, void* event_data) +{ + SemaphoreHandle_t *sem = (SemaphoreHandle_t*) event_handler_arg; + // Event data is just the address value (maybe have been truncated due to casting). + int *data = (int*) event_data; + TEST_ASSERT_EQUAL(*data, (int) (*sem)); + xSemaphoreGive(*sem); +} + +bool test_event_on_timer_alarm(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx) +{ + int data = (int)user_ctx; + gptimer_stop(timer); + // Posting events with data more than 4 bytes should fail. + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_isr_post(s_test_base1, TEST_EVENT_BASE1_EV1, &data, 5, NULL)); + // This should succeedd, as data is int-sized. The handler for the event checks that the passed event data + // is correct. + BaseType_t task_unblocked; + TEST_ESP_OK(esp_event_isr_post(s_test_base1, TEST_EVENT_BASE1_EV1, &data, sizeof(data), &task_unblocked)); + return task_unblocked == pdTRUE; +} + +TEST_CASE("can post events from interrupt handler", "[event][intr]") +{ + /* Lazy allocated resources in gptimer/intr_alloc */ + unity_utils_set_leak_level(150); + + TEST_ESP_OK(esp_event_loop_create_default()); + + SemaphoreHandle_t sem = xSemaphoreCreateBinary(); + gptimer_handle_t gptimer = NULL; + /* Select and initialize basic parameters of the timer */ + gptimer_config_t config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1000000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&config, &gptimer)); + + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = 500000, + }; + gptimer_event_callbacks_t cbs = { + .on_alarm = test_event_on_timer_alarm + }; + TEST_ESP_OK(gptimer_register_event_callbacks(gptimer, &cbs, sem)); + TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config)); + TEST_ESP_OK(gptimer_enable(gptimer)); + TEST_ESP_OK(gptimer_start(gptimer)); + + TEST_ESP_OK(esp_event_handler_register(s_test_base1, TEST_EVENT_BASE1_EV1, + test_handler_post_from_isr, &sem)); + + xSemaphoreTake(sem, portMAX_DELAY); + + vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); + + vSemaphoreDelete(sem); + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); + + TEST_ESP_OK(esp_event_loop_delete_default()); + + vTaskDelay(2); +} + +#endif // CONFIG_ESP_EVENT_POST_FROM_ISR diff --git a/components/esp_event/test_apps/pytest_esp_event.py b/components/esp_event/test_apps/pytest_esp_event.py index 7d17321c10..8e9cbd5538 100644 --- a/components/esp_event/test_apps/pytest_esp_event.py +++ b/components/esp_event/test_apps/pytest_esp_event.py @@ -35,3 +35,11 @@ def test_esp_event_qemu_esp32c3(dut: Dut) -> None: for case in dut.test_menu: if 'qemu-ignore' not in case.groups and not case.is_ignored and case.type == 'normal': dut._run_normal_case(case) + + +@pytest.mark.linux +@pytest.mark.host_test +def test_esp_event_posix_simulator(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests.') + dut.write('*') + dut.expect(r'\d{2} Tests 0 Failures 0 Ignored', timeout=120) diff --git a/components/esp_event/test_apps/sdkconfig.defaults b/components/esp_event/test_apps/sdkconfig.defaults index e4bfc208a5..e69de29bb2 100644 --- a/components/esp_event/test_apps/sdkconfig.defaults +++ b/components/esp_event/test_apps/sdkconfig.defaults @@ -1 +0,0 @@ -CONFIG_ESP_TASK_WDT_EN=n diff --git a/components/esp_event/test_apps/sdkconfig.defaults.linux b/components/esp_event/test_apps/sdkconfig.defaults.linux new file mode 100644 index 0000000000..e1ed9340db --- /dev/null +++ b/components/esp_event/test_apps/sdkconfig.defaults.linux @@ -0,0 +1 @@ +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=n