Skip to content

Commit

Permalink
feature(teardown_test): Added the test_app for teardowning the cdc de…
Browse files Browse the repository at this point in the history
…vice
  • Loading branch information
roma-jam committed Dec 13, 2024
1 parent 01168bf commit 38fd542
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 0 deletions.
9 changes: 9 additions & 0 deletions device/esp_tinyusb/test_apps/teardown_device/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)

project(test_app_teardown_device)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
REQUIRES unity
WHOLE_ARCHIVE)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb:
version: "*"
override_path: "../../../"
62 changes: 62 additions & 0 deletions device/esp_tinyusb/test_apps/teardown_device/main/test_app_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdio.h>
#include <string.h>
#include "unity.h"
#include "unity_test_runner.h"
#include "unity_test_utils_memory.h"

void app_main(void)
{
/*
_ _ _
| | (_) | |
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
| |______ __/ |
|_|______| |___/
_____ _____ _____ _____
|_ _| ___/ ___|_ _|
| | | |__ \ `--. | |
| | | __| `--. \ | |
| | | |___/\__/ / | |
\_/ \____/\____/ \_/
*/

printf(" _ _ _ \n");
printf(" | | (_) | | \n");
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
printf(" | |______ __/ | \n");
printf(" |_|______| |___/ \n");
printf(" _____ _____ _____ _____ \n");
printf("|_ _| ___/ ___|_ _| \n");
printf(" | | | |__ \\ `--. | | \n");
printf(" | | | __| `--. \\ | | \n");
printf(" | | | |___/\\__/ / | | \n");
printf(" \\_/ \\____/\\____/ \\_/ \n");

unity_utils_setup_heap_record(80);
unity_utils_set_leak_level(128);
unity_run_menu();
}

/* setUp runs before every test */
void setUp(void)
{
unity_utils_record_free_mem();
}

/* tearDown runs after every test */
void tearDown(void)
{
unity_utils_evaluate_leaks();
}
164 changes: 164 additions & 0 deletions device/esp_tinyusb/test_apps/teardown_device/main/test_teardown.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "soc/soc_caps.h"
// #if SOC_USB_OTG_SUPPORTED

//
#include <stdio.h>
#include <string.h>
//
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
//
#include "esp_system.h"
#include "esp_log.h"
#include "esp_err.h"
//
#include "unity.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"

static const char *TAG = "teardown_test";
static const char *TEARDOWN_CMD = "teardown";
// DElay between device connection, required for the Host to handle device disconnection/connection without errors
#define TEARDOWN_DELAY_MS 2000
#define TEARDOWN_AMOUNT 10

static const tusb_desc_device_t cdc_device_descriptor = {
.bLength = sizeof(cdc_device_descriptor),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = USB_ESPRESSIF_VID,
.idProduct = 0x4002,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};

static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN;
static const uint8_t cdc_desc_configuration[] = {
TUD_CONFIG_DESCRIPTOR(1, 4, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, (TUD_OPT_HIGH_SPEED ? 512 : 64)),
};

#if (TUD_OPT_HIGH_SPEED)
static const tusb_desc_device_qualifier_t device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0
};
#endif // TUD_OPT_HIGH_SPEED

static QueueHandle_t rx_queue;
static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];

typedef struct {
int itf; // Interface number
uint8_t data[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; // Data buffer
size_t len; // Number of bytes received
} rx_pkt_t;

static void test_rx_enqueue_pkt(int itf, uint8_t *buf, size_t size)
{
rx_pkt_t rx_pkt = {
.itf = itf,
.len = size,
};
memcpy(rx_pkt.data, buf, size);
xQueueSend(rx_queue, &rx_pkt, 0);
}

static void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event)
{
size_t rx_size = 0;
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_read(itf, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size));
test_rx_enqueue_pkt(itf, rx_buf, rx_size);
}

/**
* @brief TinyUSB Teardown specific testcase
*/
TEST_CASE("tinyusb_teardown", "[esp_tinyusb][teardown]")
{
rx_pkt_t rx_pkt;
// Create FreeRTOS primitives
rx_queue = xQueueCreate(5, sizeof(rx_pkt_t));
TEST_ASSERT(rx_queue);

// TinyUSB driver configuration
const tinyusb_config_t tusb_cfg = {
.device_descriptor = &cdc_device_descriptor,
.string_descriptor = NULL,
.string_descriptor_count = 0,
.external_phy = false,
#if (TUD_OPT_HIGH_SPEED)
.fs_configuration_descriptor = cdc_desc_configuration,
.hs_configuration_descriptor = cdc_desc_configuration,
.qualifier_descriptor = &device_qualifier,
#else
.configuration_descriptor = cdc_desc_configuration,
#endif // TUD_OPT_HIGH_SPEED
};

// TinyUSB ACM Driver configuration
tinyusb_config_cdcacm_t acm_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.cdc_port = TINYUSB_CDC_ACM_0,
.rx_unread_buf_sz = 64,
.callback_rx = &tinyusb_cdc_rx_callback,
.callback_rx_wanted_char = NULL,
.callback_line_state_changed = NULL,
.callback_line_coding_changed = NULL
};
int attempts = TEARDOWN_AMOUNT;
while (1) {
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DELAY_MS));
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
// Init CDC 0
TEST_ASSERT_FALSE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0));
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_init(&acm_cfg));
TEST_ASSERT_TRUE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0));

// Wait for the pytest poke
while (1) {
if (xQueueReceive(rx_queue, &rx_pkt, portMAX_DELAY)) {
if (rx_pkt.len) {
/* echoed back */
TEST_ASSERT_EQUAL(rx_pkt.len, tinyusb_cdcacm_write_queue(rx_pkt.itf, rx_pkt.data, rx_pkt.len));
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_write_flush(rx_pkt.itf, pdMS_TO_TICKS(100)));
if (strncmp(TEARDOWN_CMD, (const char *) rx_pkt.data, rx_pkt.len) == 0) {
// Wait for the host
attempts--;
break;
}
}
}
}
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DELAY_MS));
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_unregister_callback(TINYUSB_CDC_ACM_0, CDC_EVENT_RX));
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_deinit(TINYUSB_CDC_ACM_0));
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
}
// Remove primitives
vQueueDelete(rx_queue);
// All attempts should be completed
TEST_ASSERT_EQUAL(0, attempts);
}

// #endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0

import pytest
from pytest_embedded_idf.dut import IdfDut
import serial
import serial.tools.list_ports
import time
from time import sleep


@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.esp32p4
@pytest.mark.usb_device

def find_serial_port_by_vid_pid(vid, pid):
"""
Find a serial port by its VID and PID.
"""
ports = serial.tools.list_ports.comports()
for port in ports:
if port.vid == vid and port.pid == pid:
return port.device
return None

def wait_for_device(vid, pid, timeout=30):
"""
Wait for a device with the specified VID and PID to appear.
"""
start_time = time.time()
while time.time() - start_time < timeout:
serial_port = find_serial_port_by_vid_pid(vid, pid)
if serial_port:
return serial_port
time.sleep(0.5) # Check every 0.5 seconds
return None

def teardown():
TARGET_VID = 0x303A # Espressif TinyUSB VID
TARGET_PID = 0x4002 # Espressif TinyUSB VID

# Command to send and expected response
COMMAND = "teardown"
EXPECTED_RESPONSE = "teardown"

# Number of iterations, must be equal to ITERATIONS in the test application
ITERATIONS = 10

for i in range(ITERATIONS):
print(f"Iteration {i+1} of {ITERATIONS}")

# Wait for the device to appear
print("Waiting for the device to connect...")
serial_port = wait_for_device(TARGET_VID, TARGET_PID)
if not serial_port:
print("Error: Device did not appear within the timeout period.")
break

try:
# Open the serial port
with serial.Serial(port=serial_port, baudrate=9600, timeout=1) as set:
print(f"Opened port: {serial_port}")

# Send the 'teardown' command
set.write(COMMAND.encode('utf-8'))
print(f"Sent command: {COMMAND.strip()}")

# Wait for the response
response = set.readline().decode('utf-8').strip()
print(f"Received response: {response}")

# Check if the response matches the expected response
if response == EXPECTED_RESPONSE:
print("Response matches expected value.")
else:
print("Error: Response does not match expected value.")

except serial.SerialException as e:
print(f"Error communicating with the serial port: {e}")
break

# Wait for the device to disconnect
print("Waiting for the device to disconnect...")
while find_serial_port_by_vid_pid(TARGET_VID, TARGET_PID):
time.sleep(0.5) # Check every 0.5 seconds

print("Finished all iterations.")

def test_usb_teardown_device(dut) -> None:
dut.expect_exact('Press ENTER to see the list of tests.')
dut.write('[teardown]')
dut.expect_exact('TinyUSB: TinyUSB Driver installed')
sleep(2) # Some time for the OS to enumerate our USB device
teardown()

16 changes: 16 additions & 0 deletions device/esp_tinyusb/test_apps/teardown_device/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Configure TinyUSB, it will be used to mock USB devices
CONFIG_TINYUSB_CDC_ENABLED=y
CONFIG_TINYUSB_CDC_COUNT=1

# Disable watchdogs, they'd get triggered during unity interactive menu
CONFIG_ESP_INT_WDT=n
CONFIG_ESP_TASK_WDT=n

# Run-time checks of Heap and Stack
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
CONFIG_COMPILER_STACK_CHECK=y

CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y

CONFIG_COMPILER_CXX_EXCEPTIONS=y

0 comments on commit 38fd542

Please sign in to comment.