diff --git a/.build-test-rules.yml b/.build-test-rules.yml index 83e5f54a..e11856ad 100644 --- a/.build-test-rules.yml +++ b/.build-test-rules.yml @@ -14,15 +14,15 @@ host/class/uvc/usb_host_uvc/examples/camera_display: # 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) - + - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 5) + reason: USB mocks are run only for the latest version of IDF host/class/hid/usb_host_hid/host_test: enable: - - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4) - reason: USB mock was added in v5.4 + - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 5) + reason: USB mocks are run only for the latest version of IDF host/class/uvc/usb_host_uvc/host_test: enable: - - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4) - reason: USB mock was added in v5.4 + - if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 5) + reason: USB mocks are run only for the latest version of IDF diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/descriptors/cdc_descriptors.hpp b/host/class/cdc/usb_host_cdc_acm/host_test/descriptors/cdc_descriptors.hpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/descriptors/cdc_descriptors.hpp rename to host/class/cdc/usb_host_cdc_acm/host_test/descriptors/cdc_descriptors.hpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/descriptors/cypress_rfa.hpp b/host/class/cdc/usb_host_cdc_acm/host_test/descriptors/cypress_rfa.hpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/descriptors/cypress_rfa.hpp rename to host/class/cdc/usb_host_cdc_acm/host_test/descriptors/cypress_rfa.hpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/descriptors/stm32_device.hpp b/host/class/cdc/usb_host_cdc_acm/host_test/descriptors/stm32_device.hpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/descriptors/stm32_device.hpp rename to host/class/cdc/usb_host_cdc_acm/host_test/descriptors/stm32_device.hpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/CMakeLists.txt b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/CMakeLists.txt new file mode 100644 index 00000000..b97e0a27 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/CMakeLists.txt @@ -0,0 +1,12 @@ +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/" We are using freertos as real component + ) + +add_definitions("-DCMOCK_MEM_DYNAMIC") +project(host_test_usb_cdc) diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/README.md b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/README.md new file mode 100644 index 00000000..1e0ecc65 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/README.md @@ -0,0 +1,31 @@ +| Supported Targets | Linux | +| ----------------- | ----- | + +# Description + +This directory contains test code for `USB Host CDC-ACM` driver. Namely: +* Interactions with Mocked device added to the CDC-ACM driver (Device open, send mocked transfers, device close) + +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. + +This test directory uses freertos as real component +# 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_cdc.elf +``` + +The test executable have some options provided by the test framework. diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/CMakeLists.txt b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/CMakeLists.txt new file mode 100644 index 00000000..366aff1f --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/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) # We don't mock FreeRTOS for this test diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/common_test_fixtures.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/common_test_fixtures.cpp new file mode 100644 index 00000000..a62bc665 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/common_test_fixtures.cpp @@ -0,0 +1,311 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "usb/cdc_acm_host.h" +#include "mock_add_usb_device.h" +#include "common_test_fixtures.hpp" +#include "cdc_host_descriptor_parsing.h" + +extern "C" { +#include "Mockusb_host.h" +} + +/** + * @brief CMock expectations for current CDC device + * + * Only used for mocked usb host expectations + * + * For example, based on the descriptors, we record that a certain device has CTRL EP, IN EP, OUT EP and NO notif EP + * Then, during the device opening we expect usb_host_transfer_alloc() exactly 3 times (CTRL, IN, OUT) + * Also, during the device closing we expect usb_host_transfer_free() exactly 3 times (CTRL, IN, OUT) + * Different device could have CTRL, IN, OUT and NOTIF, thus we must expect those mocked functions exactly 4 times + */ +typedef struct { + struct { + usb_transfer_t *out_xfer; + usb_transfer_t *in_xfer; + uint8_t in_bEndpointAddress; + uint8_t out_bEndpointAddress; + } data; + struct { + usb_transfer_t *xfer; + uint8_t bEndpointAddress; + } notif; +} cdc_dev_expects_t; + +static cdc_dev_expects_t *p_cdc_dev_expects = nullptr; + +/** + * @brief Create CMock expectations for current device + * + * This function creates CMock expectations for a USB device, + * + * @param[in] dev_address Device address + * @param[in] interface_index Interface index to be used + * @param[in] dev_config CDC-ACM Host device config struct + * + * @return + * - ESP_OK: Mock expectations created successfully + * - ESP_ERR_NO_MEM: Not enough memory + */ +static esp_err_t _test_create_cmock_expectations(uint8_t dev_address, uint8_t interface_index, const cdc_acm_host_device_config_t *dev_config) +{ + cdc_dev_expects_t *cdc_dev_expects = (cdc_dev_expects_t *)calloc(1, sizeof(cdc_dev_expects_t)); + if (cdc_dev_expects == 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; + 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_index, &cdc_info); + + // Get IN and OUT endpoints addresses + cdc_dev_expects->data.in_bEndpointAddress = cdc_info.in_ep->bEndpointAddress; + cdc_dev_expects->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) { + cdc_dev_expects->notif.bEndpointAddress = cdc_info.notif_ep->bEndpointAddress; + cdc_dev_expects->notif.xfer = reinterpret_cast(¬if_xfer); + } else { + cdc_dev_expects->notif.bEndpointAddress = 0; + cdc_dev_expects->notif.xfer = nullptr; + } + + // Check, if IN data transfer is allocated + if (dev_config->in_buffer_size) { + cdc_dev_expects->data.in_xfer = reinterpret_cast(&data_in_xfer); + } else { + cdc_dev_expects->data.in_xfer = nullptr; + } + + // Check if OUT data transfer is allocated + if (dev_config->out_buffer_size) { + cdc_dev_expects->data.out_xfer = reinterpret_cast(&data_out_xfer); + } else { + cdc_dev_expects->data.out_xfer = nullptr; + } + + p_cdc_dev_expects = cdc_dev_expects; + return ESP_OK; +} + +/** + * @brief free space allocated for p_cdc_dev_expects + */ +static void _test_delete_cmock_expectations(void) +{ + free(p_cdc_dev_expects); + p_cdc_dev_expects = nullptr; +} + +esp_err_t test_cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config) +{ + usb_host_client_register_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_client_register_AddCallback(usb_host_client_register_mock_callback); + + usb_host_client_handle_events_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_client_handle_events_AddCallback(usb_host_client_handle_events_mock_callback); + + // Call the real function cdc_acm_host_install() + return cdc_acm_host_install(driver_config); +} + +esp_err_t test_cdc_acm_host_uninstall(void) +{ + usb_host_client_unblock_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_client_unblock_AddCallback(usb_host_client_unblock_mock_callback); + + usb_host_client_deregister_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_client_deregister_AddCallback(usb_host_client_deregister_mock_callback); + + _test_delete_cmock_expectations(); + + // Call the real function, cdc_acm_host_uninstall() + return cdc_acm_host_uninstall(); +} + +esp_err_t test_cdc_acm_host_open(uint8_t dev_address, uint16_t vid, uint16_t pid, uint8_t interface_index, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret) +{ + esp_err_t ret = _test_create_cmock_expectations(dev_address, interface_index, dev_config); + if (ret != ESP_OK) { + return ret; // ESP_ERR_NO_MEM + } + + // Device opening + usb_host_device_addr_list_fill_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_addr_list_fill_AddCallback(usb_host_device_addr_list_fill_mock_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_mock_callback); + usb_host_get_device_descriptor_AddCallback(usb_host_get_device_descriptor_mock_callback); + usb_host_device_close_AddCallback(usb_host_device_close_mock_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_mock_callback); + + // Setup control transfer + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + + // Setup Notif transfer + if (p_cdc_dev_expects->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); + } + + // Register callback + usb_host_transfer_alloc_AddCallback(usb_host_transfer_alloc_mock_callback); + + // Call cdc_acm_start + + // Claim data interface + // Make sure that the interface_index has been claimed + test_usb_host_interface_claim(interface_index); + + // Claim notification interface (if supported) + if (p_cdc_dev_expects->notif.xfer) { + test_usb_host_interface_claim(interface_index); + } + + // Call the real function cdc_acm_host_open + // Expect ESP_OK and dev_hdl non nullptr + ret = cdc_acm_host_open(vid, pid, interface_index, dev_config, cdc_hdl_ret); + + // If the cdc_acm_host_open() fails, delete the created cdc_device + if (ret != ESP_OK) { + _test_delete_cmock_expectations(); + } + return ret; +} + +esp_err_t test_cdc_acm_host_close(cdc_acm_dev_hdl_t *cdc_hdl, uint8_t interface_index) +{ + // Cancel pooling of IN endpoint -> halt, flush, clear + test_cdc_acm_reset_transfer_endpoint(p_cdc_dev_expects->data.in_bEndpointAddress); + + // Cancel pooling of Notification endpoint -> halt, flush, clear + if (p_cdc_dev_expects->notif.xfer) { + test_cdc_acm_reset_transfer_endpoint(p_cdc_dev_expects->notif.bEndpointAddress); + } + + // Release data interface + usb_host_interface_release_ExpectAndReturn(nullptr, nullptr, interface_index, ESP_OK); + usb_host_interface_release_IgnoreArg_client_hdl(); // Ignore all function parameters, except interface_index + usb_host_interface_release_IgnoreArg_dev_hdl(); + + + // Free notif transfer + if (p_cdc_dev_expects->notif.xfer) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + p_cdc_dev_expects->notif.xfer = nullptr; + } + + // Free in transfer + if (p_cdc_dev_expects->data.in_xfer) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + p_cdc_dev_expects->data.in_xfer = nullptr; + } + + // Free out transfer + if (p_cdc_dev_expects->data.out_xfer) { + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + p_cdc_dev_expects->data.out_xfer = nullptr; + } + + // Call cdc_acm_device_remove + // Free ctrl transfer + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_transfer_free_AddCallback(usb_host_transfer_free_mock_callback); + + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + + // Call the real function cdc_acm_host_close + return cdc_acm_host_close(*cdc_hdl); +} + +esp_err_t test_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) +{ + 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_mock_callback); + break; + } + case MOCK_USB_TRANSFER_ERROR: { + // Make the submitted transfer to fail + usb_host_transfer_submit_AddCallback(usb_host_transfer_submit_invalid_response_mock_callback); + break; + } + case MOCK_USB_TRANSFER_TIMEOUT: { + // Make the submitted transfer to be timed out + usb_host_transfer_submit_AddCallback(usb_host_transfer_submit_timeout_mock_callback); + // Reset out endpoint + test_cdc_acm_reset_transfer_endpoint(p_cdc_dev_expects->data.out_bEndpointAddress); + break; + } + default: + break; + } + + // Call the real function cdc_acm_host_data_tx_blocking() + return cdc_acm_host_data_tx_blocking(cdc_hdl, data, data_len, timeout_ms); +} + +esp_err_t test_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(); + + return ESP_OK; +} + +esp_err_t test_usb_host_interface_claim(uint8_t interface_index) +{ + usb_host_interface_claim_ExpectAndReturn(nullptr, nullptr, interface_index, 0, ESP_OK); + usb_host_interface_claim_IgnoreArg_client_hdl(); // Ignore all function parameters, except interface_index + usb_host_interface_claim_IgnoreArg_dev_hdl(); + usb_host_interface_claim_IgnoreArg_bAlternateSetting(); + usb_host_transfer_submit_ExpectAnyArgsAndReturn(ESP_OK); + + return ESP_OK; +} diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/common_test_fixtures.hpp b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/common_test_fixtures.hpp new file mode 100644 index 00000000..3fbf8859 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/common_test_fixtures.hpp @@ -0,0 +1,136 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" + +#pragma once + +/** + * @brief Intended transfer response + * + * It can be defined how the transfer is submitted + * We can submit a transfer successfully, 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 function, 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 test_cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config); + +/** + * @brief Host test fixture function, 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 test_cdc_acm_host_uninstall(void); + +/** + * @brief Host test fixture function, 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_index 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 test_cdc_acm_host_open(uint8_t device_addr, uint16_t vid, uint16_t pid, uint8_t interface_index, const cdc_acm_host_device_config_t *dev_config, cdc_acm_dev_hdl_t *cdc_hdl_ret); + +/** + * @brief Host test fixture function, 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_index 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 test_cdc_acm_host_close(cdc_acm_dev_hdl_t *cdc_hdl, uint8_t interface_index); + +/** + * @brief Host test fixture function, 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 test_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); + +/** + * @brief Host test fixture function, reset endpoint halt -> flush -> clear + * + * Call a set of mocked functions to simplify endpoint reset + * + * @param ep_address[in] Endpoint address to be reset + * + * @return: + * - ESP_OK: endpoint reset successful + */ +esp_err_t test_cdc_acm_reset_transfer_endpoint(uint8_t ep_address); + +/** + * @brief Host test fixture function, claim interface + * + * Call a set of mocked functions to simplify interface claim + * + * @param interface_index[in] Interface index + * + * @return: + * - ESP_OK: Interface claim successful + */ +esp_err_t test_usb_host_interface_claim(uint8_t interface_index); diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/idf_component.yml b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/idf_component.yml similarity index 72% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/idf_component.yml rename to host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/idf_component.yml index 528c8f3d..d72ab1a2 100644 --- a/host/class/cdc/usb_host_cdc_acm/host_test/main/idf_component.yml +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/idf_component.yml @@ -2,4 +2,4 @@ dependencies: espressif/catch2: "^3.4.0" usb_host_cdc_acm: version: "*" - override_path: "../../" + override_path: "../../../" diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_device_interaction.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_device_interaction.cpp new file mode 100644 index 00000000..0f467369 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_device_interaction.cpp @@ -0,0 +1,177 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "descriptors/cdc_descriptors.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" +} + +/** + * @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(0, (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(1, (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(2, (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(3, (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(4, (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 == test_cdc_acm_host_data_tx_blocking(*dev, tx_buf, sizeof(tx_buf), 200, MOCK_USB_TRANSFER_SUCCESS)); + // Submit transfer which will fail to submit + REQUIRE(ESP_ERR_INVALID_RESPONSE == test_cdc_acm_host_data_tx_blocking(*dev, tx_buf, sizeof(tx_buf), 200, MOCK_USB_TRANSFER_ERROR)); + // Submit transfer which times out + REQUIRE(ESP_ERR_TIMEOUT == test_cdc_acm_host_data_tx_blocking(*dev, tx_buf, sizeof(tx_buf), 200, MOCK_USB_TRANSFER_TIMEOUT)); +} + +SCENARIO("Interact with mocked USB devices") +{ + // We put the 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 + //Susb_host_mock_print_mocked_devices(0xFF); + } + + GIVEN("Mocked devices are added to the device list") { + // Install CDC-ACM driver + + REQUIRE(ESP_OK == test_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, + }; + + 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 = 0, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == test_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 == test_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 = 1, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == test_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 == test_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 = 2, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == test_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 == test_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 = 3, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == test_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 == test_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 = 4, interface_index = 0; + + // Open a device + REQUIRE(ESP_OK == test_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 == test_cdc_acm_host_close(&dev, interface_index)); + } + + // Uninstall CDC-ACM driver + REQUIRE(ESP_OK == test_cdc_acm_host_uninstall()); + } +} diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_main.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_main.cpp new file mode 100644 index 00000000..c6130ffa --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_main.cpp @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + + +extern "C" void app_main(void) +{ + int argc = 1; + const char *argv[2] = { + "target_test_main", + NULL + }; + + auto result = Catch::Session().run(argc, argv); + if (result != 0) { + printf("Test failed with result %d\n", result); + } else { + printf("Test passed.\n"); + } + fflush(stdout); + exit(result); +} diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_opening_device.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_opening_device.cpp new file mode 100644 index 00000000..c911596f --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/main/test_opening_device.cpp @@ -0,0 +1,194 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "descriptors/cdc_descriptors.hpp" +#include "usb/cdc_acm_host.h" +#include "mock_add_usb_device.h" +#include "common_test_fixtures.hpp" + +extern "C" { +#include "Mockusb_host.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)); +} + + +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(0xFF); + } + + SECTION("Fail to open CDC-ACM Device: CDC-ACM Host is not installed") { + REQUIRE(ESP_ERR_INVALID_STATE == cdc_acm_host_open(0, 0, 0, nullptr, nullptr)); + } + + GIVEN("Mocked device is added to the device list") { + // Install CDC-ACM driver + REQUIRE(ESP_OK == test_cdc_acm_host_install(nullptr)); + + cdc_acm_dev_hdl_t dev = nullptr; + 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, + }; + + // Define input parameters for cdc_acm_host_open + // VID, PID, interface index and IN EP of the added cp120x device + const uint16_t vid = 0x10C4, pid = 0xEA60; + const uint8_t interface_index = 0; + const uint8_t in_ep = 0x82; + + SECTION("Fail to open CDC-ACM Device: dev_config is nullptr") { + REQUIRE(ESP_ERR_INVALID_ARG == cdc_acm_host_open(vid, pid, interface_index, nullptr, &dev)); + } + + SECTION("Fail to open CDC-ACM Device: No devices found") { + dev_config.connection_timeout_ms = 1; // Set small connection timeout, so the usb_host_device_addr_list_fill() is called only once + int num_of_devices = 0; + usb_host_device_addr_list_fill_ExpectAnyArgsAndReturn(ESP_OK); + // Don't register callback to usb_host_device_addr_list_fill(), we want to use mocked function here + usb_host_device_addr_list_fill_ReturnThruPtr_num_dev_ret(&num_of_devices); + REQUIRE(ESP_ERR_NOT_FOUND == cdc_acm_host_open(vid, pid, interface_index, &dev_config, &dev)); + } + + AND_GIVEN("Fail to open CDC-ACM Device: No device with specified PID, VID, interface found") { + dev_config.connection_timeout_ms = 1; // Set small connection timeout, so the usb_host_device_addr_list_fill() is called only once + + // Fill address list with callback function + usb_host_device_addr_list_fill_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_addr_list_fill_AddCallback(usb_host_device_addr_list_fill_mock_callback); + + // Open device with callback function + usb_host_device_open_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_open_AddCallback(usb_host_device_open_mock_callback); + + usb_host_get_device_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_device_descriptor_AddCallback(usb_host_get_device_descriptor_mock_callback); + + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_close_AddCallback(usb_host_device_close_mock_callback); + + SECTION("Incorrect VID") { + // USB device present, but incorrect VID is given + const uint16_t incorrect_vid = 0x1234; + REQUIRE(ESP_ERR_NOT_FOUND == cdc_acm_host_open(incorrect_vid, pid, interface_index, &dev_config, &dev)); + } + + SECTION("Incorrect PID") { + // USB device present, but incorrect PID is given + const uint16_t incorrect_pid = 0x1234; + REQUIRE(ESP_ERR_NOT_FOUND == cdc_acm_host_open(vid, incorrect_pid, interface_index, &dev_config, &dev)); + } + + SECTION("Incorrect Interface parsed") { + // USB device present, but incorrect interface is given + const uint8_t incorrect_interface_index = 2; + 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_mock_callback); + + REQUIRE(ESP_ERR_NOT_FOUND == cdc_acm_host_open(vid, pid, incorrect_interface_index, &dev_config, &dev)); + } + } + + SECTION("Successfully open and close the device") { + // We must open and close a device in one section, otherwise the test_cdc_acm_host_uninstall() would fail + + // Fill address list with callback function + usb_host_device_addr_list_fill_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_addr_list_fill_AddCallback(usb_host_device_addr_list_fill_mock_callback); + + // Open device with callback function + usb_host_device_open_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_open_AddCallback(usb_host_device_open_mock_callback); + + usb_host_get_device_descriptor_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_get_device_descriptor_AddCallback(usb_host_get_device_descriptor_mock_callback); + + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_device_close_AddCallback(usb_host_device_close_mock_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_mock_callback); + + // Setup control transfer + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + // Setup IN data transfer + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + // Setup OUT bulk transfer + usb_host_transfer_alloc_ExpectAnyArgsAndReturn(ESP_OK); + // Register callback + usb_host_transfer_alloc_AddCallback(usb_host_transfer_alloc_mock_callback); + + // Call cdc_acm_start + + // Claim data interface, make sure that the interface_idx has been claimed + test_usb_host_interface_claim(interface_index); + + // Call the real function cdc_acm_host_open + // Expect ESP_OK and dev_hdl non nullptr + REQUIRE(ESP_OK == cdc_acm_host_open(vid, pid, interface_index, &dev_config, &dev)); + REQUIRE(nullptr != dev); + + // Device is opened, now close it + + // Cancel pooling of IN endpoint -> halt, flush, clear + test_cdc_acm_reset_transfer_endpoint(in_ep); + + // Release data interface + usb_host_interface_release_ExpectAndReturn(nullptr, nullptr, interface_index, ESP_OK); + usb_host_interface_release_IgnoreArg_client_hdl(); // Ignore all function parameters, except interface_idx + usb_host_interface_release_IgnoreArg_dev_hdl(); + + // Free in transfer + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + // Free out transfer + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + + // Call cdc_acm_device_remove, free ctrl transfer + usb_host_transfer_free_ExpectAnyArgsAndReturn(ESP_OK); + usb_host_transfer_free_AddCallback(usb_host_transfer_free_mock_callback); + + usb_host_device_close_ExpectAnyArgsAndReturn(ESP_OK); + + // Call the real function cdc_acm_host_close + REQUIRE(ESP_OK == cdc_acm_host_close(dev)); + } + + // Uninstall CDC-ACM driver + REQUIRE(ESP_OK == test_cdc_acm_host_uninstall()); + } +} diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/sdkconfig.defaults b/host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/sdkconfig.defaults similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/sdkconfig.defaults rename to host/class/cdc/usb_host_cdc_acm/host_test/device_interaction/sdkconfig.defaults diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/CMakeLists.txt b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/CMakeLists.txt similarity index 86% rename from host/class/cdc/usb_host_cdc_acm/host_test/CMakeLists.txt rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/CMakeLists.txt index 460a5156..ffb1e583 100644 --- a/host/class/cdc/usb_host_cdc_acm/host_test/CMakeLists.txt +++ b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/CMakeLists.txt @@ -8,4 +8,5 @@ list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/mocks/freertos/" ) +add_definitions("-DCMOCK_MEM_DYNAMIC") project(host_test_usb_cdc) diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/README.md b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/README.md similarity index 93% rename from host/class/cdc/usb_host_cdc_acm/host_test/README.md rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/README.md index be5808f2..0a770f35 100644 --- a/host/class/cdc/usb_host_cdc_acm/host_test/README.md +++ b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/README.md @@ -9,6 +9,7 @@ This directory contains test code for `USB Host CDC-ACM` driver. Namely: 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. +This test directory uses freertos as mocked component # Build Tests build regularly like an idf project. Currently only working on Linux machines. diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/CMakeLists.txt b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/CMakeLists.txt similarity index 73% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/CMakeLists.txt rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/CMakeLists.txt index 6fa86b66..e2a7f0f4 100644 --- a/host/class/cdc/usb_host_cdc_acm/host_test/main/CMakeLists.txt +++ b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register(SRC_DIRS . REQUIRES cmock usb - INCLUDE_DIRS . - PRIV_INCLUDE_DIRS "../../private_include" + INCLUDE_DIRS "../../" . + PRIV_INCLUDE_DIRS "../../../private_include" WHOLE_ARCHIVE) # Currently 'main' for IDF_TARGET=linux is defined in freertos component. diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/idf_component.yml b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/idf_component.yml new file mode 100644 index 00000000..d72ab1a2 --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/idf_component.yml @@ -0,0 +1,5 @@ +dependencies: + espressif/catch2: "^3.4.0" + usb_host_cdc_acm: + version: "*" + override_path: "../../../" diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_checker.hpp b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_checker.hpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_checker.hpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_checker.hpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_cypress.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_cypress.cpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_cypress.cpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_cypress.cpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_ethernet_converters.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_ethernet_converters.cpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_ethernet_converters.cpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_ethernet_converters.cpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_modems.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_modems.cpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_modems.cpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_modems.cpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_stm32.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_stm32.cpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_stm32.cpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_stm32.cpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_tinyusb_devices.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_tinyusb_devices.cpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_tinyusb_devices.cpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_tinyusb_devices.cpp diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_uart_converters.cpp b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_uart_converters.cpp similarity index 100% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_parsing_uart_converters.cpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_parsing_uart_converters.cpp 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/parsing_tests/main/test_unit_public_api.cpp similarity index 53% rename from host/class/cdc/usb_host_cdc_acm/host_test/main/test_unit_public_api.cpp rename to host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/main/test_unit_public_api.cpp index 535c5ad3..17fa1c46 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/parsing_tests/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()); + } + } } diff --git a/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/sdkconfig.defaults b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/sdkconfig.defaults new file mode 100644 index 00000000..14bc2aef --- /dev/null +++ b/host/class/cdc/usb_host_cdc_acm/host_test/parsing_tests/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