diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/CMakeLists.txt b/host/class/cdc/usb_host_cdc_acm/host_test/CMakeLists.txt index 460a5156..d703d412 100644 --- a/host/class/cdc/usb_host_cdc_acm/host_test/CMakeLists.txt +++ b/host/class/cdc/usb_host_cdc_acm/host_test/CMakeLists.txt @@ -8,4 +8,5 @@ list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/" ) +add_definitions("-DCMOCK_MEM_DYNAMIC") # We need a lot of memory for our frame buffers project(host_test_usb_cdc) diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/common_test_fixtures.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/main/common_test_fixtures.cpp new file mode 100644 index 00000000..b64fec75 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/main/common_test_fixtures.cpp @@ -0,0 +1,401 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "cdc_descriptors.hpp" +#include "usb/cdc_acm_host.h" +#include "mock_add_usb_device.h" +#include "common_test_fixtures.hpp" +#include "esp_system.h" +#include "cdc_host_descriptor_parsing.h" + +extern "C" { +#include "Mockusb_host.h" + +#include "Mockqueue.h" +#include "Mocktask.h" +#include "Mockidf_additions.h" +#include "Mockportmacro.h" +#include "Mockevent_groups.h" +} + +mock_cdc_acm_obj_t *p_mock_cdc_acm_obj = nullptr; +mock_cdc_dev_t *p_mock_cdc_dev = nullptr; + +/** + * @brief Create CDC device object + * + * @param[in] dev_address Device address + * @param[in] interface_idx Interface index to be used + * @param[in] dev_config CDC-ACM Host device config struct + * + * @return + * - ESP_OK: Device created successfully + * - ESP_ERR_NO_MEM: Not enough memory + */ +static esp_err_t _create_cdc_dev(uint8_t dev_address, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config) +{ + mock_cdc_dev_t *mock_cdc_dev = (mock_cdc_dev_t *)calloc(1, sizeof(mock_cdc_dev_t)); + if (mock_cdc_dev == nullptr) { + return ESP_ERR_NO_MEM; + } + + int notif_xfer, data_in_xfer, data_out_xfer; + const usb_config_desc_t *config_desc; + const usb_device_desc_t *device_desc; + cdc_parsed_info_t cdc_info; + int out_mux, out_sem, ctrl_mux, ctrl_sem; + usb_host_mock_get_config_descriptor_by_address(dev_address, &config_desc); + usb_host_mock_get_device_descriptor_by_address(dev_address, &device_desc); + cdc_parse_interface_descriptor(device_desc, config_desc, interface_idx, &cdc_info); + + // Get interface descriptors + mock_cdc_dev->data.intf_desc = cdc_info.data_intf; + mock_cdc_dev->notif.intf_desc = cdc_info.notif_intf; + // Get IN and OUT endpoints addresses + mock_cdc_dev->data.in_bEndpointAddress = cdc_info.in_ep->bEndpointAddress; + mock_cdc_dev->data.out_bEndpointAddress = cdc_info.out_ep->bEndpointAddress; + + // Get notification endpoint address and check if notification transfer is allocated (if notif. EP exists) + if (cdc_info.notif_ep) { + mock_cdc_dev->notif.bEndpointAddress = cdc_info.notif_ep->bEndpointAddress; + mock_cdc_dev->notif.xfer = reinterpret_cast(¬if_xfer); + } else { + mock_cdc_dev->notif.bEndpointAddress = 0; + mock_cdc_dev->notif.xfer = nullptr; + } + + // Check, if IN data transfer is allocated + if (dev_config->in_buffer_size) { + mock_cdc_dev->data.in_xfer = reinterpret_cast(&data_in_xfer); + } else { + mock_cdc_dev->data.in_xfer = nullptr; + } + + // Check if OUT data transfer is allocated + if (dev_config->out_buffer_size) { + mock_cdc_dev->data.out_xfer = reinterpret_cast(&data_out_xfer); + mock_cdc_dev->data.out_mux = reinterpret_cast(&out_mux); + mock_cdc_dev->data.out_sem = reinterpret_cast(&out_sem); + } else { + mock_cdc_dev->data.out_xfer = nullptr; + mock_cdc_dev->data.out_mux = nullptr; + mock_cdc_dev->data.out_sem = nullptr; + } + + mock_cdc_dev->ctrl_mux = reinterpret_cast(&ctrl_mux); + mock_cdc_dev->ctrl_sem = reinterpret_cast(&ctrl_sem); + + p_mock_cdc_dev = mock_cdc_dev; + return ESP_OK; +} + +/** + * @brief free space allocated for p_mock_cdc_dev + */ +static void _delete_cdc_dev(void) +{ + free(p_mock_cdc_dev); + p_mock_cdc_dev = nullptr; +} + +/** + * @brief Reset endpoint halt -> flush -> clear + * + * @param ep_address[in] Endpoint address to be reset + */ +static void _mock_cdc_acm_reset_transfer_endpoint(uint8_t ep_address) +{ + // Expect correct ep_address for all (halt, flush, clear) + usb_host_endpoint_halt_ExpectAndReturn(nullptr, ep_address, ESP_OK); + usb_host_endpoint_halt_IgnoreArg_dev_hdl(); + + usb_host_endpoint_flush_ExpectAndReturn(nullptr, ep_address, ESP_OK); + usb_host_endpoint_flush_IgnoreArg_dev_hdl(); + + usb_host_endpoint_clear_ExpectAndReturn(nullptr, ep_address, ESP_OK); + usb_host_endpoint_clear_IgnoreArg_dev_hdl(); +} + +esp_err_t mock_cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config) +{ + vPortEnterCritical_Ignore(); // Ignore expecting vPortEnterCritical() + vPortExitCritical_Ignore(); // Ignore expecting vPortExitCritical() + + if (p_mock_cdc_acm_obj != nullptr) { + // CDC-ACM already installed + return ESP_ERR_INVALID_STATE; + } + + mock_cdc_acm_obj_t *mock_cdc_acm_obj = (mock_cdc_acm_obj_t *)calloc(1, sizeof(mock_cdc_acm_obj_t)); + if (mock_cdc_acm_obj == nullptr) { + return ESP_ERR_NO_MEM; + } + + int task_handle_ptr; + // Ensure task_handle is non nullptr, as it is used in ReturnThruPtr type of CMock function + TaskHandle_t task_handle = reinterpret_cast(&task_handle_ptr); + usb_host_client_handle_t client_handle = reinterpret_cast(&mock_cdc_acm_obj->cdc_acm_client_hdl); + // Create an EventGroup, return event group handle, so the EventGroup is created successfully + xEventGroupCreate_ExpectAndReturn(reinterpret_cast(&mock_cdc_acm_obj->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(&mock_cdc_acm_obj->open_close_mutex)); + // Create a task, return pdTRUE, so the task is created successfully + 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); + usb_host_client_register_ReturnThruPtr_client_hdl_ret(&client_handle); + + // Resume the task + vTaskResume_Expect(task_handle); + + p_mock_cdc_acm_obj = mock_cdc_acm_obj; + + // Call the real function cdc_acm_host_install() + return cdc_acm_host_install(driver_config); +} + +esp_err_t mock_cdc_acm_host_uninstall(void) +{ + vPortEnterCritical_Ignore(); // Ignore expecting vPortEnterCritical() + vPortExitCritical_Ignore(); // Ignore expecting vPortExitCritical() + + xQueueSemaphoreTake_ExpectAndReturn(reinterpret_cast(&p_mock_cdc_acm_obj->open_close_mutex), portMAX_DELAY, pdTRUE); + + // Signal to CDC task to stop, unblock it and wait for its deletion + xEventGroupSetBits_ExpectAndReturn(reinterpret_cast(&p_mock_cdc_acm_obj->event_group), BIT0, pdTRUE); + + // Call mocked function from USB Host + // return ESP_OK, so the client si unblocked successfully + usb_host_client_unblock_ExpectAnyArgsAndReturn(ESP_OK); + xEventGroupWaitBits_ExpectAndReturn(reinterpret_cast(&p_mock_cdc_acm_obj->event_group), BIT1, pdFALSE, pdFALSE, pdMS_TO_TICKS(100), pdTRUE); + + // Free remaining resources + vEventGroupDelete_Expect(reinterpret_cast(&p_mock_cdc_acm_obj->event_group)); + xQueueGenericSend_ExpectAnyArgsAndReturn(pdTRUE); + vQueueDelete_Expect(reinterpret_cast(&p_mock_cdc_acm_obj->open_close_mutex)); + + free(p_mock_cdc_acm_obj); + p_mock_cdc_acm_obj = nullptr; + _delete_cdc_dev(); + + // Call the real function, cdc_acm_host_uninstall() + return cdc_acm_host_uninstall(); +} + +esp_err_t mock_cdc_acm_host_open(uint8_t dev_address, uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret) +{ + vPortEnterCritical_Ignore(); // Ignore expecting vPortEnterCritical() + vPortExitCritical_Ignore(); // Ignore expecting vPortExitCritical() + + esp_err_t ret_create_dev = _create_cdc_dev(dev_address, interface_idx, dev_config); + if (ret_create_dev != ESP_OK) { + return ret_create_dev; // ESP_ERR_NO_MEM + } + + xQueueSemaphoreTake_ExpectAndReturn(reinterpret_cast(&p_mock_cdc_acm_obj->open_close_mutex), portMAX_DELAY, pdTRUE); + vTaskSetTimeOutState_ExpectAnyArgs(); + + // Device opening + usb_host_device_addr_list_fill_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_addr_list_fill_AddCallback(usb_host_device_addr_list_fill_callback); + + // We are expecting usb_host_device_open, usb_host_get_device_descriptor usb_host_device_close + // to be called at least mocked_devs_count times + const int mocked_devs_count = usb_host_mock_get_devs_count(); // Get number of mocked USB devices in mocked device list + for (int i = 0; i < mocked_devs_count; i++) { + usb_host_device_open_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_device_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + } + + usb_host_device_open_AddCallback(usb_host_device_open_callback); + usb_host_get_device_descriptor_AddCallback(usb_host_get_device_descriptor_callback); + usb_host_device_close_AddCallback(usb_host_device_close_callback); + + // We have found the device by specified PID VID + + // Get Device and Configuration descriptors of the correct device + usb_host_get_device_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_active_config_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_active_config_descriptor_AddCallback(usb_host_get_active_config_descriptor_callback); + + // Setup control transfer + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + // Semaphore: cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary(); + xQueueGenericCreate_ExpectAnyArgsAndReturn(p_mock_cdc_dev->ctrl_sem); + // Mutex: cdc_dev->ctrl_mux = xSemaphoreCreateMutex(); + xQueueCreateMutex_ExpectAnyArgsAndReturn(p_mock_cdc_dev->ctrl_mux); + + // Setup Notif transfer + if (p_mock_cdc_dev->notif.xfer) { + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + } + + // Setup IN data transfer + if (dev_config->in_buffer_size) { + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + } + + // Setup OUT bulk transfer + if (dev_config->out_buffer_size) { + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + // Semaphore: cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary(); + xQueueGenericCreate_ExpectAnyArgsAndReturn(p_mock_cdc_dev->data.out_sem); + // Mutex: cdc_dev->data.out_mux = xSemaphoreCreateMutex(); + xQueueCreateMutex_ExpectAnyArgsAndReturn(p_mock_cdc_dev->data.out_mux); + } + + // Register callback + usb_host_transfer_alloc_AddCallback(usb_host_transfer_alloc_callback); + + // Call cdc_acm_start + + // Claim data interface + // Make sure that the interface_idx has been claimed + usb_host_interface_claim_ExpectAndReturn(nullptr, nullptr, interface_idx, 0, ESP_OK); + usb_host_interface_claim_IgnoreArg_client_hdl(); // Ignore all function parameters, except interface_number + usb_host_interface_claim_IgnoreArg_dev_hdl(); + usb_host_interface_claim_IgnoreArg_bAlternateSetting(); + usb_host_transfer_submit_ExpectAnyArgsAndReturn(ESP_OK); + + // Claim notification interface (if supported) + if (p_mock_cdc_dev->notif.xfer) { + usb_host_interface_claim_ExpectAndReturn(nullptr, nullptr, interface_idx, 0, ESP_OK); + usb_host_interface_claim_IgnoreArg_client_hdl(); // Ignore all function parameters, except interface_number + usb_host_interface_claim_IgnoreArg_dev_hdl(); + usb_host_interface_claim_IgnoreArg_bAlternateSetting(); + usb_host_transfer_submit_ExpectAnyArgsAndReturn(ESP_OK); + } + + xQueueGenericSend_ExpectAndReturn(reinterpret_cast(&p_mock_cdc_acm_obj->open_close_mutex), nullptr, 0, 0, pdTRUE); + xQueueGenericSend_IgnoreArg_pvItemToQueue(); + xQueueGenericSend_IgnoreArg_xCopyPosition(); + xQueueGenericSend_IgnoreArg_xTicksToWait(); + + // Call the real function cdc_acm_host_open + // Expect ESP_OK and dev_hdl non nullptr + esp_err_t ret = cdc_acm_host_open(vid, pid, interface_idx, dev_config, cdc_hdl_ret); + + // If the cdc_acm_host_open() fails, delete the created cdc_device + if (ret != ESP_OK) { + _delete_cdc_dev(); + } + return ret; +} + +esp_err_t mock_cdc_acm_host_close(cdc_acm_dev_hdl_t *cdc_hdl, uint8_t interface_number) +{ + vPortEnterCritical_Ignore(); // Ignore vPortEnterCritical() during this test case + vPortExitCritical_Ignore(); // Ignore vPortExitCritical() during this test case + + xQueueSemaphoreTake_ExpectAndReturn(reinterpret_cast(&p_mock_cdc_acm_obj->open_close_mutex), portMAX_DELAY, pdTRUE); + + // Cancel pooling of IN endpoint -> halt, flush, clear + _mock_cdc_acm_reset_transfer_endpoint(p_mock_cdc_dev->data.in_bEndpointAddress); + + // Cancel pooling of Notification endpoint -> halt, flush, clear + if (p_mock_cdc_dev->notif.xfer) { + _mock_cdc_acm_reset_transfer_endpoint(p_mock_cdc_dev->notif.bEndpointAddress); + } + + // Release data interface + usb_host_interface_release_ExpectAndReturn(nullptr, nullptr, interface_number, ESP_OK); + usb_host_interface_release_IgnoreArg_client_hdl(); // Ignore all function parameters, except interface_number + usb_host_interface_release_IgnoreArg_dev_hdl(); + + + // Free notif transfer + if (p_mock_cdc_dev->notif.xfer) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + p_mock_cdc_dev->notif.xfer = nullptr; + } + + // Free in transfer + if (p_mock_cdc_dev->data.in_xfer) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + p_mock_cdc_dev->data.in_xfer = nullptr; + } + + // Free out transfer + if (p_mock_cdc_dev->data.out_xfer) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + vQueueDelete_Expect(p_mock_cdc_dev->data.out_sem); + vQueueDelete_Expect(p_mock_cdc_dev->data.out_mux); + p_mock_cdc_dev->data.out_xfer = nullptr; + } + + // Call cdc_acm_device_remove + // Free ctrl transfer + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + vQueueDelete_Expect(p_mock_cdc_dev->ctrl_sem); + vQueueDelete_Expect(p_mock_cdc_dev->ctrl_mux); + + usb_host_transfer_free_AddCallback(usb_host_transfer_free_callback); + + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + + xQueueGenericSend_ExpectAndReturn(reinterpret_cast(&p_mock_cdc_acm_obj->open_close_mutex), nullptr, 0, 0, pdTRUE); + xQueueGenericSend_IgnoreArg_pvItemToQueue(); + xQueueGenericSend_IgnoreArg_xCopyPosition(); + xQueueGenericSend_IgnoreArg_xTicksToWait(); + + // Call the real function cdc_acm_host_close + return cdc_acm_host_close(*cdc_hdl); +} + +esp_err_t mock_cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl, const uint8_t *data, size_t data_len, uint32_t timeout_ms, mock_usb_transfer_response_t transfer_response) +{ + vPortEnterCritical_Ignore(); // Ignore vPortEnterCritical() during this test case + vPortExitCritical_Ignore(); // Ignore vPortExitCritical() during this test case + + xQueueSemaphoreTake_ExpectAndReturn(p_mock_cdc_dev->data.out_mux, pdMS_TO_TICKS(timeout_ms), pdTRUE); + xQueueSemaphoreTake_ExpectAndReturn(p_mock_cdc_dev->data.out_sem, 0, pdTRUE); + usb_host_transfer_submit_ExpectAnyArgsAndReturn(ESP_OK); + + switch (transfer_response) { + case MOCK_USB_TRANSFER_SUCCESS: { + // Make the submitted transfer to pass + usb_host_transfer_submit_AddCallback(usb_host_transfer_submit_success_callback); + xQueueSemaphoreTake_ExpectAndReturn(p_mock_cdc_dev->data.out_sem, pdMS_TO_TICKS(timeout_ms), pdTRUE); + break; + } + case MOCK_USB_TRANSFER_ERROR: { + // Make the submitted transfer to fail + usb_host_transfer_submit_AddCallback(usb_host_transfer_submit_fail_callback); + xQueueSemaphoreTake_ExpectAndReturn(p_mock_cdc_dev->data.out_sem, pdMS_TO_TICKS(timeout_ms), pdTRUE); + break; + } + case MOCK_USB_TRANSFER_TIMEOUT: { + // Make the submitted transfer to be timed out + usb_host_transfer_submit_AddCallback(usb_host_transfer_submit_success_callback); + xQueueSemaphoreTake_ExpectAndReturn(p_mock_cdc_dev->data.out_sem, pdMS_TO_TICKS(timeout_ms), pdFALSE); + + // Reset out endpoint + _mock_cdc_acm_reset_transfer_endpoint(p_mock_cdc_dev->data.out_bEndpointAddress); + break; + } + default: + break; + } + + xQueueGenericSend_ExpectAndReturn(p_mock_cdc_dev->data.out_mux, 0, 0, 0, pdTRUE); + xQueueGenericSend_IgnoreArg_pvItemToQueue(); + xQueueGenericSend_IgnoreArg_xCopyPosition(); + xQueueGenericSend_IgnoreArg_xTicksToWait(); + + // Call the real function cdc_acm_host_data_tx_blocking() + return cdc_acm_host_data_tx_blocking(cdc_hdl, data, data_len, timeout_ms); +} diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/common_test_fixtures.hpp b/host/class/cdc/usb_host_cdc_acm/host_test/main/common_test_fixtures.hpp new file mode 100644 index 00000000..efef54ac --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/main/common_test_fixtures.hpp @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "freertos/queue.h" + +#pragma once + +/** + * @brief CDC-ACM mocked driver object + * + * Variation of cdc_acm_obj_t *p_cdc_acm_obj; but for host tests + */ +typedef struct { + int open_close_mutex; + int cdc_acm_client_hdl; + int event_group; +} mock_cdc_acm_obj_t; + +/** + * @brief CDC-ACM device + * + * Variation of cdc_dev_t but for host tests + */ +typedef struct { + struct { + usb_transfer_t *out_xfer; + usb_transfer_t *in_xfer; + const usb_intf_desc_t *intf_desc; + QueueHandle_t out_mux; // This should be SemaphoreHandle_t, but freertos mock is not correct + QueueHandle_t out_sem; // cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary(); + uint8_t in_bEndpointAddress; + uint8_t out_bEndpointAddress; + } data; + struct { + usb_transfer_t *xfer; + const usb_intf_desc_t *intf_desc; + uint8_t bEndpointAddress; + } notif; + usb_transfer_t *ctr_transfer; + QueueHandle_t ctrl_mux; // This should be SemaphoreHandle_t, but freertos mock is not correct + QueueHandle_t ctrl_sem; // cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary(); +} mock_cdc_dev_t; + +/** + * @brief Intended transfer response + * + * It can be defined, how the is submitted, we can successfully submit a transfer, or we can induce some transfer errors + */ +typedef enum { + MOCK_USB_TRANSFER_SUCCESS, /*!< Transfer submitted successfully */ + MOCK_USB_TRANSFER_ERROR, /*!< Transfer error */ + MOCK_USB_TRANSFER_TIMEOUT /*!< Transfer timeout */ +} mock_usb_transfer_response_t; + + +/** + * @brief Host test fixture, for installing CDC-ACM host driver + * + * - This function calls the real cdc_acm_host_install() function with USB Host stack CMock expectations + * + * @param[in] driver_config configuration for CDC-ACM Host + * + * @note This function returns the same parameters as the real the cdc_acm_host_install() function + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: The CDC driver is already installed or USB host library is not installed + * - ESP_ERR_NO_MEM: Not enough memory for installing the driver + */ +esp_err_t mock_cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config); + +/** + * @brief Host test fixture, for uninstalling CDC-ACM host driver + * + * - This function calls the real cdc_acm_host_uninstall() function with USB Host stack CMock expectations + * + * @note This function returns the same parameters as the real the cdc_acm_host_uninstall() function + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: The CDC driver is not installed or not all CDC devices are closed + * - ESP_ERR_NOT_FINISHED: The CDC driver failed to uninstall completely + */ +esp_err_t mock_cdc_acm_host_uninstall(void); + +/** + * @brief Host test fixture, for opening CDC-ACM device + * + * - This function calls the real cdc_acm_host_open() functions with USB Host stack CMock expectations + * + * @param[in] device_addr Device's Address + * @param[in] vid Device's Vendor ID + * @param[in] pid Device's Product ID + * @param[in] interface_idx Index of device's interface used for CDC-ACM communication + * @param[in] dev_config Configuration structure of the device + * @param[out] cdc_hdl_ret CDC device handle + * + * @note This function returns the same parameters as the real the cdc_acm_host_open() function + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: The CDC driver is not installed + * - ESP_ERR_INVALID_ARG: dev_config or cdc_hdl_ret is NULL + * - ESP_ERR_NO_MEM: Not enough memory for opening the device + * - ESP_ERR_NOT_FOUND: USB device with specified VID/PID is not connected or does not have specified interface + */ +esp_err_t mock_cdc_acm_host_open(uint8_t device_addr, uint16_t vid, uint16_t pid, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret); + +/** + * @brief Host test fixture, for closing CDC-ACM device + * + * - This function calls the real cdc_acm_host_close() functions with USB Host stack CMock expectations + * + * @param[in] cdc_hdl CDC handle obtained from cdc_acm_host_open() + * @param[in] interface_idx Index of device's interface used for CDC-ACM communication + * + * @note This function returns the same parameters as the real the cdc_acm_host_open() function + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_STATE: The CDC driver is not installed + * - ESP_ERR_INVALID_ARG: dev_config or cdc_hdl_ret is NULL + * - ESP_ERR_NO_MEM: Not enough memory for opening the device + * - ESP_ERR_NOT_FOUND: USB device with specified VID/PID is not connected or does not have specified interface + */ +esp_err_t mock_cdc_acm_host_close(cdc_acm_dev_hdl_t *cdc_hdl, uint8_t interface_idx); + +/** + * @brief Send transfer to mocked device + * + * - This function calls the real cdc_acm_host_data_tx_blocking() functions with USB Host stack CMock expectations + * + * @param[in] cdc_hdl CDC handle obtained from cdc_acm_host_open() + * @param[in] data Data to be sent + * @param[in] data_len Data length + * @param[in] timeout_ms Timeout in [ms] + * @param[in] transfer_response Intended transfer response (for testing multiple scenarios) + * + * @note This function returns the same parameters as the real the cdc_acm_host_data_tx_blocking() function + * @return + * - ESP_OK: Success + * - ESP_ERR_NOT_SUPPORTED: The device was opened as read only + * - ESP_ERR_INVALID_ARG: Invalid input arguments + * - ESP_ERR_INVALID_SIZE: Invalid size of data to be sent + * - ESP_ERR_TIMEOUT: tx transfer has timed out + * - ESP_ERR_INVALID_RESPONSE: Invalid transfer response + */ +esp_err_t mock_cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl, const uint8_t *data, size_t data_len, uint32_t timeout_ms, mock_usb_transfer_response_t transfer_response); diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_device_interaction.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/main/test_device_interaction.cpp new file mode 100644 index 00000000..90387f4c --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/main/test_device_interaction.cpp @@ -0,0 +1,188 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "cdc_descriptors.hpp" +#include "test_parsing_checker.hpp" +#include "usb/cdc_acm_host.h" +#include "mock_add_usb_device.h" +#include "common_test_fixtures.hpp" +#include "usb_helpers.h" +#include "cdc_host_descriptor_parsing.h" + +extern "C" { +#include "Mockusb_host.h" + +#include "Mockqueue.h" +#include "Mocktask.h" +#include "Mockidf_additions.h" +#include "Mockportmacro.h" +#include "Mockevent_groups.h" +} + +/** + * @brief Add mocked devices + * + * Mocked devices are defined by a device descriptor, a configuration descriptor and a device address + */ +static void _add_mocked_devices(void) +{ + // Init mocked devices list at the beginning of the test + usb_host_mock_dev_list_init(); + + // Fill mocked devices list + + // ASIX Electronics Corp. AX88772A Fast Ethernet (FS descriptor) + REQUIRE(ESP_OK == usb_host_mock_add_device(1, (const usb_device_desc_t *)premium_cord_device_desc_fs, + (const usb_config_desc_t *)premium_cord_config_desc_fs)); + + // ASIX Electronics Corp. AX88772B (FS descriptor) + REQUIRE(ESP_OK == usb_host_mock_add_device(2, (const usb_device_desc_t *)i_tec_device_desc_fs, + (const usb_config_desc_t *)i_tec_config_desc_fs)); + + // FTDI chip dual (FS descriptor) + REQUIRE(ESP_OK == usb_host_mock_add_device(3, (const usb_device_desc_t *)ftdi_device_desc_fs_hs, + (const usb_config_desc_t *)ftdi_config_desc_fs)); + + // TTL232RG (FS descriptor) + REQUIRE(ESP_OK == usb_host_mock_add_device(4, (const usb_device_desc_t *)ttl232_device_desc, + (const usb_config_desc_t *)ttl232_config_desc)); + + // CP210x (FS descriptor) + REQUIRE(ESP_OK == usb_host_mock_add_device(5, (const usb_device_desc_t *)cp210x_device_desc, + (const usb_config_desc_t *)cp210x_config_desc)); +} + +/** + * @brief Submit mock transfers to opened mocked device + * + * This function will submit couple of transfers to the mocked opened device, to test interaction of CDC-ACM host driver + * with the mocked USB Host stack and mocked USB device + * + * @param[in] dev CDC handle obtained from cdc_acm_host_open() + */ +static void _submit_mock_transfer(cdc_acm_dev_hdl_t *dev) +{ + const uint8_t tx_buf[] = "HELLO"; + // Submit transfer successfully + REQUIRE(ESP_OK == mock_cdc_acm_host_data_tx_blocking(*dev, tx_buf, sizeof(tx_buf), 1000, MOCK_USB_TRANSFER_SUCCESS)); + // Submit transfer which will fail to submit + REQUIRE(ESP_ERR_INVALID_RESPONSE == mock_cdc_acm_host_data_tx_blocking(*dev, tx_buf, sizeof(tx_buf), 1000, MOCK_USB_TRANSFER_ERROR)); + // Submit transfer which times out + REQUIRE(ESP_ERR_TIMEOUT == mock_cdc_acm_host_data_tx_blocking(*dev, tx_buf, sizeof(tx_buf), 1000, MOCK_USB_TRANSFER_TIMEOUT)); +} + + +SCENARIO("Interact with mocked USB devices") +{ + // We will put device adding to the SECTION, to run it just once, not repeatedly for all the following SECTIONs + SECTION("Add mocked devices") { + + _add_mocked_devices(); + + // Optionally, print all the devices + //usb_host_mock_print_mocked_devices(0); + } + + GIVEN("Mocked devices are added to the device list") { + // Install CDC-ACM driver + REQUIRE(ESP_OK == mock_cdc_acm_host_install(nullptr)); + + cdc_acm_dev_hdl_t dev = nullptr; + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 1000, + .out_buffer_size = 100, + .in_buffer_size = 100, + .event_cb = nullptr, + .data_cb = nullptr, + .user_arg = nullptr, + }; + + // We can use this approach, in case the device opening and closing are not our DUT functions + + // Eg, in case we want to open a device, test something with the opened device open and close the device + // Test fixtures for seamless device opening and closing mock_cdc_acm_host_open(), mock_cdc_acm_host_close() + SECTION("Interact with device: ASIX Electronics Corp. AX88772A Fast Ethernet") { + + // Define details of a device which will be opened + const uint16_t vid = 0xB95, pid = 0x772A; + const uint8_t device_address = 1, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == mock_cdc_acm_host_open(device_address, vid, pid, interface_index, &dev_config, &dev)); + REQUIRE(dev != nullptr); + // Interact with the device - submit mocked transfers + _submit_mock_transfer(&dev); + // Close the device + REQUIRE(ESP_OK == mock_cdc_acm_host_close(&dev, interface_index)); + } + + SECTION("Interact with device: ASIX Electronics Corp. AX88772B") { + + // Define details of a device which will be opened + const uint16_t vid = 0xB95, pid = 0x772B; + const uint8_t device_address = 2, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == mock_cdc_acm_host_open(device_address, vid, pid, interface_index, &dev_config, &dev)); + REQUIRE(dev != nullptr); + // Interact with the device - submit mocked transfers + _submit_mock_transfer(&dev); + // Close the device + REQUIRE(ESP_OK == mock_cdc_acm_host_close(&dev, interface_index)); + } + + SECTION("Interact with device: FTDI chip dual (FS descriptor)") { + + // Define details of a device which will be opened + const uint16_t vid = 0x403, pid = 0x6010; + const uint8_t device_address = 3, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == mock_cdc_acm_host_open(device_address, vid, pid, interface_index, &dev_config, &dev)); + REQUIRE(dev != nullptr); + // Interact with the device - submit mocked transfers + _submit_mock_transfer(&dev); + // Close the device + REQUIRE(ESP_OK == mock_cdc_acm_host_close(&dev, interface_index)); + } + + SECTION("Interact with device: TTL232RG (FS descriptor)") { + + // Define details of a device which will be opened + const uint16_t vid = 0x403, pid = 0x6001; + const uint8_t device_address = 4, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == mock_cdc_acm_host_open(device_address, vid, pid, interface_index, &dev_config, &dev)); + REQUIRE(dev != nullptr); + // Interact with the device - submit mocked transfers + _submit_mock_transfer(&dev); + // Close the device + REQUIRE(ESP_OK == mock_cdc_acm_host_close(&dev, interface_index)); + } + + SECTION("Interact with device: CP210x") { + + // Define details of a device which will be opened + const uint16_t vid = 0x10C4, pid = 0xEA60; + const uint8_t device_address = 5, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == mock_cdc_acm_host_open(device_address, vid, pid, interface_index, &dev_config, &dev)); + REQUIRE(dev != nullptr); + // Interact with the device - submit mocked transfers + _submit_mock_transfer(&dev); + // Close the device + REQUIRE(ESP_OK == mock_cdc_acm_host_close(&dev, interface_index)); + } + + // Uninstall CDC-ACM driver + REQUIRE(ESP_OK == mock_cdc_acm_host_uninstall()); + } +} diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_opening_device.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/main/test_opening_device.cpp new file mode 100644 index 00000000..c1e4cf68 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/main/test_opening_device.cpp @@ -0,0 +1,255 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "cdc_descriptors.hpp" +#include "test_parsing_checker.hpp" +#include "usb/cdc_acm_host.h" +#include "mock_add_usb_device.h" +#include "common_test_fixtures.hpp" +#include "usb_helpers.h" +#include "cdc_host_descriptor_parsing.h" + +extern "C" { +#include "Mockusb_host.h" + +#include "Mockqueue.h" +#include "Mocktask.h" +#include "Mockidf_additions.h" +#include "Mockportmacro.h" +#include "Mockevent_groups.h" +} + +/** + * @brief Add mocked devices + * + * Mocked devices are defined by a device descriptor, a configuration descriptor and a device address + */ +static void _add_mocked_devices(void) +{ + // Init mocked devices list at the beginning of the test + usb_host_mock_dev_list_init(); + + // Fill mocked devices list + // CP210x (FS descriptor) + REQUIRE(ESP_OK == usb_host_mock_add_device(5, (const usb_device_desc_t *)cp210x_device_desc, + (const usb_config_desc_t *)cp210x_config_desc)); +} + +/** + * @brief Open mocked device from mocked device list + * + * @note Device(s) must be firstly added to mocked device list by usb_host_mock_add_device() + * + * @param[in] vid VID of a device to be opened + * @param[in] pid PID of a device to be opened + * @param[in] interface_numer Interface number of a device to be opened + * @param[in] dev_config CDC-ACM device configuration + * @param[out] dev device handle + */ +static void _open_device(uint16_t vid, uint16_t pid, uint8_t interface_number, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *dev) +{ + xQueueSemaphoreTake_ExpectAnyArgsAndReturn(pdTRUE); + vTaskSetTimeOutState_ExpectAnyArgs(); + + // Device opening + usb_host_device_addr_list_fill_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_addr_list_fill_AddCallback(usb_host_device_addr_list_fill_callback); + + // We are expecting usb_host_device_open, usb_host_get_device_descriptor usb_host_device_close + // to be called at least mocked_devs_count times + // Get number of mocked USB devices in mocked device list + const int mocked_devs_count = usb_host_mock_get_devs_count(); + for (int i = 0; i < mocked_devs_count; i++) { + usb_host_device_open_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_device_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + } + usb_host_device_open_AddCallback(usb_host_device_open_callback); + usb_host_get_device_descriptor_AddCallback(usb_host_get_device_descriptor_callback); + usb_host_device_close_AddCallback(usb_host_device_close_callback); + + // We have found the device by specified PID VID + + // Get Device and Configuration descriptors of the correct device + usb_host_get_device_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_active_config_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_active_config_descriptor_AddCallback(usb_host_get_active_config_descriptor_callback); + + // Call cdc_acm_transfer_allocate + int ctrl_transfer_semaphore, data_out_transfer_semaphore; + int ctrl_transfer_mutex, data_out_transfer_mutex; + + // Setup control transfer + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + // Semaphore: cdc_dev->ctrl_transfer->context = xSemaphoreCreateBinary(); + xQueueGenericCreate_ExpectAnyArgsAndReturn(reinterpret_cast(&ctrl_transfer_semaphore)); + // Mutex: cdc_dev->ctrl_mux = xSemaphoreCreateMutex(); + xQueueCreateMutex_ExpectAnyArgsAndReturn(reinterpret_cast(&ctrl_transfer_mutex)); + + // Setup IN data transfer + if (dev_config->in_buffer_size) { + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + } + + // Setup OUT bulk transfer + if (dev_config->out_buffer_size) { + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + // Semaphore: cdc_dev->data.out_xfer->context = xSemaphoreCreateBinary(); + xQueueGenericCreate_ExpectAnyArgsAndReturn(reinterpret_cast(&data_out_transfer_semaphore)); + // Mutex: cdc_dev->data.out_mux = xSemaphoreCreateMutex(); + xQueueCreateMutex_ExpectAnyArgsAndReturn(reinterpret_cast(&data_out_transfer_mutex)); + } + + // Register callback + usb_host_transfer_alloc_AddCallback(usb_host_transfer_alloc_callback); + + // Call cdc_acm_start + // Make sure that the interface_number has been claimed + usb_host_interface_claim_ExpectAndReturn(nullptr, nullptr, interface_number, 0, ESP_OK); + usb_host_interface_claim_IgnoreArg_client_hdl(); + usb_host_interface_claim_IgnoreArg_dev_hdl(); + usb_host_interface_claim_IgnoreArg_bAlternateSetting(); + + usb_host_transfer_submit_ExpectAnyArgsAndReturn(ESP_OK); + xQueueGenericSend_ExpectAnyArgsAndReturn(pdTRUE); + + // Call the real DUT function cdc_acm_host_open() + // Expect ESP_OK and dev_hdl non nullptr + REQUIRE(ESP_OK == cdc_acm_host_open(vid, pid, interface_number, dev_config, dev)); + REQUIRE(dev != nullptr); +} + +/** + * @brief Just print the device and configuration descriptor of opened mocked device + * + * @note Device must be firstly opened by cdc_acm_host_open() + * + * @param[in] dev device handle + */ +static void _print_descriptor(cdc_acm_dev_hdl_t *dev) +{ + // Test that the mocked device was opened and can be used by the CDC-ACM host driver + usb_host_get_device_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_active_config_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + + // Call DUT function cdc_acm_host_desc_print + // Expect device and configuration descriptors will be printed + cdc_acm_host_desc_print(*dev); +} + +/** + * @brief Close mocked device + * + * @note Device must be firstly opened by cdc_acm_host_open() + * + * @param[in] interface_numer Interface number of a device to be closed + * @param[in] dev_config CDC-ACM device configuration + * @param[out] dev device handle + */ +static void _close_device(uint8_t device_address, uint8_t interface_idx, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *dev) +{ + // Get IN_EP address + const usb_config_desc_t *config_desc; + const usb_device_desc_t *device_desc; + cdc_parsed_info_t cdc_info; + usb_host_mock_get_config_descriptor_by_address(device_address, &config_desc); + usb_host_mock_get_device_descriptor_by_address(device_address, &device_desc); + cdc_parse_interface_descriptor(device_desc, config_desc, interface_idx, &cdc_info); + + xQueueSemaphoreTake_ExpectAnyArgsAndReturn(pdTRUE); + + // Cancel pooling of Interrupt IN endpoint -> halt, flush, clear + // Make sure to halt, flush, clear correct endpoints + usb_host_endpoint_halt_ExpectAndReturn(nullptr, cdc_info.in_ep->bEndpointAddress, ESP_OK); + usb_host_endpoint_halt_IgnoreArg_dev_hdl(); + + usb_host_endpoint_flush_ExpectAndReturn(nullptr, cdc_info.in_ep->bEndpointAddress, ESP_OK); + usb_host_endpoint_flush_IgnoreArg_dev_hdl(); + + usb_host_endpoint_clear_ExpectAndReturn(nullptr, cdc_info.in_ep->bEndpointAddress, ESP_OK); + usb_host_endpoint_clear_IgnoreArg_dev_hdl(); + + // Release data interface, make sure to release a correct interface + usb_host_interface_release_ExpectAndReturn(nullptr, nullptr, interface_idx, ESP_OK); + usb_host_interface_release_IgnoreArg_client_hdl(); + usb_host_interface_release_IgnoreArg_dev_hdl(); + + // Call cdc_acm_device_remove + // Free ctrl transfer + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + vQueueDelete_ExpectAnyArgs(); + vQueueDelete_ExpectAnyArgs(); + + // Free in transfer + if (dev_config->in_buffer_size) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + } + + // Free out transfer + if (dev_config->out_buffer_size) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + vQueueDelete_ExpectAnyArgs(); + vQueueDelete_ExpectAnyArgs(); + } + usb_host_transfer_free_AddCallback(usb_host_transfer_free_callback); + + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + xQueueGenericSend_ExpectAnyArgsAndReturn(pdTRUE); + + // Call the real DUT function cdc_acm_host_close() + // Expect ESP_OK + REQUIRE(ESP_OK == cdc_acm_host_close(*dev)); +} + +SCENARIO("Test mocked device opening and closing") +{ + // We will put device adding to the SECTION, to run it just once, not repeatedly for all the following SECTIONs + // (if multiple sections are present) + SECTION("Add mocked device") { + _add_mocked_devices(); + + // Optionally, print all the devices + //usb_host_mock_print_mocked_devices(0); + } + + GIVEN("Mocked device is added to the device list") { + // Install CDC-ACM driver + REQUIRE(ESP_OK == mock_cdc_acm_host_install(nullptr)); + + cdc_acm_dev_hdl_t dev = nullptr; + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 1000, + .out_buffer_size = 100, + .in_buffer_size = 100, + .event_cb = nullptr, + .data_cb = nullptr, + .user_arg = nullptr, + }; + + // We can use this approach, in case the device opening and closing are our DUT functions + // Eg, in case we want to test device opening and closing itself + SECTION("Test device opening and closing") { + + // Define input parameters for cdc_acm_host_open + const uint16_t vid = 0x10C4, pid = 0xEA60; + const uint8_t device_address = 5; + const uint8_t interface_number = 0; + + // ------------------------------------------ Open a device ------------------------------------------------ + _open_device(vid, pid, interface_number, &dev_config, &dev); + // ------------------------------------------ Get device info (use the device) ----------------------------- + _print_descriptor(&dev); + // ------------------------------------------ Close the device --------------------------------------------- + _close_device(device_address, interface_number, &dev_config, &dev); + } + + // Uninstall CDC-ACM driver + REQUIRE(ESP_OK == mock_cdc_acm_host_uninstall()); + } +} 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 index d4424848..93084e81 100644 --- 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 @@ -9,6 +9,7 @@ #include #include "usb/cdc_acm_host.h" +#include "esp_system.h" extern "C" { #include "Mockusb_host.h" @@ -19,16 +20,33 @@ extern "C" { #include "Mockevent_groups.h" } -SCENARIO("CDC-ACM Host install") +int task_handle_ptr; +// Ensure task_handle is non nullptr, as it is used in ReturnThruPtr type of Cmock function +TaskHandle_t task_handle = reinterpret_cast(&task_handle_ptr); +int sem; +int event_group; + + +SCENARIO("CDC-ACM Host install/uninstall") { // 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 + // Try to uninstall CDC ACM Host (not previously installed) + // Expect fail because of p_cdc_acm_obj being nullptr + SECTION("Try to uninstall not installed CDC ACM Host") { + + vPortEnterCritical_Expect(); + vPortExitCritical_Expect(); + + // Call the DUT function, expect ESP_ERR_INVALID_STATE + REQUIRE(ESP_ERR_INVALID_STATE == cdc_acm_host_uninstall()); + } + + // Try to install CDC ACM Host, induce an error during install + // Expect 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 @@ -48,8 +66,10 @@ SCENARIO("CDC-ACM Host install") REQUIRE(ESP_ERR_NO_MEM == cdc_acm_host_install(nullptr)); } - // Call cdc_acm_host_install, expect to successfully install the CDC ACM host + // Install CDC ACM Host + // 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 @@ -74,4 +94,42 @@ SCENARIO("CDC-ACM Host install") REQUIRE(ESP_OK == cdc_acm_host_install(nullptr)); } } + + GIVEN("NO CDC-ACM Host driver config, driver already installed") { + + // Try to install CDC ACM Host again, while it is already installed (from the previous test section) + // Expect fail because of p_cdc_acm_obj being non nullptr + SECTION("Try to install CDC ACM Host again") { + + // Call the DUT function, expect ESP_ERR_INVALID_STATE + REQUIRE(ESP_ERR_INVALID_STATE == cdc_acm_host_install(nullptr)); + } + + // Uninstall CDC ACM Host + // Expect to successfully uninstall the CDC ACM Host + SECTION("Successfully uninstall CDC ACM Host") { + + vPortEnterCritical_Expect(); + vPortExitCritical_Expect(); + xQueueSemaphoreTake_ExpectAndReturn(reinterpret_cast(&sem), portMAX_DELAY, pdTRUE); + vPortEnterCritical_Expect(); + vPortExitCritical_Expect(); + + // Signal to CDC task to stop, unblock it and wait for its deletion + xEventGroupSetBits_ExpectAndReturn(reinterpret_cast(&event_group), BIT0, pdTRUE); + + // Call mocked function from USB Host + // return ESP_OK, so the client si unblocked successfully + usb_host_client_unblock_ExpectAnyArgsAndReturn(ESP_OK); + xEventGroupWaitBits_ExpectAndReturn(reinterpret_cast(&event_group), BIT1, pdFALSE, pdFALSE, pdMS_TO_TICKS(100), pdTRUE); + + // Free remaining resources + vEventGroupDelete_Expect(reinterpret_cast(&event_group)); + xQueueGenericSend_ExpectAnyArgsAndReturn(pdTRUE); + vQueueDelete_Expect(reinterpret_cast(&sem)); + + // Call the DUT function, expect ESP_OK + REQUIRE(ESP_OK == cdc_acm_host_uninstall()); + } + } }