Merge branch 'refactor/esp_event_test' into 'master'

ESP Event: improved tests to fail less frequently on QEMU

See merge request espressif/esp-idf!24783
This commit is contained in:
Jakob Hasse 2023-07-19 14:54:23 +08:00
commit 52279c0837
2 changed files with 103 additions and 46 deletions

View File

@ -31,6 +31,7 @@ static const char* TAG = "test_event";
#define TEST_CONFIG_ITEMS_TO_REGISTER 5 #define TEST_CONFIG_ITEMS_TO_REGISTER 5
#define TEST_CONFIG_TASKS_TO_SPAWN 2 #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, /* General wait time for tests, e.g. waiting for background task to finish,
expected timeout, etc. */ expected timeout, etc. */
@ -44,12 +45,10 @@ static const char* TAG = "test_event";
#define TEST_SETUP() \ #define TEST_SETUP() \
ESP_LOGI(TAG, "initializing test"); \ ESP_LOGI(TAG, "initializing test"); \
size_t free_mem_before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); \ size_t free_mem_before = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); \
test_setup(); \
s_test_core_id = xPortGetCoreID(); \ s_test_core_id = xPortGetCoreID(); \
s_test_priority = uxTaskPriorityGet(NULL); s_test_priority = uxTaskPriorityGet(NULL);
#define TEST_TEARDOWN() \ #define TEST_TEARDOWN() \
test_teardown(); \
vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); \ vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT)); \
TEST_ASSERT_EQUAL(free_mem_before, heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); TEST_ASSERT_EQUAL(free_mem_before, heap_caps_get_free_size(MALLOC_CAP_DEFAULT));
@ -101,9 +100,11 @@ static UBaseType_t s_test_priority;
ESP_EVENT_DECLARE_BASE(s_test_base1); ESP_EVENT_DECLARE_BASE(s_test_base1);
ESP_EVENT_DECLARE_BASE(s_test_base2); 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_base1);
ESP_EVENT_DEFINE_BASE(s_test_base2); ESP_EVENT_DEFINE_BASE(s_test_base2);
ESP_EVENT_DEFINE_BASE(s_semphr_base);
enum { enum {
TEST_EVENT_BASE1_EV1, TEST_EVENT_BASE1_EV1,
@ -144,6 +145,13 @@ static esp_event_loop_args_t test_event_get_default_loop_args(void)
return loop_config; 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) 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) { if (!event_handler_arg) {
@ -163,6 +171,12 @@ static void test_event_simple_handler(void* event_handler_arg, esp_event_base_t
xSemaphoreGive(arg->mutex); 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) 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; int *arg = (int*) event_handler_arg;
@ -304,17 +318,6 @@ static void test_post_from_handler_loop_task(void* args)
} }
} }
static void test_setup(void)
{
TEST_ASSERT_TRUE(TEST_CONFIG_TASKS_TO_SPAWN >= 2);
TEST_ESP_OK(esp_event_loop_create_default());
}
static void test_teardown(void)
{
TEST_ESP_OK(esp_event_loop_delete_default());
}
TEST_CASE("can create and delete event loops", "[event]") TEST_CASE("can create and delete event loops", "[event]")
{ {
/* this test aims to verify that: /* this test aims to verify that:
@ -815,6 +818,9 @@ TEST_CASE("can register/unregister handlers for all events/all events for a spec
loop_args.task_name = NULL; loop_args.task_name = NULL;
TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); 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) /* 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 * This aims to verify: 1) Handler's argument to be updated
* 2) Registration not to leak memory * 2) Registration not to leak memory
@ -828,6 +834,9 @@ TEST_CASE("can register/unregister handlers for all events/all events for a spec
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_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)); 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, 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, 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_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_event_post_to(loop, ESP_EVENT_ANY_BASE, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY));
@ -837,14 +846,18 @@ TEST_CASE("can register/unregister handlers for all events/all events for a spec
// Post unknown events. Respective loop level and base level handlers should still execute. // 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_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_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));
TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); while (xSemaphoreTake(sem, 0) != pdTRUE) {
TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10)));
}
TEST_ASSERT_EQUAL(9, count); // 3 + 3 + 2 + 1 TEST_ASSERT_EQUAL(10, count); // 3 + 3 + 2 + 1 + 1
TEST_ESP_OK(esp_event_loop_delete(loop)); TEST_ESP_OK(esp_event_loop_delete(loop));
vSemaphoreDelete(arg.mutex); vSemaphoreDelete(arg.mutex);
vSemaphoreDelete(sem);
TEST_TEARDOWN(); TEST_TEARDOWN();
} }
@ -861,36 +874,51 @@ TEST_CASE("can unregister handler", "[event]")
loop_args.task_name = NULL; loop_args.task_name = NULL;
TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop)); TEST_ESP_OK(esp_event_loop_create(&loop_args, &loop));
int count = 0; SemaphoreHandle_t sem = xSemaphoreCreateBinary();
TEST_ASSERT(sem);
simple_arg_t arg = { uint32_t handler_0_record;
.data = &count, uint32_t handler_1_record;
.mutex = xSemaphoreCreateMutex()
};
TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); uint32_t a = 0xA;
TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE1_EV1, test_event_simple_handler, &arg)); uint32_t b = 0xB;
uint32_t c = 0xC;
uint32_t d = 0xD;
TEST_ESP_OK(esp_event_post_to(loop, s_test_base1, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); 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_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); TEST_ESP_OK(esp_event_handler_register_with(loop, s_test_base2, TEST_EVENT_BASE1_EV1, pass_4byte_event, &handler_1_record));
TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); // 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(2, count); 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));
TEST_ESP_OK(esp_event_handler_unregister_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, test_event_simple_handler)); while (xSemaphoreTake(sem, 0) != pdTRUE) {
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_EV1, NULL, 0, portMAX_DELAY)); TEST_ASSERT_EQUAL(0xA, handler_0_record);
TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, NULL, 0, portMAX_DELAY)); TEST_ASSERT_EQUAL(0xB, handler_1_record);
TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); TEST_ESP_OK(esp_event_handler_unregister_with(loop, s_test_base1, TEST_EVENT_BASE1_EV1, pass_4byte_event));
TEST_ASSERT_EQUAL(3, count); 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_ESP_OK(esp_event_loop_delete(loop));
vSemaphoreDelete(arg.mutex);
TEST_TEARDOWN(); TEST_TEARDOWN();
} }
@ -1029,7 +1057,8 @@ TEST_CASE("handler instance can unregister itself", "[event]")
TEST_TEARDOWN(); TEST_TEARDOWN();
} }
TEST_CASE("can exit running loop at approximately the set amount of time", "[event]") // 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 /* this test aims to verify that running loop does not block indefinitely in cases where
* events are posted frequently */ * events are posted frequently */
@ -1082,7 +1111,7 @@ TEST_CASE("can exit running loop at approximately the set amount of time", "[eve
diff = (esp_timer_get_time() - start); diff = (esp_timer_get_time() - start);
// Threshold is 25 percent. // Threshold is 25 percent.
TEST_ASSERT(diff < runtime_us * 1.25f); TEST_ASSERT_LESS_THAN_INT(runtime_us * 1.25f, diff);
// Verify that the post task still continues // Verify that the post task still continues
TEST_ASSERT_NOT_EQUAL(pdTRUE, xSemaphoreTake(post_event_arg.done, pdMS_TO_TICKS(10))); TEST_ASSERT_NOT_EQUAL(pdTRUE, xSemaphoreTake(post_event_arg.done, pdMS_TO_TICKS(10)));
@ -1921,6 +1950,9 @@ TEST_CASE("events are dispatched in the order they are registered", "[event]")
int data_arr[12] = {0}; 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, 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, 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_base1, ESP_EVENT_ANY_ID, test_event_ordered_dispatch, id_arr + 2));
@ -1929,6 +1961,9 @@ TEST_CASE("events are dispatched in the order they are registered", "[event]")
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_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)); 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); esp_event_dump(stdout);
ordered_data_t data = { ordered_data_t data = {
@ -1943,15 +1978,20 @@ TEST_CASE("events are dispatched in the order they are registered", "[event]")
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_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)); TEST_ESP_OK(esp_event_post_to(loop, s_test_base2, TEST_EVENT_BASE1_EV1, &dptr, sizeof(dptr), portMAX_DELAY));
TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10))); // 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));
// Expected data executing the posts above while (xSemaphoreTake(sem, 0) != pdTRUE) {
int ref_arr[12] = {1, 3, 5, 1, 2, 4, 1, 2, 6, 0, 1, 5}; TEST_ESP_OK(esp_event_loop_run(loop, pdMS_TO_TICKS(10)));
for (int i = 0; i < 12; i++) {
TEST_ASSERT_EQUAL(ref_arr[i], data_arr[i]);
} }
// 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_ESP_OK(esp_event_loop_delete(loop));
TEST_TEARDOWN(); TEST_TEARDOWN();
@ -2013,6 +2053,8 @@ bool test_event_on_timer_alarm(gptimer_handle_t timer, const gptimer_alarm_event
TEST_CASE("can post events from interrupt handler", "[event]") TEST_CASE("can post events from interrupt handler", "[event]")
{ {
TEST_ESP_OK(esp_event_loop_create_default());
/* Lazy allocated resources in gptimer/intr_alloc */ /* Lazy allocated resources in gptimer/intr_alloc */
set_leak_threshold(-150); set_leak_threshold(-150);
@ -2038,17 +2080,18 @@ TEST_CASE("can post events from interrupt handler", "[event]")
TEST_ESP_OK(gptimer_enable(gptimer)); TEST_ESP_OK(gptimer_enable(gptimer));
TEST_ESP_OK(gptimer_start(gptimer)); TEST_ESP_OK(gptimer_start(gptimer));
TEST_SETUP();
TEST_ESP_OK(esp_event_handler_register(s_test_base1, TEST_EVENT_BASE1_EV1, TEST_ESP_OK(esp_event_handler_register(s_test_base1, TEST_EVENT_BASE1_EV1,
test_handler_post_from_isr, &sem)); test_handler_post_from_isr, &sem));
xSemaphoreTake(sem, portMAX_DELAY); xSemaphoreTake(sem, portMAX_DELAY);
TEST_TEARDOWN(); vTaskDelay(pdMS_TO_TICKS(TEST_CONFIG_TEARDOWN_WAIT));
vSemaphoreDelete(sem); vSemaphoreDelete(sem);
TEST_ESP_OK(gptimer_disable(gptimer)); TEST_ESP_OK(gptimer_disable(gptimer));
TEST_ESP_OK(gptimer_del_timer(gptimer)); TEST_ESP_OK(gptimer_del_timer(gptimer));
TEST_ESP_OK(esp_event_loop_delete_default());
} }
#endif // CONFIG_ESP_EVENT_POST_FROM_ISR #endif // CONFIG_ESP_EVENT_POST_FROM_ISR

View File

@ -14,10 +14,24 @@ def test_esp_event(dut: Dut) -> None:
@pytest.mark.esp32 @pytest.mark.esp32
@pytest.mark.esp32c3
@pytest.mark.host_test @pytest.mark.host_test
@pytest.mark.qemu @pytest.mark.qemu
def test_esp_event_qemu(dut: Dut) -> None: @pytest.mark.parametrize('qemu_extra_args', [
'-global driver=timer.esp32.timg,property=wdt_disable,value=true',
], indirect=True) # need to disable wdt since it is not synchronized with target cpu clock on QEMU for ESP32
def test_esp_event_qemu_esp32(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.esp32c3
@pytest.mark.host_test
@pytest.mark.qemu
@pytest.mark.parametrize('qemu_extra_args', [
'-icount 3',
], indirect=True) # need to add -icount 3 to make WDT accurate on QEMU for ESP32-C3
def test_esp_event_qemu_esp32c3(dut: Dut) -> None:
for case in dut.test_menu: for case in dut.test_menu:
if 'qemu-ignore' not in case.groups and not case.is_ignored and case.type == 'normal': if 'qemu-ignore' not in case.groups and not case.is_ignored and case.type == 'normal':
dut._run_normal_case(case) dut._run_normal_case(case)