diff --git a/components/touch_element/test/CMakeLists.txt b/components/touch_element/test/CMakeLists.txt new file mode 100644 index 0000000000..fbdfd322bd --- /dev/null +++ b/components/touch_element/test/CMakeLists.txt @@ -0,0 +1,7 @@ +if(IDF_TARGET STREQUAL "esp32s2") + idf_component_register(SRCS "test_touch_element.c" + "test_touch_button.c" + "test_touch_slider.c" + "test_touch_matrix.c" + PRIV_REQUIRES unity touch_element) +endif() diff --git a/components/touch_element/test/component.mk b/components/touch_element/test/component.mk new file mode 100644 index 0000000000..888b7d448c --- /dev/null +++ b/components/touch_element/test/component.mk @@ -0,0 +1,5 @@ +# +# Component Makefile +# +# Touch Element lib is not supported in GNU Make build system +COMPONENT_CONFIG_ONLY := 1 diff --git a/components/touch_element/test/test_touch_button.c b/components/touch_element/test/test_touch_button.c new file mode 100644 index 0000000000..5bbd728b9f --- /dev/null +++ b/components/touch_element/test/test_touch_button.c @@ -0,0 +1,570 @@ +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "unity.h" + +#include "touch_element/touch_element_private.h" +#include "touch_element/touch_button.h" + +static portMUX_TYPE test_button_spinlock = portMUX_INITIALIZER_UNLOCKED; +#define TEST_BUTTON_ENTER_CRITICAL() portENTER_CRITICAL(&test_button_spinlock) +#define TEST_BUTTON_EXIT_CRITICAL() portEXIT_CRITICAL(&test_button_spinlock) + +static const touch_pad_t button_channel_array[14] = { + TOUCH_PAD_NUM1, + TOUCH_PAD_NUM2, + TOUCH_PAD_NUM3, + TOUCH_PAD_NUM4, + TOUCH_PAD_NUM5, + TOUCH_PAD_NUM6, + TOUCH_PAD_NUM7, + TOUCH_PAD_NUM8, + TOUCH_PAD_NUM9, + TOUCH_PAD_NUM10, + TOUCH_PAD_NUM11, + TOUCH_PAD_NUM12, + TOUCH_PAD_NUM13, + TOUCH_PAD_NUM14, +}; +const uint8_t BUTTON_CHANNEL_NUM = sizeof(button_channel_array) / sizeof(touch_pad_t); + +typedef struct { + QueueHandle_t valid_msg_handle; + SemaphoreHandle_t response_sig_handle; +} test_monitor_t; + +typedef struct { + QueueHandle_t valid_msg_handle; + SemaphoreHandle_t response_sig_handle; + touch_button_handle_t button_handle; +} test_concurrent_monitor_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +void test_button_event_simulator(touch_button_handle_t button_handle, touch_button_event_t button_event); +void test_button_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message); +static void test_button_callback_check(touch_button_handle_t current_handle, touch_button_message_t *current_message, touch_elem_message_t *valid_message); +void test_button_event_trigger_and_check(touch_button_handle_t handle, touch_button_event_t button_event); +void test_button_callback_trigger_and_check(touch_button_handle_t handle, touch_button_event_t button_event, bool should_trigger, test_monitor_t *monitor); +/* ------------------------------------------------ Dispatch method test -------------------------------------------- */ +static void test_button_disp_event(void); +static void test_button_disp_callback(void); +void test_button_handler(touch_button_handle_t handle, touch_button_message_t *message, void *arg); +/* ------------------------------------------------ Run-time test --------------------------------------------------- */ +static void test_button_event_change_lp(void); +static void test_button_callback_change_lp(void); +static void test_button_change_lp_handler(touch_button_handle_t handle, touch_button_message_t *message, void *arg); +/* ------------------------------------------------ Concurrent test ------------------------------------------------- */ +static void test_button_event_concurrent(void); +static void test_button_random_trigger_concurrent(void); +void test_random_trigger_concurrent_task(void *arg); +static void random_trigger_concurrent_handler(touch_button_handle_t handle, touch_button_message_t *message, void *arg); +/* ------------------------------------------------------------------------------------------------------------------ */ + +TEST_CASE("Touch button dispatch methods test", "[button][touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_button_disp_event(); + test_button_disp_callback(); + touch_element_uninstall(); +} + +TEST_CASE("Touch button run-time test", "[button][touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_button_event_change_lp(); + test_button_callback_change_lp(); + touch_element_uninstall(); +} + +TEST_CASE("Touch button concurrent test", "[button][touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_button_event_concurrent(); + test_button_random_trigger_concurrent(); + touch_element_uninstall(); +} + +void test_button_event_simulator(touch_button_handle_t button_handle, touch_button_event_t button_event) +{ + te_button_handle_t te_button = (te_button_handle_t) button_handle; + touch_pad_t channel = te_button->device->channel; + if (button_event == TOUCH_BUTTON_EVT_ON_PRESS) { + touch_pad_set_cnt_mode(channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); + } else if (button_event == TOUCH_BUTTON_EVT_ON_RELEASE) { + touch_pad_set_cnt_mode(channel, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + } else { + touch_pad_set_cnt_mode(channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); //LongPress + } +} + +void test_button_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message) +{ + TEST_ASSERT_MESSAGE(current_message->handle == valid_message->handle, "check handle failed"); + TEST_ASSERT_MESSAGE(current_message->element_type == valid_message->element_type, "check element type failed"); + const touch_button_message_t *valid_button_message = touch_button_get_message(valid_message); + const touch_button_message_t *current_button_message = touch_button_get_message(current_message); + TEST_ASSERT_MESSAGE(current_button_message->event == valid_button_message->event, "check event failed"); +} + +static void test_button_callback_check(touch_button_handle_t current_handle, touch_button_message_t *current_message, touch_elem_message_t *valid_message) +{ + const touch_button_message_t *valid_button_message = touch_button_get_message(valid_message); + TEST_ASSERT_MESSAGE(valid_message->handle == current_handle, "check handle failed"); + TEST_ASSERT_MESSAGE(valid_message->element_type == TOUCH_ELEM_TYPE_BUTTON, "check element type failed"); + TEST_ASSERT_MESSAGE(valid_button_message->event == current_message->event, "check event failed"); +} + +void test_button_event_trigger_and_check(touch_button_handle_t handle, touch_button_event_t button_event) +{//TODO: refactor this with a constructor + touch_elem_message_t valid_message = { + .handle = handle, + .element_type = TOUCH_ELEM_TYPE_BUTTON, + .arg = NULL, + }; + touch_button_message_t button_message = { + .event = button_event + }; + memcpy(valid_message.child_msg, &button_message, sizeof(touch_button_message_t)); //Construct valid_message + + test_button_event_simulator(handle, button_event); //Trigger signal + + touch_elem_message_t current_message; + te_button_handle_t te_button = handle; + esp_err_t ret = touch_element_message_receive(¤t_message, pdMS_TO_TICKS(2 * te_button->trigger_thr * 10)); + TEST_ASSERT_MESSAGE(ret == ESP_OK, "button event receive timeout"); + + test_button_event_check(&valid_message, ¤t_message); //Verification +} + +void test_button_callback_trigger_and_check(touch_button_handle_t handle, touch_button_event_t button_event, bool should_trigger, test_monitor_t *monitor) +{ + if (should_trigger) { + touch_elem_message_t valid_message = { + .handle = handle, + .element_type = TOUCH_ELEM_TYPE_BUTTON, + .arg = NULL + }; + touch_button_message_t button_message = { + .event = button_event + }; + memcpy(valid_message.child_msg, &button_message, sizeof(touch_button_message_t)); //Construct valid_message + xQueueSend(monitor->valid_msg_handle, &valid_message, portMAX_DELAY); + } + + test_button_event_simulator(handle, button_event); //Trigger signal + + te_button_handle_t te_button = handle; + if (should_trigger) { //Verification + BaseType_t os_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(2 * te_button->trigger_thr * 10)); + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "Button queue timeout"); + } else { + BaseType_t os_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(500)); + TEST_ASSERT_MESSAGE(os_ret == pdFALSE, "Button invalid trigger"); + } +} + +static void test_button_disp_event(void) +{ + touch_button_handle_t button_handle[BUTTON_CHANNEL_NUM]; + touch_button_global_config_t global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&global_config)); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = 0.1F + }; + TEST_ESP_OK(touch_button_create(&button_config, &button_handle[i])); + TEST_ESP_OK(touch_button_subscribe_event(button_handle[i], + TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE | TOUCH_ELEM_EVENT_ON_LONGPRESS, + (void *) button_channel_array[i])); + TEST_ESP_OK(touch_button_set_longpress(button_handle[i], 300)); + TEST_ESP_OK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_EVENT)); + } + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + //10 times random press/longpress/release test + printf("Touch button event test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch button event test... (%d/10)\n", i + 1); + touch_button_handle_t current_handle = button_handle[random() % 14]; + test_button_event_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_PRESS); + test_button_event_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_LONGPRESS); + test_button_event_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_RELEASE); + } + printf("Touch button event test finish\n"); + TEST_ESP_OK(touch_element_stop()); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_button_delete(button_handle[i])); + } + touch_button_uninstall(); +} + +static void test_button_disp_callback(void) +{ + test_monitor_t monitor; + touch_button_handle_t button_handle[BUTTON_CHANNEL_NUM]; + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + TEST_ASSERT(monitor.valid_msg_handle != NULL || monitor.response_sig_handle != NULL); + + touch_button_global_config_t button_init = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&button_init)); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = 0.1F + }; + TEST_ESP_OK(touch_button_create(&button_config, &button_handle[i])); + TEST_ESP_OK(touch_button_subscribe_event(button_handle[i], + TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE | TOUCH_ELEM_EVENT_ON_LONGPRESS, + (void *) &monitor)); + TEST_ESP_OK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_button_set_callback(button_handle[i], &test_button_handler)); + TEST_ESP_OK(touch_button_set_longpress(button_handle[i], 300)); + } + TEST_ESP_OK(touch_element_start()); + + srandom((unsigned int)time(NULL)); + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + //10 times random press/longpress/release test + printf("Touch button callback test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch button callback test... (%d/10)\n", i + 1); + touch_button_handle_t current_handle = button_handle[random() % 14]; + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_PRESS, true, &monitor); + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_LONGPRESS, true, &monitor); + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_RELEASE, true, &monitor); + } + printf("Touch button callback test finish\n"); + TEST_ESP_OK(touch_element_stop()); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_button_delete(button_handle[i])); + } + touch_button_uninstall(); + vQueueDelete(monitor.valid_msg_handle); + vSemaphoreDelete(monitor.response_sig_handle); +} + +void test_button_handler(touch_button_handle_t handle, touch_button_message_t *message, void *arg) +{ + test_monitor_t *monitor = (test_monitor_t *)arg; + touch_elem_message_t valid_message; + BaseType_t os_ret = xQueueReceive(monitor->valid_msg_handle, &valid_message, pdMS_TO_TICKS(200)); //Get the valid message for the verification, 500ms timeout + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "test_button_handler: queue timeout"); + test_button_callback_check(handle, message, &valid_message); + xSemaphoreGive(monitor->response_sig_handle); +} + +static void test_button_event_change_lp(void) +{ + touch_button_handle_t button_handle[BUTTON_CHANNEL_NUM]; + touch_button_global_config_t global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&global_config)); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = 0.1F + }; + TEST_ESP_OK(touch_button_create(&button_config, &button_handle[i])); + TEST_ESP_OK(touch_button_subscribe_event(button_handle[i], TOUCH_ELEM_EVENT_ON_LONGPRESS, NULL)); + TEST_ESP_OK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_EVENT)); + } + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + //10 times random press/longpress/release test + printf("Touch button event change longtime test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch button event change longtime test... (%d/10)\n", i + 1); + esp_err_t ret; + uint8_t channel_index = random() % BUTTON_CHANNEL_NUM; + touch_elem_message_t valid_message = { + .handle = button_handle[channel_index], + .element_type = TOUCH_ELEM_TYPE_BUTTON, + .arg = NULL + }; + touch_button_message_t button_message = { + .event = TOUCH_BUTTON_EVT_ON_LONGPRESS + }; + memcpy(valid_message.child_msg, &button_message, sizeof(touch_button_message_t)); //Construct valid_message + + TEST_ESP_OK(touch_button_set_longpress(valid_message.handle, 200 + (i + 1) * 50)); + test_button_event_simulator(valid_message.handle, button_message.event); //Trigger signal + + touch_elem_message_t current_message; + ret = touch_element_message_receive(¤t_message, pdMS_TO_TICKS(10 * 1000)); + TEST_ASSERT_MESSAGE(ret == ESP_OK, "button event LongPress timeout"); + test_button_event_check(&valid_message, ¤t_message); //Verification + + test_button_event_simulator(valid_message.handle, TOUCH_BUTTON_EVT_ON_RELEASE); //Release the button. + } + printf("Touch button event change longtime test finish\n"); + TEST_ESP_OK(touch_element_stop()); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_button_delete(button_handle[i])); + } + touch_button_uninstall(); +} + +static void test_button_callback_change_lp(void) +{ + test_monitor_t monitor; + touch_button_handle_t button_handle[BUTTON_CHANNEL_NUM]; + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + TEST_ASSERT(monitor.valid_msg_handle != NULL || monitor.response_sig_handle != NULL); + + touch_button_global_config_t global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&global_config)); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = 0.1F + }; + TEST_ESP_OK(touch_button_create(&button_config, &button_handle[i])); + TEST_ESP_OK(touch_button_subscribe_event(button_handle[i], TOUCH_ELEM_EVENT_ON_LONGPRESS, (void *)&monitor)); + TEST_ESP_OK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_button_set_callback(button_handle[i], &test_button_change_lp_handler)); + } + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + //10 times random press/longpress/release test + printf("Touch button event change longtime test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch button event change longtime test... (%d/10)\n", i + 1); + uint8_t channel_index = 5; //Always this channel + touch_elem_message_t valid_message = { + .handle = button_handle[channel_index], + .element_type = TOUCH_ELEM_TYPE_BUTTON, + .arg = NULL, + }; + touch_button_message_t button_message = { + .event = TOUCH_BUTTON_EVT_ON_LONGPRESS + }; + memcpy(valid_message.child_msg, &button_message, sizeof(touch_button_message_t)); //Construct valid_message + + xQueueSend(monitor.valid_msg_handle, &valid_message, portMAX_DELAY); + test_button_event_simulator(button_handle[channel_index], button_message.event); + + BaseType_t os_ret = xSemaphoreTake(monitor.response_sig_handle, pdMS_TO_TICKS(10 * 1000)); //100ms timeout + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "Button LongPress queue timeout"); + test_button_event_simulator(valid_message.handle, TOUCH_BUTTON_EVT_ON_RELEASE); //Reset hardware + } + printf("Touch button event change longtime test finish\n"); + TEST_ESP_OK(touch_element_stop()); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_button_delete(button_handle[i])); + } + touch_button_uninstall(); +} + +static void test_button_change_lp_handler(touch_button_handle_t handle, touch_button_message_t *message, void *arg) +{ + test_monitor_t *monitor = (test_monitor_t *)arg; + touch_elem_message_t valid_message; + BaseType_t os_ret = xQueueReceive(monitor->valid_msg_handle, &valid_message, pdMS_TO_TICKS(200)); //Get the valid message for the verification, 500ms timeout + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "test_button_handler: queue timeout"); + test_button_callback_check(handle, message, &valid_message); + xSemaphoreGive(monitor->response_sig_handle); + TEST_ESP_OK(touch_button_set_longpress(valid_message.handle, 300)); // Always 300ms +} + +static void test_button_event_concurrent(void) +{ + touch_button_handle_t button_handle[BUTTON_CHANNEL_NUM]; + touch_button_global_config_t global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&global_config)); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = 0.1F + }; + TEST_ESP_OK(touch_button_create(&button_config, &button_handle[i])); + TEST_ESP_OK(touch_button_subscribe_event(button_handle[i], TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, NULL)); + TEST_ESP_OK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_EVENT)); + } + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + //10 times random press/longpress/release test + printf("Touch button event concurrent test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch button event concurrent test... (%d/10)\n", i + 1); + esp_err_t ret; + uint32_t message_count = 0; + touch_elem_message_t current_message; + + TEST_BUTTON_ENTER_CRITICAL(); + for (int idx = 0; idx < BUTTON_CHANNEL_NUM; idx++) { + test_button_event_simulator(button_handle[idx], TOUCH_BUTTON_EVT_ON_PRESS); //All channels trigger + } + TEST_BUTTON_EXIT_CRITICAL(); + message_count = 0; + do { + ret = touch_element_message_receive(¤t_message, pdMS_TO_TICKS(500)); + if (ret == ESP_OK) { + message_count++; + } + } while (ret == ESP_OK); + TEST_ASSERT_MESSAGE(message_count == BUTTON_CHANNEL_NUM, "button concurrent Press failed"); + + TEST_BUTTON_ENTER_CRITICAL(); + for (int idx = 0; idx < BUTTON_CHANNEL_NUM; idx++) { + test_button_event_simulator(button_handle[idx], TOUCH_BUTTON_EVT_ON_RELEASE); //All channels trigger + } + TEST_BUTTON_EXIT_CRITICAL(); + message_count = 0; + do { + ret = touch_element_message_receive(¤t_message, pdMS_TO_TICKS(500)); + if (ret == ESP_OK) { + message_count++; + } + } while (ret == ESP_OK); + TEST_ASSERT_MESSAGE(message_count == BUTTON_CHANNEL_NUM, "button concurrent Release failed"); + } + printf("Touch button event concurrent test finish\n"); + TEST_ESP_OK(touch_element_stop()); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_button_delete(button_handle[i])); + } + touch_button_uninstall(); +} + +static void test_button_random_trigger_concurrent(void) +{ + uint64_t sem_and_monitor[BUTTON_CHANNEL_NUM]; + printf("Touch button random trigger concurrent test start\n"); + test_concurrent_monitor_t monitor[BUTTON_CHANNEL_NUM]; + + SemaphoreHandle_t count_sem = xSemaphoreCreateCounting(BUTTON_CHANNEL_NUM, 0); + + touch_button_global_config_t global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&global_config)); + for (uint32_t i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = 0.1F + }; + monitor[i].response_sig_handle = xSemaphoreCreateBinary(); + monitor[i].valid_msg_handle = xQueueCreate(BUTTON_CHANNEL_NUM, sizeof(touch_elem_message_t)); + TEST_ASSERT(monitor[i].valid_msg_handle != NULL && monitor[i].response_sig_handle != NULL); + uintptr_t temp_count_sem = (uint32_t)count_sem; + uintptr_t temp_monitor = (uint32_t)&monitor[i]; //Prevent compiler warning + sem_and_monitor[i] = (uint64_t)(((uint64_t)temp_count_sem << 32) | (uint64_t) temp_monitor); + TEST_ESP_OK(touch_button_create(&button_config, &monitor[i].button_handle)); + TEST_ESP_OK(touch_button_subscribe_event(monitor[i].button_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_LONGPRESS | TOUCH_ELEM_EVENT_ON_RELEASE, (void *)&sem_and_monitor[i])); + TEST_ESP_OK(touch_button_set_longpress(monitor[i].button_handle, 500)); + TEST_ESP_OK(touch_button_set_dispatch_method(monitor[i].button_handle, TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_button_set_callback(monitor[i].button_handle, &random_trigger_concurrent_handler)); + } + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + for (uint32_t i = 0; i < BUTTON_CHANNEL_NUM; i++) { + BaseType_t os_ret = xTaskCreate(test_random_trigger_concurrent_task, "test_random_trigger_concurrent_task", 1024 * 4, (void *)&sem_and_monitor[i], 10, NULL); + TEST_ASSERT(os_ret == pdPASS); + } + + + uint32_t run_count = 0; + while (1) { + if (run_count++ % 1000 == 0) { + printf("Touch button random trigger concurrent test running... (1/1)\n"); + } + uint8_t count = uxSemaphoreGetCount(count_sem); + if (count == BUTTON_CHANNEL_NUM) { + vTaskDelay(1); //Let IDLE task running and get tasks cleanup + break; + } + vTaskDelay(1); + } + + TEST_ESP_OK(touch_element_stop()); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + vQueueDelete(monitor[i].valid_msg_handle); + vSemaphoreDelete(monitor[i].response_sig_handle); + TEST_ESP_OK(touch_button_delete(monitor[i].button_handle)); + } + touch_button_uninstall(); + printf("Touch button random trigger concurrent test stop\n"); +} + +void test_random_trigger_concurrent_task(void *arg) +{ + uintptr_t temp_monitor = *((uint32_t *) arg); + uintptr_t temp_count_sem = (*((uint64_t *) arg) >> 32); //Prevent compiler warning + test_concurrent_monitor_t *monitor = (test_concurrent_monitor_t *)temp_monitor; + SemaphoreHandle_t count_sem = (SemaphoreHandle_t) temp_count_sem; + uint32_t start_delay_time = (esp_random() % 100) * 10; + vTaskDelay(pdMS_TO_TICKS(start_delay_time)); + + touch_elem_message_t valid_message = { + .handle = monitor->button_handle, + .element_type = TOUCH_ELEM_TYPE_BUTTON, + .arg = NULL, + }; + touch_button_message_t button_message; + button_message.event = TOUCH_BUTTON_EVT_ON_PRESS; + memcpy(valid_message.child_msg, &button_message, sizeof(touch_button_message_t)); //Construct valid_message + xQueueSend(monitor->valid_msg_handle, &valid_message, portMAX_DELAY); + test_button_event_simulator(valid_message.handle, button_message.event); //Trigger signal + BaseType_t res_sem_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(1000)); + TEST_ASSERT_MESSAGE(res_sem_ret == pdPASS, "Response timeout"); + + uint32_t hold_state_time_ms = (esp_random() % 100) * 10 + 100; + te_button_handle_t te_button = (te_button_handle_t) valid_message.handle; + if ((int)(hold_state_time_ms - te_button->trigger_thr * 10) > 50) { //should raise longpress event + button_message.event = TOUCH_BUTTON_EVT_ON_LONGPRESS; + memcpy(valid_message.child_msg, &button_message, sizeof(touch_button_message_t)); //Construct valid_message + xQueueSend(monitor->valid_msg_handle, &valid_message, portMAX_DELAY); + test_button_event_simulator(valid_message.handle, button_message.event); //Trigger signal + res_sem_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(1000)); //+100 make sure it will really raise longpress event + TEST_ASSERT_MESSAGE(res_sem_ret == pdPASS, "Response timeout"); + } else { //should not raise longpress event + //Do nothing + } + + button_message.event = TOUCH_BUTTON_EVT_ON_RELEASE; + memcpy(valid_message.child_msg, &button_message, sizeof(touch_button_message_t)); //Construct valid_message + xQueueSend(monitor->valid_msg_handle, &valid_message, portMAX_DELAY); + test_button_event_simulator(valid_message.handle, button_message.event); //Trigger signal + res_sem_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(1000)); + TEST_ASSERT_MESSAGE(res_sem_ret == pdPASS, "Response timeout"); + + xSemaphoreGive(count_sem); + vTaskDelete(NULL); +} + +static void random_trigger_concurrent_handler(touch_button_handle_t handle, touch_button_message_t *message, void *arg) +{ + uintptr_t temp_monitor = *((uint32_t *) arg); //Prevent compiler warning + test_concurrent_monitor_t *monitor = (test_concurrent_monitor_t *) temp_monitor; + touch_elem_message_t valid_message; + BaseType_t os_ret = xQueueReceive(monitor->valid_msg_handle, &valid_message, pdMS_TO_TICKS(1000)); + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "valid message timeout"); + const touch_button_message_t *button_message = touch_button_get_message(&valid_message); + if (button_message->event == TOUCH_BUTTON_EVT_ON_LONGPRESS) { + touch_button_set_longpress(handle, portMAX_DELAY); //Prevent button triggers LongPress event again + } + TEST_ASSERT_MESSAGE(handle == valid_message.handle, "check handle failed"); + TEST_ASSERT_MESSAGE(valid_message.element_type == TOUCH_ELEM_TYPE_BUTTON, "check element type failed"); + TEST_ASSERT_MESSAGE(message->event == button_message->event, "check event failed"); + xSemaphoreGive(monitor->response_sig_handle); +} diff --git a/components/touch_element/test/test_touch_element.c b/components/touch_element/test/test_touch_element.c new file mode 100644 index 0000000000..883195021a --- /dev/null +++ b/components/touch_element/test/test_touch_element.c @@ -0,0 +1,412 @@ +/* ---------------------------------------------------------- README ------------------------------------------------ + * This doc is aimed at explain some important code block and do some records for the test result, if developer or + * test-owner has some question in reading this code implementation, please read it first. + * + * CODE Block: + * `code-block-1`: Touch Element lib need some time to finish the initialization so as to configure the right threshold. + * Since some hardware issue(Must to pass 2 times "meas_done" interrupt), Touch Element lib will spend + * some time(Maybe <30ms) to finish the initialization, every operations (interrupts) happen to touch + * sensor will be ignored before initialization. That's why "vTaskDelay()" could be saw in after call + * "touch_element_start()". However, this just for the unit test, in the real application, users don't + * need to delay something. + * + * NOTES: + * `Simulator`: Currently the event simulator depend on the touch sensor driver and play some "hack" in some register so + * as to raise a FAKE interrupt. It means that Touch Element lib test case must be burned on dev-kit and keep + * the touch channel as clean as possible, ESP32-S2-Saola is good test board. //TODO: Remove the dependent of touch sensor driver. + ------------------------------------------------------------------------------------------------------------------ */ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "unity.h" + +#include "touch_element/touch_button.h" +#include "touch_element/touch_slider.h" +#include "touch_element/touch_matrix.h" + +typedef struct { + QueueHandle_t valid_msg_handle; + SemaphoreHandle_t response_sig_handle; +} test_monitor_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +extern void test_button_event_simulator(touch_button_handle_t button_handle, touch_button_event_t button_event); +extern void test_button_handler(touch_button_handle_t handle, touch_button_message_t *message, void *arg); +extern void test_button_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message); +extern void test_button_event_trigger_and_check(touch_button_handle_t handle, touch_button_event_t button_event); +extern void test_button_callback_trigger_and_check(touch_button_handle_t handle, touch_button_event_t button_event, bool should_trigger, test_monitor_t *monitor); +extern void test_slider_event_simulator(touch_slider_handle_t slider_handle, touch_slider_event_t slider_event, uint32_t random); +extern void test_slider_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message); +extern void test_matrix_event_simulator(touch_matrix_handle_t matrix_handle, touch_matrix_event_t matrix_event, uint32_t pos_index); +extern void test_matrix_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message); +/* ------------------------------------------------------------------------------------------------------------------ */ +static void test_waterproof_event_simulator(touch_pad_t guard_channel, touch_button_event_t guard_state); +static void test_system_waterproof_guard(void); +static void test_integrat_btn_sld_mat(void); +static void test_integration_monitor_task(void *arg); +/* ------------------------------------------------------------------------------------------------------------------ */ +TEST_CASE("Touch element integration test", "[touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_integrat_btn_sld_mat(); + touch_element_uninstall(); +} + +TEST_CASE("Touch element waterproof test", "[touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_system_waterproof_guard(); //TODO: add waterproof work with slider and matrix + touch_element_uninstall(); +} + +static void test_system_waterproof_guard(void) +{ + static const touch_pad_t button_channel_array[12] = { + TOUCH_PAD_NUM1, + TOUCH_PAD_NUM2, + TOUCH_PAD_NUM3, + TOUCH_PAD_NUM4, + TOUCH_PAD_NUM5, + TOUCH_PAD_NUM6, + TOUCH_PAD_NUM7, + TOUCH_PAD_NUM8, + TOUCH_PAD_NUM9, + TOUCH_PAD_NUM10, + TOUCH_PAD_NUM11, + TOUCH_PAD_NUM12 + }; + const uint8_t BUTTON_CHANNEL_NUM = sizeof(button_channel_array) / sizeof(touch_pad_t); + + test_monitor_t monitor; + touch_button_handle_t button_handle[BUTTON_CHANNEL_NUM]; + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + TEST_ASSERT(monitor.valid_msg_handle != NULL || monitor.response_sig_handle != NULL); + touch_button_global_config_t global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&global_config)); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = 0.1F + }; + TEST_ESP_OK(touch_button_create(&button_config, &button_handle[i])); + TEST_ESP_OK(touch_button_subscribe_event(button_handle[i], TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, (void *)&monitor)); + TEST_ESP_OK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_button_set_callback(button_handle[i], test_button_handler)); + } + printf("Touch Element waterproof guard sensor test start\n"); + + srandom((unsigned int)time(NULL)); + {//No use waterproof guard sensor + touch_elem_waterproof_config_t waterproof_config = { + .guard_channel = TOUCH_WATERPROOF_GUARD_NOUSE, + .guard_sensitivity = 0.0F + }; + TEST_ESP_OK(touch_element_waterproof_install(&waterproof_config)); + TEST_ESP_OK(touch_element_start()); + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + for (int i = 0; i < 10; i++) { //Start state test + printf("Touch Element waterproof no-use guard sensor test... (%d/10)\n", i + 1); + touch_button_handle_t current_handle = button_handle[random() % 12]; + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_PRESS, true, &monitor); + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_RELEASE, true, &monitor); + } + TEST_ESP_OK(touch_element_stop()); + touch_element_waterproof_uninstall(); + } + + {//Use waterproof guard sensor(Add all handles) + touch_elem_waterproof_config_t waterproof_config = { + .guard_channel = TOUCH_PAD_NUM13, + .guard_sensitivity = 0.1F + }; + TEST_ESP_OK(touch_element_waterproof_install(&waterproof_config)); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_element_waterproof_add(button_handle[i])); + } + TEST_ESP_OK(touch_element_start()); + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + for (int i = 0; i < 10; i++) { + printf("Touch Element waterproof use guard sensor random trigger test... (%d/10)\n", i + 1); + bool should_trigger = random() % 2; + if (should_trigger) { + touch_button_handle_t current_handle = button_handle[random() % 12]; + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_PRESS, should_trigger, &monitor); + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_RELEASE, should_trigger, &monitor); + } else { + test_waterproof_event_simulator(waterproof_config.guard_channel, TOUCH_BUTTON_EVT_ON_PRESS); //Waterproof guard sensor trigger + touch_button_handle_t current_handle = button_handle[random() % 12]; + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_PRESS, should_trigger, &monitor); + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_RELEASE, should_trigger, &monitor); + test_waterproof_event_simulator(waterproof_config.guard_channel, TOUCH_BUTTON_EVT_ON_RELEASE); //Waterproof guard sensor release + } + } + TEST_ESP_OK(touch_element_stop()); + touch_element_waterproof_uninstall(); + } + + {//Put half button handles into guard ring + const uint8_t protect_handle_threshold = BUTTON_CHANNEL_NUM / 2; + touch_elem_waterproof_config_t waterproof_config = { + .guard_channel = TOUCH_PAD_NUM13, + .guard_sensitivity = 0.1F + }; + TEST_ESP_OK(touch_element_waterproof_install(&waterproof_config)); + for (int i = 0; i < protect_handle_threshold; i++) { + TEST_ESP_OK(touch_element_waterproof_add(button_handle[i])); + } + TEST_ESP_OK(touch_element_start()); + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + for (int i = 0; i < 10; i++) { + printf("Touch Element waterproof use guard sensor test(guard sensor is triggered will half button handles)... (%d/10)\n", i + 1); + test_waterproof_event_simulator(waterproof_config.guard_channel, TOUCH_BUTTON_EVT_ON_PRESS); //Waterproof guard sensor trigger + uint32_t handle_index = random() % 12; + touch_button_handle_t current_handle = button_handle[handle_index]; + bool should_trigger = (handle_index < protect_handle_threshold) ? false : true; + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_PRESS, should_trigger, &monitor); + test_button_callback_trigger_and_check(current_handle, TOUCH_BUTTON_EVT_ON_RELEASE, should_trigger, &monitor); + test_waterproof_event_simulator(waterproof_config.guard_channel, TOUCH_BUTTON_EVT_ON_RELEASE); //Waterproof guard sensor release + } + TEST_ESP_OK(touch_element_stop()); + touch_element_waterproof_uninstall(); + } + + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_button_delete(button_handle[i])); + } + touch_button_uninstall(); + vQueueDelete(monitor.valid_msg_handle); + vSemaphoreDelete(monitor.response_sig_handle); + printf("Touch Element waterproof guard sensor test finish\n"); +} + +static void test_waterproof_event_simulator(touch_pad_t guard_channel, touch_button_event_t guard_state) +{ + if (guard_state == TOUCH_BUTTON_EVT_ON_PRESS) { + touch_pad_set_cnt_mode(guard_channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); + } else if (guard_state == TOUCH_BUTTON_EVT_ON_RELEASE) { + touch_pad_set_cnt_mode(guard_channel, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + } else { + printf("guard sensor simulator doesn't support this operation\n"); + } + /* Fixme: If the normal instance and guard sensor trigger at the same time, guard sensor will lock the state failed */ + vTaskDelay(pdMS_TO_TICKS(100)); +} + +static void test_integrat_btn_sld_mat(void) +{ + static const touch_pad_t button_channel_array[3] = { + TOUCH_PAD_NUM1, + TOUCH_PAD_NUM2, + TOUCH_PAD_NUM3 + }; + static const float button_sens_array[3] = { + 0.1F, + 0.1F, + 0.1F + }; + static const touch_pad_t slider_channel_array[5] = { + TOUCH_PAD_NUM4, + TOUCH_PAD_NUM5, + TOUCH_PAD_NUM6, + TOUCH_PAD_NUM7, + TOUCH_PAD_NUM8 + }; + static const float slider_sens_array[5] = { + 0.1F, + 0.1F, + 0.1F, + 0.1F, + 0.1F + }; + static const touch_pad_t x_axis_channel[3] = { + TOUCH_PAD_NUM9, + TOUCH_PAD_NUM10, + TOUCH_PAD_NUM11, + }; + static const touch_pad_t y_axis_channel[3] = { + TOUCH_PAD_NUM12, + TOUCH_PAD_NUM13, + TOUCH_PAD_NUM14, + }; + static const float x_axis_channel_sens[3] = { + 0.1F, + 0.1F, + 0.1F, + }; + static const float y_axis_channel_sens[3] = { + 0.1F, + 0.1F, + 0.1F, + }; + const uint8_t BUTTON_CHANNEL_NUM = sizeof(button_channel_array) / sizeof(touch_pad_t); + const uint8_t SLIDER_CHANNEL_NUM = sizeof(slider_channel_array) / sizeof(touch_pad_t); + const uint8_t MATRIX_CHANNEL_NUM_X = sizeof(x_axis_channel) / sizeof(touch_pad_t); + const uint8_t MATRIX_CHANNEL_NUM_Y = sizeof(y_axis_channel) / sizeof(touch_pad_t); + + printf("Integration test(button + slider + matrix) start\n"); + + BaseType_t os_ret; + touch_button_handle_t button_handle[BUTTON_CHANNEL_NUM]; + touch_slider_handle_t slider_handle; + touch_matrix_handle_t matrix_handle; + test_monitor_t monitor; + TaskHandle_t task_handle = NULL; + + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + os_ret = xTaskCreate(&test_integration_monitor_task, "test_integration_monitor_task", 4096, (void *)&monitor, 5, &task_handle); + TEST_ASSERT(monitor.valid_msg_handle != NULL && monitor.response_sig_handle != NULL && os_ret == pdPASS); + + touch_button_global_config_t button_global_config = TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG(); + touch_slider_global_config_t slider_global_config = TOUCH_SLIDER_GLOBAL_DEFAULT_CONFIG(); + touch_matrix_global_config_t matrix_global_config = TOUCH_MATRIX_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_button_install(&button_global_config)); + TEST_ESP_OK(touch_slider_install(&slider_global_config)); + TEST_ESP_OK(touch_matrix_install(&matrix_global_config)); + + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + touch_button_config_t button_config = { + .channel_num = button_channel_array[i], + .channel_sens = button_sens_array[i] + }; + TEST_ESP_OK(touch_button_create(&button_config, &button_handle[i])); + TEST_ESP_OK(touch_button_subscribe_event(button_handle[i], TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, NULL)); + TEST_ESP_OK(touch_button_set_dispatch_method(button_handle[i], TOUCH_ELEM_DISP_EVENT)); + } + + touch_slider_config_t slider_config = { + .channel_array = slider_channel_array, + .sensitivity_array = slider_sens_array, + .channel_num = SLIDER_CHANNEL_NUM, + .position_range = 101 + }; + TEST_ESP_OK(touch_slider_create(&slider_config, &slider_handle)); + TEST_ESP_OK(touch_slider_subscribe_event(slider_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, NULL)); + TEST_ESP_OK(touch_slider_set_dispatch_method(slider_handle, TOUCH_ELEM_DISP_EVENT)); + + touch_matrix_config_t matrix_config = { + .x_channel_array = x_axis_channel, + .y_channel_array = y_axis_channel, + .x_sensitivity_array = x_axis_channel_sens, + .y_sensitivity_array = y_axis_channel_sens, + .x_channel_num = MATRIX_CHANNEL_NUM_X, + .y_channel_num = MATRIX_CHANNEL_NUM_Y + }; + TEST_ESP_OK(touch_matrix_create(&matrix_config, &matrix_handle)); + TEST_ESP_OK(touch_matrix_subscribe_event(matrix_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, NULL)); + TEST_ESP_OK(touch_matrix_set_dispatch_method(matrix_handle, TOUCH_ELEM_DISP_EVENT)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + for (int i = 0; i < 30; i++) { + printf("Integration test... (%d/30)\n", i + 1); + touch_elem_message_t valid_message; + valid_message.element_type = (random() % (TOUCH_ELEM_TYPE_MATRIX + 1)); + if (valid_message.element_type == TOUCH_ELEM_TYPE_BUTTON) { + uint32_t button_index = random() % BUTTON_CHANNEL_NUM; + valid_message.handle = button_handle[button_index]; + touch_button_message_t button_message = { + .event = TOUCH_BUTTON_EVT_ON_PRESS + }; + memcpy(valid_message.child_msg, &button_message, sizeof(button_message)); //Construct child message + xQueueSend(monitor.valid_msg_handle, &valid_message, portMAX_DELAY); + test_button_event_simulator(valid_message.handle, button_message.event); + } else if (valid_message.element_type == TOUCH_ELEM_TYPE_SLIDER) { + valid_message.handle = slider_handle; + touch_slider_message_t slider_message = { + .event = TOUCH_SLIDER_EVT_ON_PRESS, + .position = 0 //No check + }; + memcpy(valid_message.child_msg, &slider_message, sizeof(slider_message)); //Construct child message + xQueueSend(monitor.valid_msg_handle, &valid_message, portMAX_DELAY); + test_slider_event_simulator(valid_message.handle, slider_message.event, 1); + } else if (valid_message.element_type == TOUCH_ELEM_TYPE_MATRIX) { + uint32_t matrix_x_axis_index = random() % MATRIX_CHANNEL_NUM_X; + uint32_t matrix_y_axis_index = random() % MATRIX_CHANNEL_NUM_Y; + valid_message.handle = matrix_handle; + touch_matrix_message_t matrix_message = { + .event = TOUCH_MATRIX_EVT_ON_PRESS, + .position.x_axis = matrix_x_axis_index, + .position.y_axis = matrix_y_axis_index, + .position.index = matrix_x_axis_index * MATRIX_CHANNEL_NUM_Y + matrix_y_axis_index + }; + memcpy(valid_message.child_msg, &matrix_message, sizeof(matrix_message)); //Construct child message + xQueueSend(monitor.valid_msg_handle, &valid_message, portMAX_DELAY); + test_matrix_event_simulator(valid_message.handle, matrix_message.event, matrix_message.position.index); + } + os_ret = xSemaphoreTake(monitor.response_sig_handle, pdMS_TO_TICKS(500)); + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "response queue timeout (500ms)"); + + if (valid_message.element_type == TOUCH_ELEM_TYPE_BUTTON) { + touch_button_message_t button_message; + button_message.event = TOUCH_BUTTON_EVT_ON_RELEASE; + memcpy(valid_message.child_msg, &button_message, sizeof(button_message)); + xQueueSend(monitor.valid_msg_handle, &valid_message, portMAX_DELAY); + test_button_event_simulator(valid_message.handle, button_message.event); + } else if (valid_message.element_type == TOUCH_ELEM_TYPE_SLIDER) { + touch_slider_message_t slider_message; + slider_message.event = TOUCH_SLIDER_EVT_ON_RELEASE; + memcpy(valid_message.child_msg, &slider_message, sizeof(slider_message)); + xQueueSend(monitor.valid_msg_handle, &valid_message, portMAX_DELAY); + test_slider_event_simulator(valid_message.handle, slider_message.event, 1); + } else if (valid_message.element_type == TOUCH_ELEM_TYPE_MATRIX) { + touch_matrix_message_t matrix_message; + matrix_message.event = TOUCH_MATRIX_EVT_ON_RELEASE; + memcpy(valid_message.child_msg, &matrix_message, sizeof(matrix_message)); + xQueueSend(monitor.valid_msg_handle, &valid_message, portMAX_DELAY); + const touch_matrix_message_t *matrix_message_ptr = touch_matrix_get_message(&valid_message); + test_matrix_event_simulator(valid_message.handle, matrix_message.event, matrix_message_ptr->position.index); + } + os_ret = xSemaphoreTake(monitor.response_sig_handle, pdMS_TO_TICKS(500)); + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "response queue timeout (500ms)"); + } + + TEST_ESP_OK(touch_element_stop()); + for (int i = 0; i < BUTTON_CHANNEL_NUM; i++) { + TEST_ESP_OK(touch_button_delete(button_handle[i])); + } + TEST_ESP_OK(touch_slider_delete(slider_handle)); + TEST_ESP_OK(touch_matrix_delete(matrix_handle)); + touch_button_uninstall(); + touch_slider_uninstall(); + touch_matrix_uninstall(); + + while (eTaskGetState(task_handle) == eRunning) { + vTaskDelay(pdTICKS_TO_MS(1)); + } + vTaskDelete(task_handle); + vQueueDelete(monitor.valid_msg_handle); + vSemaphoreDelete(monitor.response_sig_handle); + printf("Integration test(button + slider + matrix) finish\n"); +} + +static void test_integration_monitor_task(void *arg) +{ + test_monitor_t *monitor = (test_monitor_t *)arg; + BaseType_t os_ret; + touch_elem_message_t current_message, valid_message; + while (1) { + touch_element_message_receive(¤t_message, portMAX_DELAY); + os_ret = xQueueReceive(monitor->valid_msg_handle, &valid_message, pdMS_TO_TICKS(500)); //Get the valid message for the verification, 500ms timeout + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "trigger queue timeout (500ms)"); + if (current_message.element_type == TOUCH_ELEM_TYPE_BUTTON) { + test_button_event_check(&valid_message, ¤t_message); + } else if (current_message.element_type == TOUCH_ELEM_TYPE_SLIDER) { + test_slider_event_check(&valid_message, ¤t_message); + } else if (current_message.element_type == TOUCH_ELEM_TYPE_MATRIX) { + test_matrix_event_check(&valid_message, ¤t_message); + } + xSemaphoreGive(monitor->response_sig_handle); + } +} diff --git a/components/touch_element/test/test_touch_matrix.c b/components/touch_element/test/test_touch_matrix.c new file mode 100644 index 0000000000..1f2ca7557f --- /dev/null +++ b/components/touch_element/test/test_touch_matrix.c @@ -0,0 +1,442 @@ +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "unity.h" + +#include "touch_element/touch_element_private.h" +#include "touch_element/touch_matrix.h" + +static const touch_pad_t x_axis_channel[3] = { + TOUCH_PAD_NUM5, + TOUCH_PAD_NUM7, + TOUCH_PAD_NUM9, +}; +static const touch_pad_t y_axis_channel[3] = { + TOUCH_PAD_NUM11, + TOUCH_PAD_NUM12, + TOUCH_PAD_NUM14, +}; +static const float x_axis_channel_sens[3] = { + 0.1F, + 0.1F, + 0.1F, +}; +static const float y_axis_channel_sens[3] = { + 0.1F, + 0.1F, + 0.1F, +}; +const uint8_t MATRIX_CHANNEL_NUM_X = sizeof(x_axis_channel) / sizeof(touch_pad_t); +const uint8_t MATRIX_CHANNEL_NUM_Y = sizeof(y_axis_channel) / sizeof(touch_pad_t); + +typedef struct { + QueueHandle_t valid_msg_handle; + SemaphoreHandle_t response_sig_handle; +} test_monitor_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +void test_matrix_event_simulator(touch_matrix_handle_t matrix_handle, touch_matrix_event_t matrix_event, uint32_t pos_index); +static void test_matrix_channel_simulator(touch_pad_t channel, touch_matrix_event_t matrix_event); +void test_matrix_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message); +static void test_matrix_callback_check(touch_matrix_handle_t current_handle, touch_matrix_message_t *current_message, touch_elem_message_t *valid_message); +void test_matrix_event_trigger_and_check(touch_matrix_handle_t handle, touch_matrix_event_t matrix_event, uint32_t pos_index); +void test_matrix_callback_trigger_and_check(touch_matrix_handle_t handle, touch_matrix_event_t matrix_event, uint32_t pos_index, bool should_trigger, test_monitor_t *monitor); +/* ------------------------------------------------ Dispatch method test -------------------------------------------- */ +static void test_matrix_disp_event(void); +static void test_matrix_disp_callback(void); +static void test_matrix_handler(touch_matrix_handle_t handle, touch_matrix_message_t *message, void *arg); +/* ------------------------------------------------ Run-time test --------------------------------------------------- */ +static void test_matrix_event_change_lp(void); +static void test_matrix_callback_change_lp(void); +static void test_matrix_change_lp_handler(touch_matrix_handle_t out_handle, touch_matrix_message_t *out_message, void *arg); +/* ----------------------------------------------- Random channel trigger test -------------------------------------- */ +static void test_matrix_random_channel_trigger(void); +/* ------------------------------------------------------------------------------------------------------------------ */ + +TEST_CASE("Touch matrix dispatch methods test", "[matrix][touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_matrix_disp_event(); + test_matrix_disp_callback(); + touch_element_uninstall(); +} + +TEST_CASE("Touch matrix run-time test", "[matrix][touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_matrix_event_change_lp(); + test_matrix_callback_change_lp(); + touch_element_uninstall(); +} + +TEST_CASE("Touch matrix random channel trigger test", "[matrix][touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_matrix_random_channel_trigger(); + touch_element_uninstall(); +} + +void test_matrix_event_simulator(touch_matrix_handle_t matrix_handle, touch_matrix_event_t matrix_event, uint32_t pos_index) +{ + te_matrix_handle_t te_matrix = (te_matrix_handle_t) matrix_handle; + touch_pad_t x_channel = te_matrix->device[pos_index / te_matrix->y_channel_num]->channel; + touch_pad_t y_channel = te_matrix->device[te_matrix->x_channel_num + (pos_index % te_matrix->y_channel_num)]->channel; + if (matrix_event == TOUCH_MATRIX_EVT_ON_PRESS) { + touch_pad_set_cnt_mode(x_channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); + touch_pad_set_cnt_mode(y_channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); + } else if (matrix_event == TOUCH_MATRIX_EVT_ON_RELEASE) { + touch_pad_set_cnt_mode(x_channel, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + touch_pad_set_cnt_mode(y_channel, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + } else { + touch_pad_set_cnt_mode(x_channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); // LongPress + touch_pad_set_cnt_mode(y_channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); + } +} + +static void test_matrix_channel_simulator(touch_pad_t channel, touch_matrix_event_t matrix_event) +{ + if (matrix_event == TOUCH_MATRIX_EVT_ON_PRESS) { + touch_pad_set_cnt_mode(channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); + } else if (matrix_event == TOUCH_MATRIX_EVT_ON_RELEASE) { + touch_pad_set_cnt_mode(channel, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + } +} + +void test_matrix_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message) +{ + TEST_ASSERT_MESSAGE(current_message->handle == valid_message->handle, "check handle failed"); + TEST_ASSERT_MESSAGE(current_message->element_type == valid_message->element_type, "check element type failed"); + const touch_matrix_message_t *valid_matrix_message = touch_matrix_get_message(valid_message); + const touch_matrix_message_t *current_matrix_message = touch_matrix_get_message(current_message); + TEST_ASSERT_MESSAGE(current_matrix_message->event == valid_matrix_message->event, "check event failed"); + TEST_ASSERT_MESSAGE(current_matrix_message->position.index == valid_matrix_message->position.index, "check index failed"); + TEST_ASSERT_MESSAGE(current_matrix_message->position.x_axis == valid_matrix_message->position.x_axis, "check x_axis failed"); + TEST_ASSERT_MESSAGE(current_matrix_message->position.y_axis == valid_matrix_message->position.y_axis, "check y_axis failed"); +} + +static inline void test_matrix_callback_check(touch_matrix_handle_t current_handle, touch_matrix_message_t *current_message, touch_elem_message_t *valid_message) +{ + const touch_matrix_message_t *valid_matrix_message = touch_matrix_get_message(valid_message); + TEST_ASSERT_MESSAGE(valid_message->handle == current_handle, "check handle failed"); + TEST_ASSERT_MESSAGE(valid_message->element_type == TOUCH_ELEM_TYPE_MATRIX, "check element type failed"); + TEST_ASSERT_MESSAGE(valid_matrix_message->event == current_message->event, "check event failed"); + TEST_ASSERT_MESSAGE(valid_matrix_message->position.index == current_message->position.index, "check index failed"); + TEST_ASSERT_MESSAGE(valid_matrix_message->position.x_axis == current_message->position.x_axis, "check x_axis failed"); + TEST_ASSERT_MESSAGE(valid_matrix_message->position.y_axis == current_message->position.y_axis, "check y_axis failed"); +} + +void test_matrix_event_trigger_and_check(touch_matrix_handle_t handle, touch_matrix_event_t matrix_event, uint32_t pos_index) +{ + touch_elem_message_t valid_message = { + .handle = handle, + .element_type = TOUCH_ELEM_TYPE_MATRIX, + .arg = NULL + }; + touch_matrix_message_t matrix_message = { + .event = matrix_event, + .position.index = pos_index, + .position.x_axis = pos_index / MATRIX_CHANNEL_NUM_Y, + .position.y_axis = pos_index % MATRIX_CHANNEL_NUM_Y + }; + memcpy(valid_message.child_msg, &matrix_message, sizeof(touch_matrix_message_t)); //Construct valid_message + + test_matrix_event_simulator(handle, matrix_event, pos_index); //Trigger signal + + touch_elem_message_t current_message; + te_matrix_handle_t te_matrix = handle; + esp_err_t ret = touch_element_message_receive(¤t_message, pdMS_TO_TICKS(2 * te_matrix->trigger_thr * 10)); //Get current message for verification + TEST_ASSERT_MESSAGE(ret == ESP_OK, "matrix event receive timeout"); + + test_matrix_event_check(&valid_message, ¤t_message); //Verification +} + +void test_matrix_callback_trigger_and_check(touch_matrix_handle_t handle, touch_matrix_event_t matrix_event, uint32_t pos_index, bool should_trigger, test_monitor_t *monitor) +{ + if (should_trigger) { + touch_elem_message_t valid_message = { + .handle = handle, + .element_type = TOUCH_ELEM_TYPE_MATRIX, + .arg = NULL + }; + touch_matrix_message_t matrix_message = { + .event = matrix_event, + .position.index = pos_index, + .position.x_axis = pos_index / MATRIX_CHANNEL_NUM_Y, + .position.y_axis = pos_index % MATRIX_CHANNEL_NUM_Y + }; + memcpy(valid_message.child_msg, &matrix_message, sizeof(touch_matrix_message_t)); //Construct valid_message + xQueueSend(monitor->valid_msg_handle, &valid_message, portMAX_DELAY); + } + + test_matrix_event_simulator(handle, matrix_event, pos_index); //Trigger signal + + te_matrix_handle_t te_matrix = handle; + if (should_trigger) { + BaseType_t os_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(2 * te_matrix->trigger_thr * 10)); + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "Matrix queue timeout"); + } else { + BaseType_t os_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(500)); + TEST_ASSERT_MESSAGE(os_ret == pdFALSE, "Matrix invalid trigger"); + } +} + +static void test_matrix_disp_event(void) +{ + touch_matrix_handle_t matrix_handle = NULL; + touch_matrix_global_config_t global_config = TOUCH_MATRIX_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_matrix_install(&global_config)); + touch_matrix_config_t matrix_config = { + .x_channel_array = x_axis_channel, + .y_channel_array = y_axis_channel, + .x_sensitivity_array = x_axis_channel_sens, + .y_sensitivity_array = y_axis_channel_sens, + .x_channel_num = MATRIX_CHANNEL_NUM_X, + .y_channel_num = MATRIX_CHANNEL_NUM_Y + }; + TEST_ESP_OK(touch_matrix_create(&matrix_config, &matrix_handle)); + TEST_ESP_OK(touch_matrix_subscribe_event(matrix_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_LONGPRESS | TOUCH_ELEM_EVENT_ON_RELEASE, NULL)); + TEST_ESP_OK(touch_matrix_set_longpress(matrix_handle, 300)); + TEST_ESP_OK(touch_matrix_set_dispatch_method(matrix_handle, TOUCH_ELEM_DISP_EVENT)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + printf("Touch matrix event test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch matrix event test... (%d/10)\n", i + 1); + uint32_t button_num = random() % ( MATRIX_CHANNEL_NUM_X * MATRIX_CHANNEL_NUM_Y ); + test_matrix_event_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_PRESS, button_num); + test_matrix_event_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_LONGPRESS, button_num); + test_matrix_event_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_RELEASE, button_num); + } + printf("Touch matrix event test finish\n"); + + TEST_ESP_OK(touch_element_stop()); + TEST_ESP_OK(touch_matrix_delete(matrix_handle)); + touch_matrix_uninstall(); +} + +static void test_matrix_disp_callback(void) +{ + test_monitor_t monitor; + touch_matrix_handle_t matrix_handle = NULL; + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + TEST_ASSERT(monitor.valid_msg_handle != NULL || monitor.response_sig_handle != NULL); + + touch_matrix_global_config_t global_config = TOUCH_MATRIX_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_matrix_install(&global_config)); + touch_matrix_config_t matrix_config = { + .x_channel_array = x_axis_channel, + .y_channel_array = y_axis_channel, + .x_sensitivity_array = x_axis_channel_sens, + .y_sensitivity_array = y_axis_channel_sens, + .x_channel_num = MATRIX_CHANNEL_NUM_X, + .y_channel_num = MATRIX_CHANNEL_NUM_Y + }; + TEST_ESP_OK(touch_matrix_create(&matrix_config, &matrix_handle)); + TEST_ESP_OK(touch_matrix_subscribe_event(matrix_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_LONGPRESS | TOUCH_ELEM_EVENT_ON_RELEASE, (void *)&monitor)); + TEST_ESP_OK(touch_matrix_set_longpress(matrix_handle, 300)); + TEST_ESP_OK(touch_matrix_set_dispatch_method(matrix_handle, TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_matrix_set_callback(matrix_handle, test_matrix_handler)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + printf("Touch matrix callback test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch matrix callback test... (%d/10)\n", i + 1); + uint32_t button_num = random() % (MATRIX_CHANNEL_NUM_X * MATRIX_CHANNEL_NUM_Y); + test_matrix_callback_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_PRESS, button_num, true, &monitor); + test_matrix_callback_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_LONGPRESS, button_num, true, &monitor); + test_matrix_callback_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_RELEASE, button_num, true, &monitor); + } + printf("Touch matrix callback test finish\n"); + + TEST_ESP_OK(touch_element_stop()); + TEST_ESP_OK(touch_matrix_delete(matrix_handle)); + touch_matrix_uninstall(); + vQueueDelete(monitor.valid_msg_handle); + vSemaphoreDelete(monitor.response_sig_handle); +} + +static void test_matrix_handler(touch_matrix_handle_t handle, touch_matrix_message_t *message, void *arg) +{ + test_monitor_t *monitor = (test_monitor_t *)arg; + touch_elem_message_t valid_message; + BaseType_t os_ret = xQueueReceive(monitor->valid_msg_handle, &valid_message, pdMS_TO_TICKS(200)); + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "test_matrix_handler: queue timeout"); + test_matrix_callback_check(handle, message, &valid_message); + xSemaphoreGive(monitor->response_sig_handle); +} + +static void test_matrix_random_channel_trigger(void) +{ + test_monitor_t monitor; + touch_matrix_handle_t matrix_handle = NULL; + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + TEST_ASSERT(monitor.valid_msg_handle != NULL || monitor.response_sig_handle != NULL); + + touch_matrix_global_config_t global_config = TOUCH_MATRIX_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_matrix_install(&global_config)); + touch_matrix_config_t matrix_config = { + .x_channel_array = x_axis_channel, + .y_channel_array = y_axis_channel, + .x_sensitivity_array = x_axis_channel_sens, + .y_sensitivity_array = y_axis_channel_sens, + .x_channel_num = MATRIX_CHANNEL_NUM_X, + .y_channel_num = MATRIX_CHANNEL_NUM_Y + }; + TEST_ESP_OK(touch_matrix_create(&matrix_config, &matrix_handle)); + TEST_ESP_OK(touch_matrix_subscribe_event(matrix_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, (void *) &monitor)); + TEST_ESP_OK(touch_matrix_set_dispatch_method(matrix_handle, TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_matrix_set_callback(matrix_handle, test_matrix_handler)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + printf("Touch matrix random channel trigger test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch matrix random channel trigger test... (%d/10)\n", i + 1); + uint32_t channel_index_1 = random() % (MATRIX_CHANNEL_NUM_X + MATRIX_CHANNEL_NUM_Y); + uint32_t channel_index_2 = random() % (MATRIX_CHANNEL_NUM_X + MATRIX_CHANNEL_NUM_Y); + touch_pad_t channel_1 = (channel_index_1 < MATRIX_CHANNEL_NUM_X) ? x_axis_channel[channel_index_1] : y_axis_channel[channel_index_1 - MATRIX_CHANNEL_NUM_X]; + touch_pad_t channel_2 = (channel_index_2 < MATRIX_CHANNEL_NUM_X) ? x_axis_channel[channel_index_2] : y_axis_channel[channel_index_2 - MATRIX_CHANNEL_NUM_X]; + if ((channel_index_1 <= 2 && channel_index_2 <= 2) || (channel_index_1 > 2 && channel_index_2 > 2)) { //all x channels triggered or all y channels triggered + //Should not be triggered + BaseType_t os_ret; + test_matrix_channel_simulator(channel_1, TOUCH_MATRIX_EVT_ON_PRESS); + test_matrix_channel_simulator(channel_2, TOUCH_MATRIX_EVT_ON_PRESS); + os_ret = xSemaphoreTake(monitor.response_sig_handle, pdMS_TO_TICKS(500)); + TEST_ASSERT_MESSAGE(os_ret == pdFAIL, "Matrix Press event invalid trigger"); + + test_matrix_channel_simulator(channel_1, TOUCH_MATRIX_EVT_ON_RELEASE); + test_matrix_channel_simulator(channel_2, TOUCH_MATRIX_EVT_ON_RELEASE); + os_ret = xSemaphoreTake(monitor.response_sig_handle, pdMS_TO_TICKS(500)); + TEST_ASSERT_MESSAGE(os_ret == pdFAIL, "Matrix Release event invalid trigger"); + } else { + //Should be triggered + uint8_t button_num; + if (channel_index_1 <= 2) { + button_num = channel_index_1 * matrix_config.y_channel_num + (channel_index_2 - MATRIX_CHANNEL_NUM_X); + } else { + button_num = channel_index_2 * matrix_config.x_channel_num + (channel_index_1 - MATRIX_CHANNEL_NUM_Y); + } + test_matrix_callback_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_PRESS, button_num, true, &monitor); + test_matrix_callback_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_RELEASE, button_num, true, &monitor); + } + } + printf("Touch matrix random channel trigger test finish\n"); + + TEST_ESP_OK(touch_element_stop()); + TEST_ESP_OK(touch_matrix_delete(matrix_handle)); + touch_matrix_uninstall(); + vQueueDelete(monitor.valid_msg_handle); + vSemaphoreDelete(monitor.response_sig_handle); +} + +static void test_matrix_event_change_lp(void) +{ + touch_matrix_handle_t matrix_handle = NULL; + touch_matrix_global_config_t global_config = TOUCH_MATRIX_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_matrix_install(&global_config)); + touch_matrix_config_t matrix_config = { + .x_channel_array = x_axis_channel, + .y_channel_array = y_axis_channel, + .x_sensitivity_array = x_axis_channel_sens, + .y_sensitivity_array = y_axis_channel_sens, + .x_channel_num = MATRIX_CHANNEL_NUM_X, + .y_channel_num = MATRIX_CHANNEL_NUM_Y + }; + TEST_ESP_OK(touch_matrix_create(&matrix_config, &matrix_handle)); + TEST_ESP_OK(touch_matrix_subscribe_event(matrix_handle, TOUCH_ELEM_EVENT_ON_LONGPRESS, NULL)); + TEST_ESP_OK(touch_matrix_set_dispatch_method(matrix_handle, TOUCH_ELEM_DISP_EVENT)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + //10 times random press/longpress/release test + printf("Touch matrix event change longtime test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch matrix event change longtime test... (%d/10)\n", i + 1); + uint32_t button_num = random() % ( MATRIX_CHANNEL_NUM_X * MATRIX_CHANNEL_NUM_Y ); + TEST_ESP_OK(touch_matrix_set_longpress(matrix_handle, 200 + (i + 1) * 50)); + test_matrix_event_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_LONGPRESS, button_num); + test_matrix_event_simulator(matrix_handle, TOUCH_MATRIX_EVT_ON_RELEASE, button_num); //Reset hardware + vTaskDelay(pdMS_TO_TICKS(100)); //Fixme: Waiting for driver core handle release event + } + printf("Touch matrix event change longtime test finish\n"); + TEST_ESP_OK(touch_element_stop()); + TEST_ESP_OK(touch_matrix_delete(matrix_handle)); + touch_matrix_uninstall(); +} + +static void test_matrix_callback_change_lp(void) +{ + test_monitor_t monitor; + touch_matrix_handle_t matrix_handle = NULL; + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + TEST_ASSERT(monitor.valid_msg_handle != NULL || monitor.response_sig_handle != NULL); + + touch_matrix_global_config_t global_config = TOUCH_MATRIX_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_matrix_install(&global_config)); + touch_matrix_config_t matrix_config = { + .x_channel_array = x_axis_channel, + .y_channel_array = y_axis_channel, + .x_sensitivity_array = x_axis_channel_sens, + .y_sensitivity_array = y_axis_channel_sens, + .x_channel_num = MATRIX_CHANNEL_NUM_X, + .y_channel_num = MATRIX_CHANNEL_NUM_Y + }; + TEST_ESP_OK(touch_matrix_create(&matrix_config, &matrix_handle)); + TEST_ESP_OK(touch_matrix_subscribe_event(matrix_handle, TOUCH_ELEM_EVENT_ON_LONGPRESS, (void *)&monitor)); + TEST_ESP_OK(touch_matrix_set_longpress(matrix_handle, 300)); + TEST_ESP_OK(touch_matrix_set_dispatch_method(matrix_handle, TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_matrix_set_callback(matrix_handle, test_matrix_change_lp_handler)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + printf("Touch matrix callback change longtime test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch matrix callback change longtime test... (%d/10)\n", i + 1); + uint32_t button_num = random() % (MATRIX_CHANNEL_NUM_X * MATRIX_CHANNEL_NUM_Y); + test_matrix_callback_trigger_and_check(matrix_handle, TOUCH_MATRIX_EVT_ON_LONGPRESS, button_num, true, &monitor); + test_matrix_event_simulator(matrix_handle, TOUCH_MATRIX_EVT_ON_RELEASE, button_num); //Reset hardware + vTaskDelay(pdMS_TO_TICKS(100)); //Fixme: Waiting for driver core handle release event + + } + printf("Touch matrix callback change longtime test finish\n"); + + TEST_ESP_OK(touch_element_stop()); + TEST_ESP_OK(touch_matrix_delete(matrix_handle)); + touch_matrix_uninstall(); + vQueueDelete(monitor.valid_msg_handle); + vSemaphoreDelete(monitor.response_sig_handle); +} + +static void test_matrix_change_lp_handler(touch_matrix_handle_t out_handle, touch_matrix_message_t *out_message, void *arg) +{ + test_monitor_t *monitor = (test_monitor_t *)arg; + touch_elem_message_t valid_message; + BaseType_t os_ret = xQueueReceive(monitor->valid_msg_handle, &valid_message, pdMS_TO_TICKS(200)); //500ms timeout + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "test_matrix_handler: queue timeout"); + test_matrix_callback_check(out_handle, out_message, &valid_message); + xSemaphoreGive(monitor->response_sig_handle); + TEST_ESP_OK(touch_matrix_set_longpress(valid_message.handle, 300)); // Always 300ms +} diff --git a/components/touch_element/test/test_touch_slider.c b/components/touch_element/test/test_touch_slider.c new file mode 100644 index 0000000000..47d70ed70d --- /dev/null +++ b/components/touch_element/test/test_touch_slider.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "unity.h" + +#include "touch_element/touch_element_private.h" +#include "touch_element/touch_slider.h" + +static const touch_pad_t slider_channel_array[5] = { + TOUCH_PAD_NUM1, + TOUCH_PAD_NUM2, + TOUCH_PAD_NUM3, + TOUCH_PAD_NUM4, + TOUCH_PAD_NUM5 +}; +static const float slider_sens_array[5] = { + 0.1F, + 0.1F, + 0.1F, + 0.1F, + 0.1F +}; +const uint8_t SLIDER_CHANNEL_NUM = sizeof(slider_channel_array) / sizeof(touch_pad_t); + +typedef struct { + QueueHandle_t valid_msg_handle; + SemaphoreHandle_t response_sig_handle; +} test_monitor_t; + +/* ------------------------------------------------------------------------------------------------------------------ */ +void test_slider_event_simulator(touch_slider_handle_t slider_handle, touch_slider_event_t slider_event, uint32_t random); +void test_slider_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message); +static void test_slider_callback_check(touch_slider_handle_t current_handle, touch_slider_message_t *current_message, touch_elem_message_t *valid_message); +void test_slider_event_trigger_and_check(touch_slider_handle_t handle, touch_slider_event_t slider_event, uint32_t random_channel); +void test_slider_callback_trigger_and_check(touch_slider_handle_t handle, touch_slider_event_t slider_event, bool should_trigger, test_monitor_t *monitor, uint32_t random_channel); +/* ------------------------------------------------ Dispatch method test -------------------------------------------- */ +static void test_slider_disp_event(void); +static void test_slider_disp_callback(void); +static void test_slider_handler(touch_slider_handle_t handle, touch_slider_message_t *message, void *arg); +/* ------------------------------------------------------------------------------------------------------------------ */ + +TEST_CASE("Touch slider dispatch methods test", "[slider][touch_element]") +{ + touch_elem_global_config_t global_config = TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_element_install(&global_config)); + test_slider_disp_event(); + test_slider_disp_callback(); + touch_element_uninstall(); +} + +void test_slider_event_simulator(touch_slider_handle_t slider_handle, touch_slider_event_t slider_event, uint32_t random) +{ + te_slider_handle_t te_slider = (te_slider_handle_t) slider_handle; + touch_pad_t channel = te_slider->device[random % te_slider->channel_sum]->channel; + if (slider_event == TOUCH_SLIDER_EVT_ON_PRESS) { + touch_pad_set_cnt_mode(channel, TOUCH_PAD_SLOPE_3, TOUCH_PAD_TIE_OPT_DEFAULT); + } else if (slider_event == TOUCH_SLIDER_EVT_ON_RELEASE) { + touch_pad_set_cnt_mode(channel, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + } +} + +void test_slider_event_check(touch_elem_message_t *valid_message, touch_elem_message_t *current_message) +{ + TEST_ASSERT_MESSAGE(current_message->handle == valid_message->handle, "check handle failed"); + TEST_ASSERT_MESSAGE(current_message->element_type == valid_message->element_type, "check element type failed"); + const touch_slider_message_t *valid_slider_message = touch_slider_get_message(valid_message); + const touch_slider_message_t *current_slider_message = touch_slider_get_message(current_message); + TEST_ASSERT_MESSAGE(current_slider_message->event == valid_slider_message->event, "check event failed"); +} + +static void test_slider_callback_check(touch_slider_handle_t current_handle, touch_slider_message_t *current_message, touch_elem_message_t *valid_message) +{ + const touch_slider_message_t *valid_slider_message = touch_slider_get_message(valid_message); + TEST_ASSERT_MESSAGE(valid_message->handle == current_handle, "check handle failed"); + TEST_ASSERT_MESSAGE(valid_message->element_type == TOUCH_ELEM_TYPE_SLIDER, "check element type failed"); + TEST_ASSERT_MESSAGE(valid_slider_message->event == current_message->event, "check event failed"); +} + +void test_slider_event_trigger_and_check(touch_slider_handle_t handle, touch_slider_event_t slider_event, uint32_t random_channel) +{ + touch_elem_message_t valid_message, current_message; + touch_slider_message_t slider_message; + valid_message.handle = handle; + valid_message.element_type = TOUCH_ELEM_TYPE_SLIDER; + slider_message.event = slider_event; + memcpy(valid_message.child_msg, &slider_message, sizeof(touch_slider_message_t)); + test_slider_event_simulator(handle, slider_event, random_channel); + esp_err_t ret = touch_element_message_receive(¤t_message, 300); + TEST_ASSERT_MESSAGE(ret == ESP_OK, "slider event receive timeout"); + test_slider_event_check(&valid_message, ¤t_message); +} + +void test_slider_callback_trigger_and_check(touch_slider_handle_t handle, touch_slider_event_t slider_event, bool should_trigger, test_monitor_t *monitor, uint32_t random_channel) +{ + if (should_trigger) { + touch_elem_message_t valid_message = { + .handle = handle, + .element_type = TOUCH_ELEM_TYPE_SLIDER, + .arg = NULL + }; + touch_slider_message_t slider_message = { + .event = slider_event, + .position = 0 //No use + }; + memcpy(valid_message.child_msg, &slider_message, sizeof(touch_slider_message_t)); //Construct valid_message + xQueueSend(monitor->valid_msg_handle, &valid_message, portMAX_DELAY); + } + + test_slider_event_simulator(handle, slider_event, random_channel); //Trigger signal + + BaseType_t os_ret = xSemaphoreTake(monitor->response_sig_handle, pdMS_TO_TICKS(300)); + if (should_trigger) { + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "Button queue timeout"); + } else { + TEST_ASSERT_MESSAGE(os_ret == pdFALSE, "Button invalid trigger"); + } +} + + +static void test_slider_disp_event(void) +{ + touch_slider_handle_t slider_handle; + touch_slider_global_config_t global_config = TOUCH_SLIDER_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_slider_install(&global_config)); + /*< Create Touch Slider */ + touch_slider_config_t slider_config = { + .channel_array = slider_channel_array, + .sensitivity_array = slider_sens_array, + .channel_num = SLIDER_CHANNEL_NUM, + .position_range = 101 + }; + TEST_ESP_OK(touch_slider_create(&slider_config, &slider_handle)); + TEST_ESP_OK(touch_slider_subscribe_event(slider_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, (void *) slider_handle)); + TEST_ESP_OK(touch_slider_set_dispatch_method(slider_handle, TOUCH_ELEM_DISP_EVENT)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + //10 times random (x channels) press/release test + printf("Touch slider event test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch slider event test... (%d/10)\n", i + 1); + uint32_t random_channel = random() % SLIDER_CHANNEL_NUM; + test_slider_event_trigger_and_check(slider_handle, TOUCH_SLIDER_EVT_ON_PRESS, random_channel); + test_slider_event_trigger_and_check(slider_handle, TOUCH_SLIDER_EVT_ON_RELEASE, random_channel); + } + printf("Touch slider event test finish\n"); + TEST_ESP_OK(touch_element_stop()); + TEST_ESP_OK(touch_slider_delete(slider_handle)); + touch_slider_uninstall(); +} + + +static void test_slider_disp_callback(void) +{ + test_monitor_t monitor; + monitor.valid_msg_handle = xQueueCreate(10, sizeof(touch_elem_message_t)); + monitor.response_sig_handle = xSemaphoreCreateBinary(); + TEST_ASSERT(monitor.valid_msg_handle != NULL || monitor.response_sig_handle != NULL); + + touch_slider_handle_t slider_handle; + touch_slider_global_config_t global_config = TOUCH_SLIDER_GLOBAL_DEFAULT_CONFIG(); + TEST_ESP_OK(touch_slider_install(&global_config)); + touch_slider_config_t slider_config = { + .channel_array = slider_channel_array, + .sensitivity_array = slider_sens_array, + .channel_num = SLIDER_CHANNEL_NUM, + .position_range = 101 + }; + TEST_ESP_OK(touch_slider_create(&slider_config, &slider_handle)); + TEST_ESP_OK(touch_slider_subscribe_event(slider_handle, TOUCH_ELEM_EVENT_ON_PRESS | TOUCH_ELEM_EVENT_ON_RELEASE, (void *) &monitor)); + TEST_ESP_OK(touch_slider_set_dispatch_method(slider_handle, TOUCH_ELEM_DISP_CALLBACK)); + TEST_ESP_OK(touch_slider_set_callback(slider_handle, test_slider_handler)); + TEST_ESP_OK(touch_element_start()); + + vTaskDelay(pdMS_TO_TICKS(500)); //Mention in README, code-block-1 + + srandom((unsigned int)time(NULL)); + printf("Touch slider callback test start\n"); + for (int i = 0; i < 10; i++) { + printf("Touch slider callback test... (%d/10)\n", i + 1); + uint32_t random_channel = random() % SLIDER_CHANNEL_NUM; + test_slider_callback_trigger_and_check(slider_handle, TOUCH_SLIDER_EVT_ON_PRESS, true, &monitor, random_channel); + test_slider_callback_trigger_and_check(slider_handle, TOUCH_SLIDER_EVT_ON_RELEASE, true, &monitor, random_channel); + } + printf("Touch slider callback test finish\n"); + + TEST_ESP_OK(touch_element_stop()); + TEST_ESP_OK(touch_slider_delete(slider_handle)); + touch_slider_uninstall(); + vQueueDelete(monitor.valid_msg_handle); + vSemaphoreDelete(monitor.response_sig_handle); +} + +static void test_slider_handler(touch_slider_handle_t handle, touch_slider_message_t *message, void *arg) +{ + test_monitor_t *monitor = (test_monitor_t *)arg; + touch_elem_message_t valid_message; + BaseType_t os_ret = xQueueReceive(monitor->valid_msg_handle, &valid_message, pdMS_TO_TICKS(200)); //Get the valid message for verification, 200ms timeout + TEST_ASSERT_MESSAGE(os_ret == pdPASS, "test_slider_handler: queue timeout"); + + test_slider_callback_check(handle, message, &valid_message); //Verification + + xSemaphoreGive(monitor->response_sig_handle); +}