From 8549d818f839efe1944d716eb9dfee1bf4af8ea3 Mon Sep 17 00:00:00 2001 From: Peter Marcisovsky Date: Wed, 11 Sep 2024 16:06:53 +0200 Subject: [PATCH] feat(cmock): Enable linux target build to run Cmock tests on class drivers - HID, CDC-ACM, UVC class drivers can be build on linux target - Added linux build test and simple Cmock test run in CI --- .build-test-rules.yml | 9 ++ .../cdc/usb_host_cdc_acm/host_test/README.md | 1 + .../host_test/main/test_unit_public_api.cpp | 77 ++++++++++ host/class/hid/usb_host_hid/hid_host.c | 44 +++--- .../hid/usb_host_hid/host_test/CMakeLists.txt | 11 ++ .../hid/usb_host_hid/host_test/README.md | 30 ++++ .../host_test/main/CMakeLists.txt | 8 + .../host_test/main/idf_component.yml | 5 + .../host_test/main/test_unit_public_api.cpp | 145 ++++++++++++++++++ .../usb_host_hid/host_test/sdkconfig.defaults | 8 + host/class/hid/usb_host_hid/idf_component.yml | 4 - .../uvc/usb_host_uvc/host_test/CMakeLists.txt | 11 ++ .../uvc/usb_host_uvc/host_test/README.md | 30 ++++ .../host_test/main/CMakeLists.txt | 9 ++ .../host_test/main/idf_component.yml | 5 + .../host_test/main/test_unit_public_api.cpp | 28 ++++ .../usb_host_uvc/host_test/sdkconfig.defaults | 8 + host/class/uvc/usb_host_uvc/idf_component.yml | 4 - .../uvc/usb_host_uvc/include/libuvc_helper.h | 2 +- .../uvc/usb_host_uvc/src/libusb_adapter.c | 10 +- 20 files changed, 413 insertions(+), 36 deletions(-) create mode 100644 host/class/cdc/usb_host_cdc_acm/host_test/main/test_unit_public_api.cpp create mode 100644 host/class/hid/usb_host_hid/host_test/CMakeLists.txt create mode 100644 host/class/hid/usb_host_hid/host_test/README.md create mode 100644 host/class/hid/usb_host_hid/host_test/main/CMakeLists.txt create mode 100644 host/class/hid/usb_host_hid/host_test/main/idf_component.yml create mode 100644 host/class/hid/usb_host_hid/host_test/main/test_unit_public_api.cpp create mode 100644 host/class/hid/usb_host_hid/host_test/sdkconfig.defaults create mode 100644 host/class/uvc/usb_host_uvc/host_test/CMakeLists.txt create mode 100644 host/class/uvc/usb_host_uvc/host_test/README.md create mode 100644 host/class/uvc/usb_host_uvc/host_test/main/CMakeLists.txt create mode 100644 host/class/uvc/usb_host_uvc/host_test/main/idf_component.yml create mode 100644 host/class/uvc/usb_host_uvc/host_test/main/test_unit_public_api.cpp create mode 100644 host/class/uvc/usb_host_uvc/host_test/sdkconfig.defaults diff --git a/.build-test-rules.yml b/.build-test-rules.yml index 1bfa7b39..31fd6468 100644 --- a/.build-test-rules.yml +++ b/.build-test-rules.yml @@ -6,6 +6,15 @@ host/class: enable: - if: SOC_USB_OTG_SUPPORTED == 1 +# Host tests host/class/cdc/usb_host_cdc_acm/host_test: enable: - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4) + +host/class/hid/usb_host_hid/host_test: + enable: + - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4) + +host/class/uvc/usb_host_uvc/host_test: + enable: + - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4) \ No newline at end of file diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/README.md b/host/class/cdc/usb_host_cdc_acm/host_test/README.md index c11046e5..be5808f2 100644 --- a/host/class/cdc/usb_host_cdc_acm/host_test/README.md +++ b/host/class/cdc/usb_host_cdc_acm/host_test/README.md @@ -5,6 +5,7 @@ This directory contains test code for `USB Host CDC-ACM` driver. Namely: * Descriptor parsing +* Simple public API call with mocked USB component to test Linux build and Cmock run for this class driver Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework, use CMock, so you must install Ruby on your machine to run them. diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_unit_public_api.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/main/test_unit_public_api.cpp new file mode 100644 index 00000000..d4424848 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/main/test_unit_public_api.cpp @@ -0,0 +1,77 @@ + +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "usb/cdc_acm_host.h" + +extern "C" { +#include "Mockusb_host.h" +#include "Mockqueue.h" +#include "Mocktask.h" +#include "Mockidf_additions.h" +#include "Mockportmacro.h" +#include "Mockevent_groups.h" +} + +SCENARIO("CDC-ACM Host install") +{ + // CDC-ACM Host driver config set to nullptr + GIVEN("NO CDC-ACM Host driver config, driver not installed") { + TaskHandle_t task_handle; + int sem; + int event_group; + + // Call cdc_acm_host_install with cdc_acm_host_driver_config set to nullptr, fail to create EventGroup + SECTION("Fail to create EventGroup") { + // Create an EventGroup, return nullptr, so the EventGroup is not created successfully + xEventGroupCreate_ExpectAndReturn(nullptr); + // We should be calling xSemaphoreCreteMutex_ExpectAnyArgsAndRetrun instead of xQueueCreateMutex_ExpectAnyArgsAndReturn + // Because of missing Freertos Mocks + // Create a semaphore, return the semaphore handle (Queue Handle in this scenario), so the semaphore is created successfully + xQueueCreateMutex_ExpectAnyArgsAndReturn(reinterpret_cast(&sem)); + // Create a task, return pdTRUE, so the task is created successfully + xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE); + // Return task handle by pointer + xTaskCreatePinnedToCore_ReturnThruPtr_pxCreatedTask(&task_handle); + + // goto err: (xEventGroupCreate returned nullptr), delete the queue and the task + vQueueDelete_Expect(reinterpret_cast(&sem)); + vTaskDelete_Expect(task_handle); + + // Call the DUT function, expect ESP_ERR_NO_MEM + REQUIRE(ESP_ERR_NO_MEM == cdc_acm_host_install(nullptr)); + } + + // Call cdc_acm_host_install, expect to successfully install the CDC ACM host + SECTION("Successfully install CDC ACM Host") { + // Create an EventGroup, return event group handle, so the EventGroup is created successfully + xEventGroupCreate_ExpectAndReturn(reinterpret_cast(&event_group)); + // We should be calling xSemaphoreCreteMutex_ExpectAnyArgsAndRetrun instead of xQueueCreateMutex_ExpectAnyArgsAndReturn + // Because of missing Freertos Mocks + // Create a semaphore, return the semaphore handle (Queue Handle in this scenario), so the semaphore is created successfully + xQueueCreateMutex_ExpectAnyArgsAndReturn(reinterpret_cast(&sem)); + // Create a task, return pdTRUE, so the task is created successfully + vPortEnterCritical_Expect(); + vPortExitCritical_Expect(); + xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE); + // Return task handle by pointer + xTaskCreatePinnedToCore_ReturnThruPtr_pxCreatedTask(&task_handle); + + // Call mocked function from USB Host + // return ESP_OK, so the client si registered successfully + usb_host_client_register_ExpectAnyArgsAndReturn(ESP_OK); + + // Resume the task + vTaskResume_Expect(task_handle); + + // Call the DUT Function, expect ESP_OK + REQUIRE(ESP_OK == cdc_acm_host_install(nullptr)); + } + } +} diff --git a/host/class/hid/usb_host_hid/hid_host.c b/host/class/hid/usb_host_hid/hid_host.c index f1c62106..d4c1e3af 100644 --- a/host/class/hid/usb_host_hid/hid_host.c +++ b/host/class/hid/usb_host_hid/hid_host.c @@ -166,7 +166,7 @@ typedef struct hid_class_request { static void event_handler_task(void *arg) { ESP_LOGD(TAG, "USB HID handling start"); - while (hid_host_handle_events(portMAX_DELAY) == ESP_OK) { + while (hid_host_handle_events((uint32_t)portMAX_DELAY) == ESP_OK) { } ESP_LOGD(TAG, "USB HID handling stop"); vTaskDelete(NULL); @@ -293,40 +293,40 @@ static bool hid_interface_present(const usb_config_desc_t *config_desc) /** * @brief HID Interface user callback function. * - * @param[in] hid_iface Pointer to an Interface structure - * @param[in] event_id HID Interface event + * @param[in] iface Pointer to an Interface structure + * @param[in] event HID Interface event */ -static inline void hid_host_user_interface_callback(hid_iface_t *hid_iface, +static inline void hid_host_user_interface_callback(hid_iface_t *iface, const hid_host_interface_event_t event) { - assert(hid_iface); + assert(iface); - hid_host_dev_params_t *dev_params = &hid_iface->dev_params; + hid_host_dev_params_t *dev_params = &iface->dev_params; assert(dev_params); - if (hid_iface->user_cb) { - hid_iface->user_cb(hid_iface, event, hid_iface->user_cb_arg); + if (iface->user_cb) { + iface->user_cb(iface, event, iface->user_cb_arg); } } /** * @brief HID Device user callback function. * - * @param[in] event_id HID Device event - * @param[in] dev_params HID Device parameters + * @param[in] iface Pointer to an Interface structure + * @param[in] event HID Device event */ -static inline void hid_host_user_device_callback(hid_iface_t *hid_iface, +static inline void hid_host_user_device_callback(hid_iface_t *iface, const hid_host_driver_event_t event) { - assert(hid_iface); + assert(iface); - hid_host_dev_params_t *dev_params = &hid_iface->dev_params; + hid_host_dev_params_t *dev_params = &iface->dev_params; assert(dev_params); if (s_hid_driver && s_hid_driver->user_cb) { - s_hid_driver->user_cb(hid_iface, event, s_hid_driver->user_arg); + s_hid_driver->user_cb(iface, event, s_hid_driver->user_arg); } } @@ -393,14 +393,14 @@ static esp_err_t hid_host_add_interface(hid_device_t *hid_device, * * Use only inside critical section * - * @param[in] hid_iface HID interface handle + * @param[in] iface HID interface handle * @return esp_err_t */ -static esp_err_t _hid_host_remove_interface(hid_iface_t *hid_iface) +static esp_err_t _hid_host_remove_interface(hid_iface_t *iface) { - hid_iface->state = HID_INTERFACE_STATE_NOT_INITIALIZED; - STAILQ_REMOVE(&s_hid_driver->hid_ifaces_tailq, hid_iface, hid_interface, tailq_entry); - free(hid_iface); + iface->state = HID_INTERFACE_STATE_NOT_INITIALIZED; + STAILQ_REMOVE(&s_hid_driver->hid_ifaces_tailq, iface, hid_interface, tailq_entry); + free(iface); return ESP_OK; } @@ -765,7 +765,7 @@ static esp_err_t hid_control_transfer(hid_device_t *hid_device, /** * @brief USB class standard request get descriptor * - * @param[in] hidh_device Pointer to HID device structure + * @param[in] hid_device Pointer to HID device structure * @param[in] req Pointer to a class specific request structure * @return esp_err_t */ @@ -788,7 +788,7 @@ static esp_err_t usb_class_request_get_descriptor(hid_device_t *hid_device, cons ESP_ERROR_CHECK(usb_host_device_info(hid_device->dev_hdl, &dev_info)); // reallocate the ctrl xfer buffer for new length ESP_LOGD(TAG, "Change HID ctrl xfer size from %d to %d", - ctrl_size, + (int) ctrl_size, (int) (USB_SETUP_PACKET_SIZE + req->wLength)); usb_host_transfer_free(hid_device->ctrl_xfer); @@ -829,7 +829,7 @@ static esp_err_t usb_class_request_get_descriptor(hid_device_t *hid_device, cons /** * @brief HID Host Request Report Descriptor * - * @param[in] hidh_iface Pointer to HID Interface configuration structure + * @param[in] iface Pointer to HID Interface configuration structure * @return esp_err_t */ static esp_err_t hid_class_request_report_descriptor(hid_iface_t *iface) diff --git a/host/class/hid/usb_host_hid/host_test/CMakeLists.txt b/host/class/hid/usb_host_hid/host_test/CMakeLists.txt new file mode 100644 index 00000000..cbeac04d --- /dev/null +++ b/host/class/hid/usb_host_hid/host_test/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) + +list(APPEND EXTRA_COMPONENT_DIRS + "$ENV{IDF_PATH}/tools/mocks/usb/" + "$ENV{IDF_PATH}/tools/mocks/freertos/" + ) + +project(host_test_usb_hid) diff --git a/host/class/hid/usb_host_hid/host_test/README.md b/host/class/hid/usb_host_hid/host_test/README.md new file mode 100644 index 00000000..83208ff8 --- /dev/null +++ b/host/class/hid/usb_host_hid/host_test/README.md @@ -0,0 +1,30 @@ +| Supported Targets | Linux | +| ----------------- | ----- | + +# Description + +This directory contains test code for `USB Host HID` driver. Namely: +* Simple public API call with mocked USB component to test Linux build and Cmock run for this class driver + +Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework, use CMock, so you must install Ruby on your machine to run them. + +# Build + +Tests build regularly like an idf project. Currently only working on Linux machines. + +``` +idf.py --preview set-target linux +idf.py build +``` + +# Run + +The build produces an executable in the build folder. + +Just run: + +``` +./build/host_test_usb_hid.elf +``` + +The test executable have some options provided by the test framework. diff --git a/host/class/hid/usb_host_hid/host_test/main/CMakeLists.txt b/host/class/hid/usb_host_hid/host_test/main/CMakeLists.txt new file mode 100644 index 00000000..bf8364ec --- /dev/null +++ b/host/class/hid/usb_host_hid/host_test/main/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register(SRC_DIRS . + REQUIRES cmock usb + INCLUDE_DIRS . + WHOLE_ARCHIVE) + +# Currently 'main' for IDF_TARGET=linux is defined in freertos component. +# Since we are using a freertos mock here, need to let Catch2 provide 'main'. +target_link_libraries(${COMPONENT_LIB} PRIVATE Catch2WithMain) diff --git a/host/class/hid/usb_host_hid/host_test/main/idf_component.yml b/host/class/hid/usb_host_hid/host_test/main/idf_component.yml new file mode 100644 index 00000000..ca8a3f1f --- /dev/null +++ b/host/class/hid/usb_host_hid/host_test/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + espressif/catch2: "^3.4.0" + usb_host_hid: + version: "*" + override_path: "../../" diff --git a/host/class/hid/usb_host_hid/host_test/main/test_unit_public_api.cpp b/host/class/hid/usb_host_hid/host_test/main/test_unit_public_api.cpp new file mode 100644 index 00000000..f2641a7f --- /dev/null +++ b/host/class/hid/usb_host_hid/host_test/main/test_unit_public_api.cpp @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "usb/hid_host.h" +#include "usb/hid.h" + +extern "C" { +#include "Mockusb_host.h" +#include "Mockqueue.h" +#include "Mocktask.h" +#include "Mockidf_additions.h" +#include "Mockportmacro.h" +} + +SCENARIO("HID Host install") +{ + hid_host_driver_config_t hid_host_driver_config = { + .create_background_task = true, + .task_priority = 5, + .stack_size = 4096, + .core_id = 0, + .callback = (reinterpret_cast(0xdeadbeef)), + .callback_arg = nullptr + }; + + // HID Host driver config set to nullptr + GIVEN("NO HID Host driver config, driver not already installed") { + + // Call hid_host_install with hid_host_driver_config set to nullptr + SECTION("Config is nullptr") { + REQUIRE(ESP_ERR_INVALID_ARG == hid_host_install(nullptr)); + } + } + + // HID Host driver config set to config from HID Host example, but without callback + GIVEN("Minimal HID Host driver config, driver not already installed") { + hid_host_driver_config.callback = nullptr; + + SECTION("Config error: no callback") { + // Call the DUT function, expect ESP_ERR_INVALID_ARG + REQUIRE(ESP_ERR_INVALID_ARG == hid_host_install(&hid_host_driver_config)); + } + + SECTION("Config error: stack size is 0") { + hid_host_driver_config.stack_size = 0; + // Call the DUT function, expect ESP_ERR_INVALID_ARG + REQUIRE(ESP_ERR_INVALID_ARG == hid_host_install(&hid_host_driver_config)); + } + + SECTION("Config error: task priority is 0") { + hid_host_driver_config.task_priority = 0; + // Call the DUT function, expect ESP_ERR_INVALID_ARG + REQUIRE(ESP_ERR_INVALID_ARG == hid_host_install(&hid_host_driver_config)); + } + } + + // HID Host driver config set to config from HID Host example + GIVEN("Full HID Host config, driver not already installed") { + int mtx; + + // Install error: failed to create semaphore + SECTION("Install error: unable to create semaphore") { + // We must call xQueueGenericCreate_ExpectAnyArgsAndReturn instead of xSemaphoreCreateBinary_ExpectAnyArgsAndReturn + // Because of missing Freertos Mocks + // Create a semaphore, return nullptr, so the semaphore is not created successfully + xQueueGenericCreate_ExpectAnyArgsAndReturn(nullptr); + + // Call the DUT function, expect ESP_ERR_NO_MEM + REQUIRE(ESP_ERR_NO_MEM == hid_host_install(&hid_host_driver_config)); + } + + // Unable to register client: Invalid state, goto fail + SECTION("Client register not successful: goto fail") { + // We must call xQueueGenericCreate_ExpectAnyArgsAndReturn instead of xSemaphoreCreateBinary_ExpectAnyArgsAndReturn + // Because of missing Freertos Mocks + // Create a semaphore, return the semaphore handle (Queue Handle in this scenario), so the semaphore is created successfully + xQueueGenericCreate_ExpectAnyArgsAndReturn(reinterpret_cast(&mtx)); + // Register a client, return ESP_ERR_INVALID_STATE, so the client is not registered successfully + usb_host_client_register_ExpectAnyArgsAndReturn(ESP_ERR_INVALID_STATE); + + // goto fail: delete the semaphore (Queue in this scenario) + vQueueDelete_Expect(reinterpret_cast(&mtx)); + + // Call the DUT function, expect ESP_ERR_INVALID_STATE + REQUIRE(ESP_ERR_INVALID_STATE == hid_host_install(&hid_host_driver_config)); + } + + SECTION("Client register successful: create background task fail") { + usb_host_client_handle_t client_handle; + + // Create a semaphore, return the semaphore handle (Queue Handle in this scenario), so the semaphore is created successfully + xQueueGenericCreate_ExpectAnyArgsAndReturn(reinterpret_cast(&mtx)); + // Register a client, return ESP_OK, so the client is registered successfully + usb_host_client_register_ExpectAnyArgsAndReturn(ESP_OK); + // Fill the pointer to the client_handle, which is used in goto fail, to deregister the client + usb_host_client_register_ReturnThruPtr_client_hdl_ret(&client_handle); + + // Create a background task, return pdFALSE, so the task in not created successfully + vPortEnterCritical_Expect(); + vPortExitCritical_Expect(); + xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdFALSE); + + // goto fail: delete the semaphore and deregister the client + // usb_host_client_deregister is not being checked for the return value !! Consider adding it to the host driver + usb_host_client_deregister_ExpectAndReturn(client_handle, ESP_OK); + // Delete the semaphore (Queue in this scenario) + vQueueDelete_Expect(reinterpret_cast(&mtx)); + + // Call the DUT function, expect ESP_ERR_NO_MEM + REQUIRE(ESP_ERR_NO_MEM == hid_host_install(&hid_host_driver_config)); + } + + // Call hid_host_install and expect successful installation + SECTION("Client register successful: hid_host_install successful") { + // Create semaphore, return semaphore handle (Queue Handle in this scenario), so the semaphore is created successfully + xQueueGenericCreate_ExpectAnyArgsAndReturn(reinterpret_cast(&mtx)); + // return ESP_OK, so the client is registered successfully + usb_host_client_register_ExpectAnyArgsAndReturn(ESP_OK); + + // Create a background task successfully by returning pdTRUE + vPortEnterCritical_Expect(); + vPortExitCritical_Expect(); + xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE); + + // Call the DUT function, expect ESP_OK + REQUIRE(ESP_OK == hid_host_install(&hid_host_driver_config)); + } + } + + // HID Host driver config set to config from HID Host example + GIVEN("Full HID Host config, driver already installed") { + + // Driver is already installed + SECTION("Install error: driver already installed") { + // Call the DUT function again to get the ESP_ERR_INVALID_STATE error + REQUIRE(ESP_ERR_INVALID_STATE == hid_host_install(&hid_host_driver_config)); + } + } +} diff --git a/host/class/hid/usb_host_hid/host_test/sdkconfig.defaults b/host/class/hid/usb_host_hid/host_test/sdkconfig.defaults new file mode 100644 index 00000000..14bc2aef --- /dev/null +++ b/host/class/hid/usb_host_hid/host_test/sdkconfig.defaults @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="linux" +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_ESP_MAIN_TASK_STACK_SIZE=12000 +CONFIG_FREERTOS_HZ=1000 +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n diff --git a/host/class/hid/usb_host_hid/idf_component.yml b/host/class/hid/usb_host_hid/idf_component.yml index 038bc633..8dc53f83 100644 --- a/host/class/hid/usb_host_hid/idf_component.yml +++ b/host/class/hid/usb_host_hid/idf_component.yml @@ -4,7 +4,3 @@ description: USB Host HID driver url: https://github.com/espressif/esp-usb/tree/master/host/class/hid/usb_host_hid dependencies: idf: ">=4.4" -targets: - - esp32s2 - - esp32s3 - - esp32p4 diff --git a/host/class/uvc/usb_host_uvc/host_test/CMakeLists.txt b/host/class/uvc/usb_host_uvc/host_test/CMakeLists.txt new file mode 100644 index 00000000..0347c762 --- /dev/null +++ b/host/class/uvc/usb_host_uvc/host_test/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +set(COMPONENTS main) + +list(APPEND EXTRA_COMPONENT_DIRS + "$ENV{IDF_PATH}/tools/mocks/usb/" + "$ENV{IDF_PATH}/tools/mocks/freertos/" + ) + +project(host_test_usb_uvc) diff --git a/host/class/uvc/usb_host_uvc/host_test/README.md b/host/class/uvc/usb_host_uvc/host_test/README.md new file mode 100644 index 00000000..32ab66fe --- /dev/null +++ b/host/class/uvc/usb_host_uvc/host_test/README.md @@ -0,0 +1,30 @@ +| Supported Targets | Linux | +| ----------------- | ----- | + +# Description + +This directory contains test code for `USB Host UVC` driver. Namely: +* Simple public API call with mocked USB component to test Linux build and Cmock run for this class driver + +Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework, use CMock, so you must install Ruby on your machine to run them. + +# Build + +Tests build regularly like an idf project. Currently only working on Linux machines. + +``` +idf.py --preview set-target linux +idf.py build +``` + +# Run + +The build produces an executable in the build folder. + +Just run: + +``` +./build/host_test_usb_uvc.elf +``` + +The test executable have some options provided by the test framework. diff --git a/host/class/uvc/usb_host_uvc/host_test/main/CMakeLists.txt b/host/class/uvc/usb_host_uvc/host_test/main/CMakeLists.txt new file mode 100644 index 00000000..6fa86b66 --- /dev/null +++ b/host/class/uvc/usb_host_uvc/host_test/main/CMakeLists.txt @@ -0,0 +1,9 @@ +idf_component_register(SRC_DIRS . + REQUIRES cmock usb + INCLUDE_DIRS . + PRIV_INCLUDE_DIRS "../../private_include" + WHOLE_ARCHIVE) + +# Currently 'main' for IDF_TARGET=linux is defined in freertos component. +# Since we are using a freertos mock here, need to let Catch2 provide 'main'. +target_link_libraries(${COMPONENT_LIB} PRIVATE Catch2WithMain) diff --git a/host/class/uvc/usb_host_uvc/host_test/main/idf_component.yml b/host/class/uvc/usb_host_uvc/host_test/main/idf_component.yml new file mode 100644 index 00000000..ac01bccf --- /dev/null +++ b/host/class/uvc/usb_host_uvc/host_test/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + espressif/catch2: "^3.4.0" + usb_host_uvc: + version: "*" + override_path: "../../" diff --git a/host/class/uvc/usb_host_uvc/host_test/main/test_unit_public_api.cpp b/host/class/uvc/usb_host_uvc/host_test/main/test_unit_public_api.cpp new file mode 100644 index 00000000..56b53bd5 --- /dev/null +++ b/host/class/uvc/usb_host_uvc/host_test/main/test_unit_public_api.cpp @@ -0,0 +1,28 @@ + +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "libuvc/libuvc.h" +#include "libuvc_helper.h" +#include "libuvc_adapter.h" + +extern "C" { +#include "Mockusb_host.h" +} + +SCENARIO("UVC Host Handle events") +{ + GIVEN("Libusb has not been initialized") { + + // Call libuvc_adapter_handle_events and expect ESP_ERR_INVALID_STATE + SECTION("s_uvc_driver is NULL") { + REQUIRE(ESP_ERR_INVALID_STATE == libuvc_adapter_handle_events(0)); + } + } +} diff --git a/host/class/uvc/usb_host_uvc/host_test/sdkconfig.defaults b/host/class/uvc/usb_host_uvc/host_test/sdkconfig.defaults new file mode 100644 index 00000000..14bc2aef --- /dev/null +++ b/host/class/uvc/usb_host_uvc/host_test/sdkconfig.defaults @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="linux" +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_ESP_MAIN_TASK_STACK_SIZE=12000 +CONFIG_FREERTOS_HZ=1000 +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n diff --git a/host/class/uvc/usb_host_uvc/idf_component.yml b/host/class/uvc/usb_host_uvc/idf_component.yml index 0cf10951..a4c376d0 100644 --- a/host/class/uvc/usb_host_uvc/idf_component.yml +++ b/host/class/uvc/usb_host_uvc/idf_component.yml @@ -8,7 +8,3 @@ sbom: manifests: - path: sbom_libuvc.yml dest: libuvc -targets: - - esp32s2 - - esp32s3 - - esp32p4 diff --git a/host/class/uvc/usb_host_uvc/include/libuvc_helper.h b/host/class/uvc/usb_host_uvc/include/libuvc_helper.h index c362bbc9..aaa10f4e 100644 --- a/host/class/uvc/usb_host_uvc/include/libuvc_helper.h +++ b/host/class/uvc/usb_host_uvc/include/libuvc_helper.h @@ -11,7 +11,7 @@ extern "C" { #endif -inline static char *uvc_error_string(uvc_error_t error) +inline static const char *uvc_error_string(uvc_error_t error) { switch (error) { case UVC_SUCCESS: return "UVC_SUCCESS"; diff --git a/host/class/uvc/usb_host_uvc/src/libusb_adapter.c b/host/class/uvc/usb_host_uvc/src/libusb_adapter.c index 25de0f3e..f3c74626 100644 --- a/host/class/uvc/usb_host_uvc/src/libusb_adapter.c +++ b/host/class/uvc/usb_host_uvc/src/libusb_adapter.c @@ -374,7 +374,7 @@ int32_t libusb_get_device_list(struct libusb_context *ctx, libusb_device ***list } for (size_t i = 0; i < actual_count; i++) { - dev_list[i] = (libusb_device *)(uint32_t)dev_addr_list[i]; + dev_list[i] = (libusb_device *)(uintptr_t)dev_addr_list[i]; } *list = (libusb_device **)dev_list; return actual_count; @@ -453,7 +453,7 @@ static esp_err_t close_device(uvc_camera_t *device) int libusb_open(libusb_device *dev, libusb_device_handle **dev_handle) { - uint8_t device_addr = (uint8_t)(uint32_t)dev; + uint8_t device_addr = (uint8_t)(uintptr_t)dev; uvc_camera_t *device; RETURN_ON_ERROR_LIBUSB( open_device_if_closed(device_addr, &device) ); @@ -672,7 +672,7 @@ int libusb_control_transfer(libusb_device_handle *dev_handle, int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) { - uint8_t device_addr = (uint8_t)(uint32_t)dev; + uint8_t device_addr = (uint8_t)(uintptr_t)dev; const usb_device_desc_t *device_desc; uvc_camera_t *device; @@ -705,7 +705,7 @@ int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config) { - uint8_t device_addr = (uint8_t)(uint32_t)dev; + uint8_t device_addr = (uint8_t)(uintptr_t)dev; const usb_config_desc_t *config_desc; uvc_camera_t *device; @@ -835,5 +835,5 @@ int8_t libusb_get_bus_number(libusb_device *device) int8_t libusb_get_device_address(libusb_device *device) { // Device address is stored directly in libusb_device - return (uint8_t)(uint32_t)device; + return (uint8_t)(uintptr_t)device; }