Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cmock): Enable linux target build for HID, CDC-ACM and UVC hosts #65

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ host/class:
enable:
- if: SOC_USB_OTG_SUPPORTED == 1

# Host tests
host/class/cdc/usb_host_cdc_acm/host_test:
enable:
- if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4)

host/class/hid/usb_host_hid/host_test:
enable:
- if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4)

host/class/uvc/usb_host_uvc/host_test:
enable:
- if: IDF_TARGET in ["linux"] and (IDF_VERSION_MAJOR >= 5 and IDF_VERSION_MINOR >= 4)
1 change: 1 addition & 0 deletions host/class/cdc/usb_host_cdc_acm/host_test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

This directory contains test code for `USB Host CDC-ACM` driver. Namely:
* Descriptor parsing
* Simple public API call with mocked USB component to test Linux build and Cmock run for this class driver

Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework, use CMock, so you must install Ruby on your machine to run them.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <catch2/catch_test_macros.hpp>

#include "usb/cdc_acm_host.h"

extern "C" {
#include "Mockusb_host.h"
#include "Mockqueue.h"
#include "Mocktask.h"
#include "Mockidf_additions.h"
#include "Mockportmacro.h"
#include "Mockevent_groups.h"
}

SCENARIO("CDC-ACM Host install")
{
// CDC-ACM Host driver config set to nullptr
GIVEN("NO CDC-ACM Host driver config, driver not installed") {
TaskHandle_t task_handle;
int sem;
int event_group;

// Call cdc_acm_host_install with cdc_acm_host_driver_config set to nullptr, fail to create EventGroup
SECTION("Fail to create EventGroup") {
// Create an EventGroup, return nullptr, so the EventGroup is not created successfully
xEventGroupCreate_ExpectAndReturn(nullptr);
// We should be calling xSemaphoreCreteMutex_ExpectAnyArgsAndRetrun instead of xQueueCreateMutex_ExpectAnyArgsAndReturn
// Because of missing Freertos Mocks
// Create a semaphore, return the semaphore handle (Queue Handle in this scenario), so the semaphore is created successfully
xQueueCreateMutex_ExpectAnyArgsAndReturn(reinterpret_cast<QueueHandle_t>(&sem));
// Create a task, return pdTRUE, so the task is created successfully
xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE);
// Return task handle by pointer
xTaskCreatePinnedToCore_ReturnThruPtr_pxCreatedTask(&task_handle);

// goto err: (xEventGroupCreate returned nullptr), delete the queue and the task
vQueueDelete_Expect(reinterpret_cast<QueueHandle_t>(&sem));
vTaskDelete_Expect(task_handle);

// Call the DUT function, expect ESP_ERR_NO_MEM
REQUIRE(ESP_ERR_NO_MEM == cdc_acm_host_install(nullptr));
}

// Call cdc_acm_host_install, expect to successfully install the CDC ACM host
SECTION("Successfully install CDC ACM Host") {
// Create an EventGroup, return event group handle, so the EventGroup is created successfully
xEventGroupCreate_ExpectAndReturn(reinterpret_cast<EventGroupHandle_t>(&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<QueueHandle_t>(&sem));
// Create a task, return pdTRUE, so the task is created successfully
vPortEnterCritical_Expect();
vPortExitCritical_Expect();
xTaskCreatePinnedToCore_ExpectAnyArgsAndReturn(pdTRUE);
// Return task handle by pointer
xTaskCreatePinnedToCore_ReturnThruPtr_pxCreatedTask(&task_handle);

// Call mocked function from USB Host
// return ESP_OK, so the client si registered successfully
usb_host_client_register_ExpectAnyArgsAndReturn(ESP_OK);

// Resume the task
vTaskResume_Expect(task_handle);

// Call the DUT Function, expect ESP_OK
REQUIRE(ESP_OK == cdc_acm_host_install(nullptr));
}
}
}
44 changes: 22 additions & 22 deletions host/class/hid/usb_host_hid/hid_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ typedef struct hid_class_request {
static void event_handler_task(void *arg)
{
ESP_LOGD(TAG, "USB HID handling start");
while (hid_host_handle_events(portMAX_DELAY) == ESP_OK) {
while (hid_host_handle_events((uint32_t)portMAX_DELAY) == ESP_OK) {
}
ESP_LOGD(TAG, "USB HID handling stop");
vTaskDelete(NULL);
Expand Down Expand Up @@ -293,40 +293,40 @@ static bool hid_interface_present(const usb_config_desc_t *config_desc)
/**
* @brief HID Interface user callback function.
*
* @param[in] hid_iface Pointer to an Interface structure
* @param[in] event_id HID Interface event
* @param[in] iface Pointer to an Interface structure
* @param[in] event HID Interface event
*/
static inline void hid_host_user_interface_callback(hid_iface_t *hid_iface,
static inline void hid_host_user_interface_callback(hid_iface_t *iface,
const hid_host_interface_event_t event)
{
assert(hid_iface);
assert(iface);

hid_host_dev_params_t *dev_params = &hid_iface->dev_params;
hid_host_dev_params_t *dev_params = &iface->dev_params;

assert(dev_params);

if (hid_iface->user_cb) {
hid_iface->user_cb(hid_iface, event, hid_iface->user_cb_arg);
if (iface->user_cb) {
iface->user_cb(iface, event, iface->user_cb_arg);
}
}

/**
* @brief HID Device user callback function.
*
* @param[in] event_id HID Device event
* @param[in] dev_params HID Device parameters
* @param[in] iface Pointer to an Interface structure
* @param[in] event HID Device event
*/
static inline void hid_host_user_device_callback(hid_iface_t *hid_iface,
static inline void hid_host_user_device_callback(hid_iface_t *iface,
const hid_host_driver_event_t event)
{
assert(hid_iface);
assert(iface);

hid_host_dev_params_t *dev_params = &hid_iface->dev_params;
hid_host_dev_params_t *dev_params = &iface->dev_params;

assert(dev_params);

if (s_hid_driver && s_hid_driver->user_cb) {
s_hid_driver->user_cb(hid_iface, event, s_hid_driver->user_arg);
s_hid_driver->user_cb(iface, event, s_hid_driver->user_arg);
}
}

Expand Down Expand Up @@ -393,14 +393,14 @@ static esp_err_t hid_host_add_interface(hid_device_t *hid_device,
*
* Use only inside critical section
*
* @param[in] hid_iface HID interface handle
* @param[in] iface HID interface handle
* @return esp_err_t
*/
static esp_err_t _hid_host_remove_interface(hid_iface_t *hid_iface)
static esp_err_t _hid_host_remove_interface(hid_iface_t *iface)
{
hid_iface->state = HID_INTERFACE_STATE_NOT_INITIALIZED;
STAILQ_REMOVE(&s_hid_driver->hid_ifaces_tailq, hid_iface, hid_interface, tailq_entry);
free(hid_iface);
iface->state = HID_INTERFACE_STATE_NOT_INITIALIZED;
STAILQ_REMOVE(&s_hid_driver->hid_ifaces_tailq, iface, hid_interface, tailq_entry);
free(iface);
return ESP_OK;
}

Expand Down Expand Up @@ -765,7 +765,7 @@ static esp_err_t hid_control_transfer(hid_device_t *hid_device,
/**
* @brief USB class standard request get descriptor
*
* @param[in] hidh_device Pointer to HID device structure
* @param[in] hid_device Pointer to HID device structure
* @param[in] req Pointer to a class specific request structure
* @return esp_err_t
*/
Expand All @@ -788,7 +788,7 @@ static esp_err_t usb_class_request_get_descriptor(hid_device_t *hid_device, cons
ESP_ERROR_CHECK(usb_host_device_info(hid_device->dev_hdl, &dev_info));
// reallocate the ctrl xfer buffer for new length
ESP_LOGD(TAG, "Change HID ctrl xfer size from %d to %d",
ctrl_size,
(int) ctrl_size,
(int) (USB_SETUP_PACKET_SIZE + req->wLength));

usb_host_transfer_free(hid_device->ctrl_xfer);
Expand Down Expand Up @@ -829,7 +829,7 @@ static esp_err_t usb_class_request_get_descriptor(hid_device_t *hid_device, cons
/**
* @brief HID Host Request Report Descriptor
*
* @param[in] hidh_iface Pointer to HID Interface configuration structure
* @param[in] iface Pointer to HID Interface configuration structure
* @return esp_err_t
*/
static esp_err_t hid_class_request_report_descriptor(hid_iface_t *iface)
Expand Down
11 changes: 11 additions & 0 deletions host/class/hid/usb_host_hid/host_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(COMPONENTS main)

list(APPEND EXTRA_COMPONENT_DIRS
"$ENV{IDF_PATH}/tools/mocks/usb/"
"$ENV{IDF_PATH}/tools/mocks/freertos/"
)

project(host_test_usb_hid)
30 changes: 30 additions & 0 deletions host/class/hid/usb_host_hid/host_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
| Supported Targets | Linux |
| ----------------- | ----- |

# Description

This directory contains test code for `USB Host HID` driver. Namely:
* Simple public API call with mocked USB component to test Linux build and Cmock run for this class driver

Tests are written using [Catch2](https://github.com/catchorg/Catch2) test framework, use CMock, so you must install Ruby on your machine to run them.

# Build

Tests build regularly like an idf project. Currently only working on Linux machines.

```
idf.py --preview set-target linux
idf.py build
```

# Run

The build produces an executable in the build folder.

Just run:

```
./build/host_test_usb_hid.elf
```

The test executable have some options provided by the test framework.
8 changes: 8 additions & 0 deletions host/class/hid/usb_host_hid/host_test/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
idf_component_register(SRC_DIRS .
REQUIRES cmock usb
INCLUDE_DIRS .
WHOLE_ARCHIVE)

# Currently 'main' for IDF_TARGET=linux is defined in freertos component.
# Since we are using a freertos mock here, need to let Catch2 provide 'main'.
target_link_libraries(${COMPONENT_LIB} PRIVATE Catch2WithMain)
5 changes: 5 additions & 0 deletions host/class/hid/usb_host_hid/host_test/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dependencies:
espressif/catch2: "^3.4.0"
usb_host_hid:
version: "*"
override_path: "../../"
Loading
Loading