diff options
Diffstat (limited to 'test/unit-test')
| -rw-r--r-- | test/unit-test/CMakeLists.txt | 131 | ||||
| -rw-r--r-- | test/unit-test/project.yml | 3 | ||||
| -rw-r--r-- | test/unit-test/test/device/midi2/test_midi2_device.c | 266 | ||||
| -rw-r--r-- | test/unit-test/test/device/msc/test_msc_device.c | 10 | ||||
| -rw-r--r-- | test/unit-test/test/device/usbd/test_usbd.c | 68 | ||||
| -rw-r--r-- | test/unit-test/test/host/midi2/test_midi2_host.c | 101 | ||||
| -rw-r--r-- | test/unit-test/test/support/tusb_config.h | 6 | ||||
| -rw-r--r-- | test/unit-test/test/test_common_func.c | 110 | ||||
| -rw-r--r-- | test/unit-test/test/test_fifo.c | 497 |
9 files changed, 1051 insertions, 141 deletions
diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt new file mode 100644 index 000000000..a33af4563 --- /dev/null +++ b/test/unit-test/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.20) + +project(tinyusb_unit_tests LANGUAGES C) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS ON) + +# Command to invoke Ceedling. Supports multi-word commands such as "bundle exec ceedling". +set(CEEDLING_COMMAND "ceedling" CACHE STRING "Command used to invoke Ceedling (Ruby gem).") +separate_arguments(CEEDLING_COMMAND_LIST NATIVE_COMMAND "${CEEDLING_COMMAND}") +if (CEEDLING_COMMAND_LIST STREQUAL "") + message(FATAL_ERROR "CEEDLING_COMMAND is empty; set it to a valid Ceedling invocation.") +endif () + +list(GET CEEDLING_COMMAND_LIST 0 CEEDLING_LAUNCHER) +find_program(CEEDLING_LAUNCHER_PATH NAMES ${CEEDLING_LAUNCHER}) +if (NOT CEEDLING_LAUNCHER_PATH) + message(FATAL_ERROR "Could not find '${CEEDLING_LAUNCHER}' on PATH; adjust CEEDLING_COMMAND or PATH.") +endif () +list(REMOVE_AT CEEDLING_COMMAND_LIST 0) +list(INSERT CEEDLING_COMMAND_LIST 0 ${CEEDLING_LAUNCHER_PATH}) + +set(CEEDLING_WORKDIR ${CMAKE_CURRENT_LIST_DIR}) +set(CEEDLING_BUILD_DIR ${CEEDLING_WORKDIR}/_build) + +# Helper to add a Ceedling-backed test target that compiles into a real CMake executable. +function(add_ceedling_test TARGET_NAME TEST_SOURCE PRODUCT_SOURCES MOCK_SOURCES) + set(runner ${CEEDLING_BUILD_DIR}/test/runners/${TARGET_NAME}_runner.c) + + add_custom_target(ceedling_gen_${TARGET_NAME} + COMMAND ${CEEDLING_COMMAND_LIST} test:${TARGET_NAME} + WORKING_DIRECTORY ${CEEDLING_WORKDIR} + BYPRODUCTS ${runner} + USES_TERMINAL + COMMENT "Generate Ceedling runner/mocks for ${TARGET_NAME}" + ) + + add_executable(${TARGET_NAME} + ${TEST_SOURCE} + ${runner} + ${MOCK_SOURCES} + ${CEEDLING_BUILD_DIR}/vendor/unity/src/unity.c + ${CEEDLING_BUILD_DIR}/vendor/cmock/src/cmock.c + ${PRODUCT_SOURCES} + ) + + set_source_files_properties( + ${runner} + ${MOCK_SOURCES} + ${CEEDLING_BUILD_DIR}/vendor/unity/src/unity.c + ${CEEDLING_BUILD_DIR}/vendor/cmock/src/cmock.c + PROPERTIES GENERATED TRUE + ) + + add_dependencies(${TARGET_NAME} ceedling_gen_${TARGET_NAME}) + + target_include_directories(${TARGET_NAME} PRIVATE + ${CEEDLING_WORKDIR}/test + ${CEEDLING_WORKDIR}/test/support + ${CEEDLING_BUILD_DIR}/test/runners + ${CEEDLING_BUILD_DIR}/test/mocks/${TARGET_NAME} + ${CEEDLING_BUILD_DIR}/vendor/unity/src + ${CEEDLING_BUILD_DIR}/vendor/cmock/src + ${CEEDLING_WORKDIR}/../../src + ${CEEDLING_WORKDIR}/../../src/common + ${CEEDLING_WORKDIR}/../../src/device + ${CEEDLING_WORKDIR}/../../src/class + ${CEEDLING_WORKDIR}/../../src/class/msc + ${CEEDLING_WORKDIR}/../../src/host + ${CEEDLING_WORKDIR}/../../src/typec + ${CEEDLING_WORKDIR}/../../src/osal + ) + + target_compile_definitions(${TARGET_NAME} PRIVATE _UNITY_TEST_) + target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra) + add_test(NAME ${TARGET_NAME} COMMAND ${TARGET_NAME}) +endfunction() + +# Custom targets to keep plain Ceedling entry-points available. +add_custom_target(ceedling_all + COMMAND ${CEEDLING_COMMAND_LIST} test:all + WORKING_DIRECTORY ${CEEDLING_WORKDIR} + USES_TERMINAL + COMMENT "Run Ceedling (Unity) unit tests" + ) + +add_custom_target(ceedling_clean + COMMAND ${CEEDLING_COMMAND_LIST} clean + WORKING_DIRECTORY ${CEEDLING_WORKDIR} + USES_TERMINAL + COMMENT "Clean Ceedling build outputs" + ) + +add_custom_target(ceedling_clobber + COMMAND ${CEEDLING_COMMAND_LIST} clobber + WORKING_DIRECTORY ${CEEDLING_WORKDIR} + USES_TERMINAL + COMMENT "Clobber Ceedling build outputs" + ) + +# Per-test wiring: mocks are generated under _build/test/mocks/<name>/. +add_ceedling_test( + test_common_func + ${CEEDLING_WORKDIR}/test/test_common_func.c + "" + "" + ) + +add_ceedling_test( + test_fifo + ${CEEDLING_WORKDIR}/test/test_fifo.c + ${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c + "" + ) + +add_ceedling_test( + test_usbd + ${CEEDLING_WORKDIR}/test/device/usbd/test_usbd.c + "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" + "${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_dcd.c;${CEEDLING_BUILD_DIR}/test/mocks/test_usbd/mock_msc_device.c" + ) + +add_ceedling_test( + test_msc_device + ${CEEDLING_WORKDIR}/test/device/msc/test_msc_device.c + "${CEEDLING_WORKDIR}/../../src/tusb.c;${CEEDLING_WORKDIR}/../../src/device/usbd.c;${CEEDLING_WORKDIR}/../../src/class/msc/msc_device.c;${CEEDLING_WORKDIR}/../../src/common/tusb_fifo.c" + "${CEEDLING_BUILD_DIR}/test/mocks/test_msc_device/mock_dcd.c" + ) + +enable_testing() diff --git a/test/unit-test/project.yml b/test/unit-test/project.yml index 6c86b0205..be3fc3de0 100644 --- a/test/unit-test/project.yml +++ b/test/unit-test/project.yml @@ -128,6 +128,9 @@ :defines: :test: - _UNITY_TEST_ + - CFG_TUD_EDPT_DEDICATED_HWFIFO=1 + - CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE=6 + - CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE=0 :release: [] # Enable to inject name of a test as a unique compilation symbol into its respective executable build. diff --git a/test/unit-test/test/device/midi2/test_midi2_device.c b/test/unit-test/test/device/midi2/test_midi2_device.c new file mode 100644 index 000000000..1314c2585 --- /dev/null +++ b/test/unit-test/test/device/midi2/test_midi2_device.c @@ -0,0 +1,266 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Saulo Verissimo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "unity.h" +#include "tusb_types.h" +#include "class/audio/audio.h" +#include "class/midi/midi.h" +#include "device/usbd.h" + +void setUp(void) {} +void tearDown(void) {} + +//--------------------------------------------------------------------+ +// UMP Word Count: all 16 message types +//--------------------------------------------------------------------+ + +void test_ump_word_count_1word_types(void) { + uint8_t types[] = {0x0, 0x1, 0x2, 0x6, 0x7}; + for (int i = 0; i < 5; i++) { + TEST_ASSERT_EQUAL(1, midi2_ump_word_count(types[i])); + } +} + +void test_ump_word_count_2word_types(void) { + uint8_t types[] = {0x3, 0x4, 0x8, 0x9, 0xA}; + for (int i = 0; i < 5; i++) { + TEST_ASSERT_EQUAL(2, midi2_ump_word_count(types[i])); + } +} + +void test_ump_word_count_3word_types(void) { + TEST_ASSERT_EQUAL(3, midi2_ump_word_count(0xB)); + TEST_ASSERT_EQUAL(3, midi2_ump_word_count(0xC)); +} + +void test_ump_word_count_4word_types(void) { + uint8_t types[] = {0x5, 0xD, 0xE, 0xF}; + for (int i = 0; i < 4; i++) { + TEST_ASSERT_EQUAL(4, midi2_ump_word_count(types[i])); + } +} + +void test_ump_word_count_covers_all_16(void) { + for (uint8_t mt = 0; mt <= 0xF; mt++) { + uint8_t wc = midi2_ump_word_count(mt); + TEST_ASSERT_TRUE(wc >= 1 && wc <= 4); + } +} + +//--------------------------------------------------------------------+ +// CS Endpoint subtypes (defined in midi.h) +//--------------------------------------------------------------------+ + +void test_cs_endpoint_subtypes(void) { + TEST_ASSERT_EQUAL(0x01, MIDI_CS_ENDPOINT_GENERAL); + TEST_ASSERT_EQUAL(0x02, MIDI_CS_ENDPOINT_GENERAL_2_0); +} + +//--------------------------------------------------------------------+ +// Descriptor macro length calculations +//--------------------------------------------------------------------+ + +void test_midi1_desc_len(void) { + TEST_ASSERT_EQUAL(TUD_MIDI_DESC_HEAD_LEN + TUD_MIDI_DESC_JACK_LEN + TUD_MIDI_DESC_EP_LEN(1) * 2, + TUD_MIDI_DESC_LEN); +} + +void test_midi2_alt1_head_len(void) { + TEST_ASSERT_EQUAL(16, TUD_MIDI2_DESC_ALT1_HEAD_LEN); +} + +void test_midi2_alt1_ep_len(void) { + // EP(7) + CS base(4) + numgtbs + TEST_ASSERT_EQUAL(12, TUD_MIDI2_DESC_ALT1_EP_LEN(1)); + TEST_ASSERT_EQUAL(13, TUD_MIDI2_DESC_ALT1_EP_LEN(2)); + TEST_ASSERT_EQUAL(18, TUD_MIDI2_DESC_ALT1_EP_LEN(7)); +} + +void test_midi2_desc_len(void) { + int expected = TUD_MIDI_DESC_LEN + TUD_MIDI2_DESC_ALT1_HEAD_LEN + TUD_MIDI2_DESC_ALT1_EP_LEN(1) * 2; + TEST_ASSERT_EQUAL(expected, TUD_MIDI2_DESC_LEN); +} + +void test_midi2_desc_len_greater_than_midi1(void) { + TEST_ASSERT_TRUE(TUD_MIDI2_DESC_LEN > TUD_MIDI_DESC_LEN); +} + +//--------------------------------------------------------------------+ +// Descriptor macro byte validation +//--------------------------------------------------------------------+ + +void test_midi2_descriptor_bytes(void) { + uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) }; + + TEST_ASSERT_EQUAL(TUD_MIDI2_DESC_LEN, sizeof(desc)); + + // First byte: Audio Control Interface descriptor length = 9 + TEST_ASSERT_EQUAL(9, desc[0]); + TEST_ASSERT_EQUAL(TUSB_DESC_INTERFACE, desc[1]); + TEST_ASSERT_EQUAL(0, desc[2]); + + // Find Alt Setting 1 by scanning + int alt1_offset = -1; + int pos = 0; + while (pos < (int)sizeof(desc)) { + if (desc[pos + 1] == TUSB_DESC_INTERFACE && desc[pos + 3] == 1) { + alt1_offset = pos; + break; + } + pos += desc[pos]; + } + + TEST_ASSERT_TRUE_MESSAGE(alt1_offset >= 0, "Alt Setting 1 interface not found"); + + TEST_ASSERT_EQUAL(9, desc[alt1_offset]); + TEST_ASSERT_EQUAL(TUSB_DESC_INTERFACE, desc[alt1_offset + 1]); + TEST_ASSERT_EQUAL(1, desc[alt1_offset + 2]); // bInterfaceNumber + TEST_ASSERT_EQUAL(1, desc[alt1_offset + 3]); // bAlternateSetting + TEST_ASSERT_EQUAL(2, desc[alt1_offset + 4]); // bNumEndpoints + TEST_ASSERT_EQUAL(TUSB_CLASS_AUDIO, desc[alt1_offset + 5]); + + // MS Header after Alt Setting 1 interface: bcdMSC = 0x0200 + int ms2_offset = alt1_offset + 9; + TEST_ASSERT_EQUAL(7, desc[ms2_offset]); + TEST_ASSERT_EQUAL(TUSB_DESC_CS_INTERFACE, desc[ms2_offset + 1]); + TEST_ASSERT_EQUAL(MIDI_CS_INTERFACE_HEADER, desc[ms2_offset + 2]); + TEST_ASSERT_EQUAL(0x00, desc[ms2_offset + 3]); + TEST_ASSERT_EQUAL(0x02, desc[ms2_offset + 4]); + // USB-MIDI 2.0 Table 5-2: wTotalLength shall match bLength (= 0x0007) + TEST_ASSERT_EQUAL(0x07, desc[ms2_offset + 5]); + TEST_ASSERT_EQUAL(0x00, desc[ms2_offset + 6]); +} + +void test_midi2_descriptor_alt1_cs_endpoint_subtype(void) { + uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) }; + + int cs_ep_count = 0; + int pos = 0; + while (pos < (int)sizeof(desc)) { + if (desc[pos + 1] == TUSB_DESC_CS_ENDPOINT && + desc[pos + 2] == MIDI_CS_ENDPOINT_GENERAL_2_0) { + cs_ep_count++; + TEST_ASSERT_EQUAL(1, desc[pos + 3]); + } + pos += desc[pos]; + } + TEST_ASSERT_EQUAL(2, cs_ep_count); +} + +void test_midi2_descriptor_has_both_alt_settings(void) { + uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) }; + + int alt0_count = 0; + int alt1_count = 0; + int pos = 0; + while (pos < (int)sizeof(desc)) { + if (desc[pos + 1] == TUSB_DESC_INTERFACE) { + if (desc[pos + 3] == 0) alt0_count++; + if (desc[pos + 3] == 1) alt1_count++; + } + pos += desc[pos]; + } + TEST_ASSERT_TRUE(alt0_count >= 2); + TEST_ASSERT_EQUAL(1, alt1_count); +} + +void test_midi2_descriptor_endpoint_addresses(void) { + uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x02, 0x82, 64) }; + + int ep_out_count = 0; + int ep_in_count = 0; + int pos = 0; + while (pos < (int)sizeof(desc)) { + if (desc[pos + 1] == TUSB_DESC_ENDPOINT) { + uint8_t ep_addr = desc[pos + 2]; + if (ep_addr == 0x02) ep_out_count++; + if (ep_addr == 0x82) ep_in_count++; + TEST_ASSERT_EQUAL(TUSB_XFER_BULK, desc[pos + 3]); + TEST_ASSERT_EQUAL(64, desc[pos + 4]); + TEST_ASSERT_EQUAL(0, desc[pos + 5]); + } + pos += desc[pos]; + } + TEST_ASSERT_EQUAL(2, ep_out_count); + TEST_ASSERT_EQUAL(2, ep_in_count); +} + +void test_midi2_descriptor_nonzero_itfnum(void) { + uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(2, 0, 0x03, 0x83, 64) }; + + TEST_ASSERT_EQUAL(2, desc[2]); + + int pos = desc[0]; + while (pos < (int)sizeof(desc)) { + if (desc[pos + 1] == TUSB_DESC_INTERFACE) { + TEST_ASSERT_EQUAL(3, desc[pos + 2]); + break; + } + pos += desc[pos]; + } +} + +//--------------------------------------------------------------------+ +// Descriptor traversal integrity +//--------------------------------------------------------------------+ + +void test_midi2_descriptor_no_zero_length(void) { + uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) }; + + int pos = 0; + int desc_count = 0; + while (pos < (int)sizeof(desc)) { + TEST_ASSERT_TRUE_MESSAGE(desc[pos] > 0, "Zero-length descriptor found"); + TEST_ASSERT_TRUE_MESSAGE(desc[pos] <= (int)sizeof(desc) - pos, + "Descriptor length exceeds remaining bytes"); + pos += desc[pos]; + desc_count++; + } + TEST_ASSERT_EQUAL((int)sizeof(desc), pos); + TEST_ASSERT_TRUE(desc_count > 5); +} + +void test_midi2_descriptor_valid_types(void) { + uint8_t desc[] = { TUD_MIDI2_DESCRIPTOR(0, 0, 0x01, 0x81, 64) }; + + int pos = 0; + while (pos < (int)sizeof(desc)) { + uint8_t dtype = desc[pos + 1]; + bool valid = (dtype == TUSB_DESC_INTERFACE || + dtype == TUSB_DESC_ENDPOINT || + dtype == TUSB_DESC_CS_INTERFACE || + dtype == TUSB_DESC_CS_ENDPOINT); + TEST_ASSERT_TRUE_MESSAGE(valid, "Invalid descriptor type found"); + pos += desc[pos]; + } +} + +//--------------------------------------------------------------------+ +// Edge cases +//--------------------------------------------------------------------+ + +void test_ump_word_count_with_values_beyond_0xf(void) { + TEST_ASSERT_EQUAL(4, midi2_ump_word_count(0x10)); + TEST_ASSERT_EQUAL(4, midi2_ump_word_count(0xFF)); +} diff --git a/test/unit-test/test/device/msc/test_msc_device.c b/test/unit-test/test/device/msc/test_msc_device.c index 49843a921..ea02b7050 100644 --- a/test/unit-test/test/device/msc/test_msc_device.c +++ b/test/unit-test/test/device/msc/test_msc_device.c @@ -256,7 +256,7 @@ void test_msc(void) dcd_edpt_open_ExpectAndReturn(rhport, (tusb_desc_endpoint_t const *) tu_desc_next(desc_ep), true); // Prepare SCSI command - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_OUT, NULL, sizeof(msc_cbw_t), true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_OUT, NULL, sizeof(msc_cbw_t), false, true); dcd_edpt_xfer_IgnoreArg_buffer(); dcd_edpt_xfer_ReturnMemThruPtr_buffer( (uint8_t*) &cbw_read10, sizeof(msc_cbw_t)); @@ -264,20 +264,20 @@ void test_msc(void) dcd_event_xfer_complete(rhport, EDPT_MSC_OUT, sizeof(msc_cbw_t), 0, true); // control status - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, false, true); // SCSI Data transfer - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_IN, NULL, 512, true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_IN, NULL, 512, false, true); dcd_edpt_xfer_IgnoreArg_buffer(); dcd_event_xfer_complete(rhport, EDPT_MSC_IN, 512, 0, true); // complete // SCSI Status - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_IN, NULL, 13, true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_IN, NULL, 13, false, true); dcd_edpt_xfer_IgnoreArg_buffer(); dcd_event_xfer_complete(rhport, EDPT_MSC_IN, 13, 0, true); // Prepare for next command - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_OUT, NULL, sizeof(msc_cbw_t), true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_MSC_OUT, NULL, sizeof(msc_cbw_t), false, true); dcd_edpt_xfer_IgnoreArg_buffer(); tud_task(); diff --git a/test/unit-test/test/device/usbd/test_usbd.c b/test/unit-test/test/device/usbd/test_usbd.c index f0153da3f..7f3c3f5b2 100644 --- a/test/unit-test/test/device/usbd/test_usbd.c +++ b/test/unit-test/test/device/usbd/test_usbd.c @@ -29,7 +29,7 @@ #include "tusb_fifo.h" #include "tusb.h" #include "usbd.h" -TEST_SOURCE_FILE("usbd_control.c") +TEST_SOURCE_FILE("usbd.c") // Mock File #include "mock_dcd.h" @@ -100,6 +100,16 @@ tusb_control_request_t const req_get_desc_configuration = .wLength = 256 }; +// Vendor OUT control request (direction OUT, type Vendor, recipient Device), 8-byte data stage +tusb_control_request_t const req_vendor_out = +{ + .bmRequestType = 0x40, + .bRequest = 0x01, + .wValue = 0x0000, + .wIndex = 0x0000, + .wLength = 8 +}; + uint8_t const* desc_device; uint8_t const* desc_configuration; @@ -120,6 +130,19 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { return NULL; } +// Backing buffer for the vendor OUT data stage. Sized to EP0 max packet so an (untested) regression +// that drops the clamp can't corrupt memory here; the regression is caught by the expectation below. +static uint8_t vendor_out_buf[CFG_TUD_ENDPOINT0_SIZE]; + +bool tud_vendor_control_xfer_cb(uint8_t rhport_, uint8_t stage, tusb_control_request_t const* request) { + (void) request; + if (stage == CONTROL_STAGE_SETUP) { + // Offer only an 8-byte capacity even though the data stage may receive a larger packet + return tud_control_xfer(rhport_, request, vendor_out_buf, 8); + } + return true; +} + void setUp(void) { dcd_int_disable_Ignore(); dcd_int_enable_Ignore(); @@ -151,11 +174,11 @@ void test_usbd_get_device_descriptor(void) dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_device, false); // data - dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*)&data_desc_device, sizeof(tusb_desc_device_t), sizeof(tusb_desc_device_t), true); + dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*)&data_desc_device, sizeof(tusb_desc_device_t), sizeof(tusb_desc_device_t), false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, sizeof(tusb_desc_device_t), 0, false); // status - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, 0, 0, false); dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_get_desc_device, 1); @@ -184,11 +207,11 @@ void test_usbd_get_configuration_descriptor(void) dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_configuration, false); // data - dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*) data_desc_configuration, total_len, total_len, true); + dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*) data_desc_configuration, total_len, total_len, false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, total_len, 0, false); // status - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, 0, 0, false); dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_get_desc_configuration, 1); @@ -227,22 +250,49 @@ void test_usbd_control_in_zlp(void) // 1st transaction dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, EDPT_CTRL_IN, - zlp_desc_configuration, CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, true); + zlp_desc_configuration, CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, CFG_TUD_ENDPOINT0_SIZE, 0, false); // 2nd transaction dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, EDPT_CTRL_IN, - zlp_desc_configuration + CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, true); + zlp_desc_configuration + CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, CFG_TUD_ENDPOINT0_SIZE, false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, CFG_TUD_ENDPOINT0_SIZE, 0, false); // Expect Zero length Packet - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, 0, 0, false); // Status - dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true); + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, false, true); dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, 0, 0, false); dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_get_desc_configuration, 1); tud_task(); } + +//--------------------------------------------------------------------+ +// Control OUT data stage host overrun +//--------------------------------------------------------------------+ + +// A non-compliant host sends an OUT data packet larger than the buffer the class offered: +// wLength = 8, but the DCD reports a full CFG_TUD_ENDPOINT0_SIZE packet. usbd must clamp the +// copy/accounting to the 8-byte capacity so total_xferred reaches wLength, ends the data stage, +// and queues the IN status stage. Without the clamp total_xferred overshoots wLength and usbd +// re-arms an OUT data packet (EDPT_CTRL_OUT) instead, failing the EDPT_CTRL_IN expectation below. +void test_usbd_control_out_overrun_clamp(void) +{ + dcd_event_setup_received(rhport, (uint8_t*) &req_vendor_out, false); + + // Data stage: usbd arms an 8-byte OUT into its internal bounce buffer (buffer ptr is internal) + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 8, false, true); + dcd_edpt_xfer_IgnoreArg_buffer(); + // Host overrun: DCD reports a full max packet, larger than the 8-byte capacity + dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, CFG_TUD_ENDPOINT0_SIZE, XFER_RESULT_SUCCESS, false); + + // Clamp -> total_xferred == wLength -> data stage done -> IN status stage queued + dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, false, true); + dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, 0, 0, false); + dcd_edpt0_status_complete_ExpectWithArray(rhport, &req_vendor_out, 1); + + tud_task(); +} diff --git a/test/unit-test/test/host/midi2/test_midi2_host.c b/test/unit-test/test/host/midi2/test_midi2_host.c new file mode 100644 index 000000000..8ad77c14e --- /dev/null +++ b/test/unit-test/test/host/midi2/test_midi2_host.c @@ -0,0 +1,101 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 Saulo Verissimo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "unity.h" +#include "tusb_option.h" +#include "class/midi/midi.h" +#include "class/midi/midi2_host.h" + +void setUp(void) {} +void tearDown(void) {} + +//--------------------------------------------------------------------+ +// UMP Word Count (shared helper, defined in midi.h) +//--------------------------------------------------------------------+ + +void test_midi2_host_ump_word_count_1word(void) { + uint8_t types[] = {0x0, 0x1, 0x2, 0x6, 0x7}; + for (int i = 0; i < 5; i++) { + TEST_ASSERT_EQUAL(1, midi2_ump_word_count(types[i])); + } +} + +void test_midi2_host_ump_word_count_2word(void) { + uint8_t types[] = {0x3, 0x4, 0x8, 0x9, 0xA}; + for (int i = 0; i < 5; i++) { + TEST_ASSERT_EQUAL(2, midi2_ump_word_count(types[i])); + } +} + +void test_midi2_host_ump_word_count_4word(void) { + uint8_t types[] = {0x5, 0xD, 0xE, 0xF}; + for (int i = 0; i < 4; i++) { + TEST_ASSERT_EQUAL(4, midi2_ump_word_count(types[i])); + } +} + +//--------------------------------------------------------------------+ +// Callback struct field validation +//--------------------------------------------------------------------+ + +void test_midi2_descriptor_cb_struct_fields(void) { + tuh_midi2_descriptor_cb_t desc = { + .protocol_version = 1, + .bcdMSC_hi = 0x02, + .bcdMSC_lo = 0x00, + .rx_cable_count = 1, + .tx_cable_count = 1 + }; + TEST_ASSERT_EQUAL(1, desc.protocol_version); + TEST_ASSERT_EQUAL(0x02, desc.bcdMSC_hi); + TEST_ASSERT_EQUAL(0x00, desc.bcdMSC_lo); + TEST_ASSERT_EQUAL(1, desc.rx_cable_count); + TEST_ASSERT_EQUAL(1, desc.tx_cable_count); +} + +void test_midi2_mount_cb_struct_fields(void) { + tuh_midi2_mount_cb_t mount = { + .daddr = 1, + .bInterfaceNumber = 0, + .protocol_version = 1, + .alt_setting_active = 1, + .rx_cable_count = 2, + .tx_cable_count = 2 + }; + TEST_ASSERT_EQUAL(1, mount.daddr); + TEST_ASSERT_EQUAL(0, mount.bInterfaceNumber); + TEST_ASSERT_EQUAL(1, mount.protocol_version); + TEST_ASSERT_EQUAL(1, mount.alt_setting_active); + TEST_ASSERT_EQUAL(2, mount.rx_cable_count); + TEST_ASSERT_EQUAL(2, mount.tx_cable_count); +} + +//--------------------------------------------------------------------+ +// CS Endpoint subtypes +//--------------------------------------------------------------------+ + +void test_midi2_host_cs_endpoint_subtypes(void) { + TEST_ASSERT_EQUAL(0x01, MIDI_CS_ENDPOINT_GENERAL); + TEST_ASSERT_EQUAL(0x02, MIDI_CS_ENDPOINT_GENERAL_2_0); +} diff --git a/test/unit-test/test/support/tusb_config.h b/test/unit-test/test/support/tusb_config.h index 00818fae5..dee24f65d 100644 --- a/test/unit-test/test/support/tusb_config.h +++ b/test/unit-test/test/support/tusb_config.h @@ -23,8 +23,8 @@ * */ -#ifndef _TUSB_CONFIG_H_ -#define _TUSB_CONFIG_H_ +#ifndef TUSB_CONFIG_H_ +#define TUSB_CONFIG_H_ // testing framework #include "unity.h" @@ -103,4 +103,4 @@ } #endif -#endif /* _TUSB_CONFIG_H_ */ +#endif /* TUSB_CONFIG_H_ */ diff --git a/test/unit-test/test/test_common_func.c b/test/unit-test/test/test_common_func.c index 981531dd7..8afcc5b2b 100644 --- a/test/unit-test/test/test_common_func.c +++ b/test/unit-test/test/test_common_func.c @@ -80,3 +80,113 @@ void test_TU_ARGS_NUM(void) TEST_ASSERT_EQUAL(31, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31)); TEST_ASSERT_EQUAL(32, TU_ARGS_NUM(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32)); } + +void test_tu_scatter_read32(void) { + // Test data: 0x04030201 + uint8_t buf1[] = {0x01, 0x02, 0x03, 0x04}; + uint8_t buf2[] = {0x05, 0x06, 0x07, 0x08}; + + // len1=1, len2=0: read 1 byte from buf1 + TEST_ASSERT_EQUAL_HEX32(0x01, tu_scatter_read32(buf1, 1, buf2, 0)); + + // len1=1, len2=1: read 1 byte from buf1, 1 byte from buf2 + TEST_ASSERT_EQUAL_HEX32(0x0501, tu_scatter_read32(buf1, 1, buf2, 1)); + + // len1=1, len2=2: read 1 byte from buf1, 2 bytes from buf2 + TEST_ASSERT_EQUAL_HEX32(0x060501, tu_scatter_read32(buf1, 1, buf2, 2)); + + // len1=1, len2=3: read 1 byte from buf1, 3 bytes from buf2 + TEST_ASSERT_EQUAL_HEX32(0x07060501, tu_scatter_read32(buf1, 1, buf2, 3)); + + // len1=2, len2=0: read 2 bytes from buf1 + TEST_ASSERT_EQUAL_HEX32(0x0201, tu_scatter_read32(buf1, 2, buf2, 0)); + + // len1=2, len2=1: read 2 bytes from buf1, 1 byte from buf2 + TEST_ASSERT_EQUAL_HEX32(0x050201, tu_scatter_read32(buf1, 2, buf2, 1)); + + // len1=2, len2=2: read 2 bytes from buf1, 2 bytes from buf2 + TEST_ASSERT_EQUAL_HEX32(0x06050201, tu_scatter_read32(buf1, 2, buf2, 2)); + + // len1=3, len2=0: read 3 bytes from buf1 + TEST_ASSERT_EQUAL_HEX32(0x030201, tu_scatter_read32(buf1, 3, buf2, 0)); + + // len1=3, len2=1: read 3 bytes from buf1, 1 byte from buf2 + TEST_ASSERT_EQUAL_HEX32(0x05030201, tu_scatter_read32(buf1, 3, buf2, 1)); +} + +void test_tu_scatter_write32(void) { + uint8_t buf1[4]; + uint8_t buf2[4]; + + // len1=1, len2=0: write 1 byte to buf1 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x01, buf1, 1, buf2, 0); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x00, buf2[0]); + + // len1=1, len2=1: write 1 byte to buf1, 1 byte to buf2 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x0201, buf1, 1, buf2, 1); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf2[0]); + + // len1=1, len2=2: write 1 byte to buf1, 2 bytes to buf2 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x030201, buf1, 1, buf2, 2); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf2[0]); + TEST_ASSERT_EQUAL_HEX8(0x03, buf2[1]); + + // len1=1, len2=3: write 1 byte to buf1, 3 bytes to buf2 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x04030201, buf1, 1, buf2, 3); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf2[0]); + TEST_ASSERT_EQUAL_HEX8(0x03, buf2[1]); + TEST_ASSERT_EQUAL_HEX8(0x04, buf2[2]); + + // len1=2, len2=0: write 2 bytes to buf1 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x0201, buf1, 2, buf2, 0); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]); + + // len1=2, len2=1: write 2 bytes to buf1, 1 byte to buf2 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x030201, buf1, 2, buf2, 1); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]); + TEST_ASSERT_EQUAL_HEX8(0x03, buf2[0]); + + // len1=2, len2=2: write 2 bytes to buf1, 2 bytes to buf2 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x04030201, buf1, 2, buf2, 2); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]); + TEST_ASSERT_EQUAL_HEX8(0x03, buf2[0]); + TEST_ASSERT_EQUAL_HEX8(0x04, buf2[1]); + + // len1=3, len2=0: write 3 bytes to buf1 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x030201, buf1, 3, buf2, 0); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]); + TEST_ASSERT_EQUAL_HEX8(0x03, buf1[2]); + + // len1=3, len2=1: write 3 bytes to buf1, 1 byte to buf2 + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + tu_scatter_write32(0x04030201, buf1, 3, buf2, 1); + TEST_ASSERT_EQUAL_HEX8(0x01, buf1[0]); + TEST_ASSERT_EQUAL_HEX8(0x02, buf1[1]); + TEST_ASSERT_EQUAL_HEX8(0x03, buf1[2]); + TEST_ASSERT_EQUAL_HEX8(0x04, buf2[0]); +} diff --git a/test/unit-test/test/test_fifo.c b/test/unit-test/test/test_fifo.c index 3b4deb33e..b9279cb25 100644 --- a/test/unit-test/test/test_fifo.c +++ b/test/unit-test/test/test_fifo.c @@ -30,132 +30,114 @@ #include "osal/osal.h" #include "tusb_fifo.h" -#define FIFO_SIZE 64 -uint8_t tu_ff_buf[FIFO_SIZE * sizeof(uint8_t)]; -tu_fifo_t tu_ff = TU_FIFO_INIT(tu_ff_buf, FIFO_SIZE, uint8_t, false); +#define FIFO_SIZE 64 +uint8_t tu_ff_buf[FIFO_SIZE * sizeof(uint8_t)]; +tu_fifo_t tu_ff = TU_FIFO_INIT(tu_ff_buf, FIFO_SIZE, false); -tu_fifo_t* ff = &tu_ff; +tu_fifo_t *ff = &tu_ff; tu_fifo_buffer_info_t info; uint8_t test_data[4096]; uint8_t rd_buf[FIFO_SIZE]; -void setUp(void) -{ +static const tu_hwfifo_access_t hwfifo_access_32 = { + .data_stride = 4, + .param = 0, +}; + +static const tu_hwfifo_access_t hwfifo_access_16 = { + .data_stride = 2, + .param = 0, +}; + +void setUp(void) { tu_fifo_clear(ff); memset(&info, 0, sizeof(tu_fifo_buffer_info_t)); - for(int i=0; i<sizeof(test_data); i++) test_data[i] = i; + for (size_t i = 0; i < sizeof(test_data); i++) { + test_data[i] = i; + } memset(rd_buf, 0, sizeof(rd_buf)); } -void tearDown(void) -{ +void tearDown(void) { } //--------------------------------------------------------------------+ // Tests //--------------------------------------------------------------------+ -void test_normal(void) -{ - for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &i); +void test_normal(void) { + for (uint8_t i = 0; i < FIFO_SIZE; i++) { + tu_fifo_write(ff, &i); + } - for(uint8_t i=0; i < FIFO_SIZE; i++) - { + for (uint8_t i = 0; i < FIFO_SIZE; i++) { uint8_t c; tu_fifo_read(ff, &c); TEST_ASSERT_EQUAL(i, c); } } -void test_item_size(void) -{ - uint8_t ff4_buf[FIFO_SIZE * sizeof(uint32_t)]; - tu_fifo_t ff4 = TU_FIFO_INIT(ff4_buf, FIFO_SIZE, uint32_t, false); - - uint32_t data4[2*FIFO_SIZE]; - for(uint32_t i=0; i<sizeof(data4)/4; i++) data4[i] = i; - - // fill up fifo - tu_fifo_write_n(&ff4, data4, FIFO_SIZE); - - uint32_t rd_buf4[FIFO_SIZE]; - uint16_t rd_count; - - // read 0 -> 4 - rd_count = tu_fifo_read_n(&ff4, rd_buf4, 5); - TEST_ASSERT_EQUAL( 5, rd_count ); - TEST_ASSERT_EQUAL_UINT32_ARRAY( data4, rd_buf4, rd_count ); // 0 -> 4 - - tu_fifo_write_n(&ff4, data4+FIFO_SIZE, 5); - - // read all 5 -> 68 - rd_count = tu_fifo_read_n(&ff4, rd_buf4, FIFO_SIZE); - TEST_ASSERT_EQUAL( FIFO_SIZE, rd_count ); - TEST_ASSERT_EQUAL_UINT32_ARRAY( data4+5, rd_buf4, rd_count ); // 5 -> 68 -} - -void test_read_n(void) -{ +void test_read_n(void) { uint16_t rd_count; // fill up fifo - for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, test_data+i); + for (uint8_t i = 0; i < FIFO_SIZE; i++) { + tu_fifo_write(ff, test_data + i); + } // case 1: Read index + count < depth // read 0 -> 4 rd_count = tu_fifo_read_n(ff, rd_buf, 5); - TEST_ASSERT_EQUAL( 5, rd_count ); - TEST_ASSERT_EQUAL_MEMORY( test_data, rd_buf, rd_count ); // 0 -> 4 + TEST_ASSERT_EQUAL(5, rd_count); + TEST_ASSERT_EQUAL_MEMORY(test_data, rd_buf, rd_count); // 0 -> 4 // case 2: Read index + count > depth // write 10, 11, 12 - tu_fifo_write(ff, test_data+FIFO_SIZE); - tu_fifo_write(ff, test_data+FIFO_SIZE+1); - tu_fifo_write(ff, test_data+FIFO_SIZE+2); + tu_fifo_write(ff, test_data + FIFO_SIZE); + tu_fifo_write(ff, test_data + FIFO_SIZE + 1); + tu_fifo_write(ff, test_data + FIFO_SIZE + 2); rd_count = tu_fifo_read_n(ff, rd_buf, 7); - TEST_ASSERT_EQUAL( 7, rd_count ); + TEST_ASSERT_EQUAL(7, rd_count); - TEST_ASSERT_EQUAL_MEMORY( test_data+5, rd_buf, rd_count ); // 5 -> 11 + TEST_ASSERT_EQUAL_MEMORY(test_data + 5, rd_buf, rd_count); // 5 -> 11 // Should only read until empty - TEST_ASSERT_EQUAL( FIFO_SIZE-5+3-7, tu_fifo_read_n(ff, rd_buf, 100) ); + TEST_ASSERT_EQUAL(FIFO_SIZE - 5 + 3 - 7, tu_fifo_read_n(ff, rd_buf, 100)); } -void test_write_n(void) -{ +void test_write_n(void) { // case 1: wr + count < depth tu_fifo_write_n(ff, test_data, 32); // wr = 32, count = 32 uint16_t rd_count; rd_count = tu_fifo_read_n(ff, rd_buf, 16); // wr = 32, count = 16 - TEST_ASSERT_EQUAL( 16, rd_count ); - TEST_ASSERT_EQUAL_MEMORY( test_data, rd_buf, rd_count ); + TEST_ASSERT_EQUAL(16, rd_count); + TEST_ASSERT_EQUAL_MEMORY(test_data, rd_buf, rd_count); // case 2: wr + count > depth - tu_fifo_write_n(ff, test_data+32, 40); // wr = 72 -> 8, count = 56 + tu_fifo_write_n(ff, test_data + 32, 40); // wr = 72 -> 8, count = 56 - tu_fifo_read_n(ff, rd_buf, 32); // count = 24 - TEST_ASSERT_EQUAL_MEMORY( test_data+16, rd_buf, rd_count); + tu_fifo_read_n(ff, rd_buf, 32); // count = 24 + TEST_ASSERT_EQUAL_MEMORY(test_data + 16, rd_buf, rd_count); TEST_ASSERT_EQUAL(24, tu_fifo_count(ff)); } -void test_write_double_overflowed(void) -{ +void test_write_double_overflowed(void) { tu_fifo_set_overwritable(ff, true); - uint8_t rd_buf[FIFO_SIZE] = { 0 }; - uint8_t* buf = test_data; + uint8_t rd_buf[FIFO_SIZE] = {0}; + uint8_t *buf = test_data; // full buf += tu_fifo_write_n(ff, buf, FIFO_SIZE); TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff)); // write more, should still full - buf += tu_fifo_write_n(ff, buf, FIFO_SIZE-8); + buf += tu_fifo_write_n(ff, buf, FIFO_SIZE - 8); TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff)); // double overflowed: in total, write more than > 2*FIFO_SIZE @@ -165,14 +147,13 @@ void test_write_double_overflowed(void) // reading back should give back data from last FIFO_SIZE write tu_fifo_read_n(ff, rd_buf, FIFO_SIZE); - TEST_ASSERT_EQUAL_MEMORY(buf-16, rd_buf+FIFO_SIZE-16, 16); + TEST_ASSERT_EQUAL_MEMORY(buf - 16, rd_buf + FIFO_SIZE - 16, 16); // TODO whole buffer should match, but we deliberately not implement it // TEST_ASSERT_EQUAL_MEMORY(buf-FIFO_SIZE, rd_buf, FIFO_SIZE); } -static uint16_t help_write(uint16_t total, uint16_t n) -{ +static uint16_t help_write(uint16_t total, uint16_t n) { tu_fifo_write_n(ff, test_data, n); total = tu_min16(FIFO_SIZE, total + n); @@ -182,8 +163,7 @@ static uint16_t help_write(uint16_t total, uint16_t n) return total; } -void test_write_overwritable2(void) -{ +void test_write_overwritable2(void) { tu_fifo_set_overwritable(ff, true); // based on actual crash tests detected by fuzzing @@ -202,13 +182,15 @@ void test_write_overwritable2(void) total = help_write(total, 192); } -void test_peek(void) -{ +void test_peek(void) { uint8_t temp; - temp = 10; tu_fifo_write(ff, &temp); - temp = 20; tu_fifo_write(ff, &temp); - temp = 30; tu_fifo_write(ff, &temp); + temp = 10; + tu_fifo_write(ff, &temp); + temp = 20; + tu_fifo_write(ff, &temp); + temp = 30; + tu_fifo_write(ff, &temp); temp = 0; @@ -222,12 +204,13 @@ void test_peek(void) TEST_ASSERT_EQUAL(30, temp); } -void test_get_read_info_when_no_wrap() -{ +void test_get_read_info_when_no_wrap() { uint8_t ch = 1; // write 6 items - for(uint8_t i=0; i < 6; i++) tu_fifo_write(ff, &ch); + for (uint8_t i = 0; i < 6; i++) { + tu_fifo_write(ff, &ch); + } // read 2 items tu_fifo_read(ff, &ch); @@ -235,22 +218,25 @@ void test_get_read_info_when_no_wrap() tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(4, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(4, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info.ptr_lin); - TEST_ASSERT_NULL(info.ptr_wrap); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr); + TEST_ASSERT_NULL(info.wrapped.ptr); } -void test_get_read_info_when_wrapped() -{ +void test_get_read_info_when_wrapped() { uint8_t ch = 1; // make fifo full - for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &ch); + for (uint8_t i = 0; i < FIFO_SIZE; i++) { + tu_fifo_write(ff, &ch); + } // read 6 items - for(uint8_t i=0; i < 6; i++) tu_fifo_read(ff, &ch); + for (uint8_t i = 0; i < 6; i++) { + tu_fifo_read(ff, &ch); + } // write 2 items tu_fifo_write(ff, &ch); @@ -258,15 +244,14 @@ void test_get_read_info_when_wrapped() tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin); - TEST_ASSERT_EQUAL(2, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE - 6, info.linear.len); + TEST_ASSERT_EQUAL(2, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info.ptr_lin); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 6, info.linear.ptr); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr); } -void test_get_write_info_when_no_wrap() -{ +void test_get_write_info_when_no_wrap() { uint8_t ch = 1; // write 2 items @@ -275,20 +260,21 @@ void test_get_write_info_when_no_wrap() tu_fifo_get_write_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE-2, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE - 2, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+2, info .ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr); // application should check len instead of ptr. - // TEST_ASSERT_NULL(info.ptr_wrap); + // TEST_ASSERT_NULL(info.wrapped.ptr); } -void test_get_write_info_when_wrapped() -{ +void test_get_write_info_when_wrapped() { uint8_t ch = 1; // write 6 items - for(uint8_t i=0; i < 6; i++) tu_fifo_write(ff, &ch); + for (uint8_t i = 0; i < 6; i++) { + tu_fifo_write(ff, &ch); + } // read 2 items tu_fifo_read(ff, &ch); @@ -296,70 +282,69 @@ void test_get_write_info_when_wrapped() tu_fifo_get_write_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE-6, info.len_lin); - TEST_ASSERT_EQUAL(2, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE - 6, info.linear.len); + TEST_ASSERT_EQUAL(2, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer+6, info .ptr_lin); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_wrap); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 6, info.linear.ptr); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr); } -void test_empty(void) -{ +void test_empty(void) { uint8_t temp; TEST_ASSERT_TRUE(tu_fifo_empty(ff)); // read info tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(0, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(0, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_NULL(info.ptr_lin); - TEST_ASSERT_NULL(info.ptr_wrap); + TEST_ASSERT_NULL(info.linear.ptr); + TEST_ASSERT_NULL(info.wrapped.ptr); // write info tu_fifo_get_write_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info .ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.linear.ptr); // application should check len instead of ptr. - // TEST_ASSERT_NULL(info.ptr_wrap); + // TEST_ASSERT_NULL(info.wrapped.ptr); // write 1 then re-check empty tu_fifo_write(ff, &temp); TEST_ASSERT_FALSE(tu_fifo_empty(ff)); } -void test_full(void) -{ +void test_full(void) { TEST_ASSERT_FALSE(tu_fifo_full(ff)); - for(uint8_t i=0; i < FIFO_SIZE; i++) tu_fifo_write(ff, &i); + for (uint8_t i = 0; i < FIFO_SIZE; i++) { + tu_fifo_write(ff, &i); + } TEST_ASSERT_TRUE(tu_fifo_full(ff)); // read info tu_fifo_get_read_info(ff, &info); - TEST_ASSERT_EQUAL(FIFO_SIZE, info.len_lin); - TEST_ASSERT_EQUAL(0, info.len_wrap); + TEST_ASSERT_EQUAL(FIFO_SIZE, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); - TEST_ASSERT_EQUAL_PTR(ff->buffer, info.ptr_lin); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.linear.ptr); // skip this, application must check len instead of buffer - // TEST_ASSERT_NULL(info.ptr_wrap); + // TEST_ASSERT_NULL(info.wrapped.ptr); // write info } -void test_rd_idx_wrap() -{ +void test_rd_idx_wrap(void) { tu_fifo_t ff10; - uint8_t buf[10]; - uint8_t dst[10]; + uint8_t buf[10]; + uint8_t dst[10]; - tu_fifo_config(&ff10, buf, 10, 1, 1); + tu_fifo_config(&ff10, buf, 10, 1); uint16_t n; @@ -376,3 +361,267 @@ void test_rd_idx_wrap() TEST_ASSERT_EQUAL(n, 2); TEST_ASSERT_EQUAL(ff10.rd_idx, 6); } + +void test_advance_write_pointer_cases(void) { + tu_fifo_clear(ff); + + tu_fifo_advance_write_pointer(ff, 3); + TEST_ASSERT_EQUAL(3, ff->wr_idx); + TEST_ASSERT_EQUAL(3, tu_fifo_count(ff)); + + // advance to cross depth but stay within 0..2*depth window + ff->wr_idx = FIFO_SIZE - 2; // 62 + ff->rd_idx = 0; + tu_fifo_advance_write_pointer(ff, 10); // 62 + 10 = 72 within window + TEST_ASSERT_EQUAL(72, ff->wr_idx); + TEST_ASSERT_EQUAL(FIFO_SIZE, tu_fifo_count(ff)); + + // advance past the unused index space (beyond 2*depth) + ff->wr_idx = (uint16_t)(2 * FIFO_SIZE - 3); // 125 + ff->rd_idx = 0; + tu_fifo_advance_write_pointer(ff, 6); // forces wrap across unused space + TEST_ASSERT_EQUAL(3, ff->wr_idx); + TEST_ASSERT_EQUAL(3, tu_fifo_count(ff)); +} + +void test_advance_read_pointer_cases(void) { + tu_fifo_clear(ff); + + ff->wr_idx = 6; + tu_fifo_advance_read_pointer(ff, 3); + TEST_ASSERT_EQUAL(3, ff->rd_idx); + TEST_ASSERT_EQUAL(3, tu_fifo_count(ff)); + + ff->wr_idx = FIFO_SIZE + 10; // 74 + ff->rd_idx = FIFO_SIZE - 10; // 54 + tu_fifo_advance_read_pointer(ff, 20); // move to match write index within window + TEST_ASSERT_EQUAL(74, ff->rd_idx); + TEST_ASSERT_EQUAL(0, tu_fifo_count(ff)); + + ff->wr_idx = 9; + ff->rd_idx = (uint16_t)(2 * FIFO_SIZE - 1); // 127 + tu_fifo_advance_read_pointer(ff, 6); // crosses unused index space + TEST_ASSERT_EQUAL(5, ff->rd_idx); + TEST_ASSERT_EQUAL(4, tu_fifo_count(ff)); +} + +void test_write_n_fixed_addr_rw32_nowrap(void) { + tu_fifo_clear(ff); + + volatile uint32_t reg = 0x11223344; + uint8_t expected[8] = {0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11}; + + for (uint8_t n = 1; n <= 8; n++) { + tu_fifo_clear(ff); + uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)®, n, &hwfifo_access_32); + TEST_ASSERT_EQUAL(n, written); + TEST_ASSERT_EQUAL(n, tu_fifo_count(ff)); + + uint8_t out[8] = {0}; + tu_fifo_read_n(ff, out, n); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n); + } +} + +void test_write_n_fixed_addr_rw32_wrapped(void) { + tu_fifo_clear(ff); + + volatile uint32_t reg = 0xA1B2C3D4; + uint8_t expected[8] = {0xD4, 0xC3, 0xB2, 0xA1, 0xD4, 0xC3, 0xB2, 0xA1}; + + for (uint8_t n = 1; n <= 8; n++) { + tu_fifo_clear(ff); + // Position the fifo near the end so writes wrap + ff->wr_idx = FIFO_SIZE - 3; + ff->rd_idx = FIFO_SIZE - 3; + + uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)®, n, &hwfifo_access_32); + TEST_ASSERT_EQUAL(n, written); + TEST_ASSERT_EQUAL(n, tu_fifo_count(ff)); + + uint8_t out[8] = {0}; + tu_fifo_read_n(ff, out, n); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n); + } +} + +void test_read_n_fixed_addr_rw32_nowrap(void) { + uint8_t pattern[8] = {0x10, 0x21, 0x32, 0x43, 0x54, 0x65, 0x76, 0x87}; + uint32_t reg_expected[8] = { + 0x00000010, 0x00002110, 0x00322110, 0x43322110, 0x00000054, 0x00006554, 0x00766554, 0x87766554}; + + for (uint8_t n = 1; n <= 8; n++) { + tu_fifo_clear(ff); + tu_fifo_write_n(ff, pattern, 8); + + uint32_t reg = 0; + uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, ®, n, &hwfifo_access_32); + TEST_ASSERT_EQUAL(n, read_cnt); + TEST_ASSERT_EQUAL(8 - n, tu_fifo_count(ff)); + + TEST_ASSERT_EQUAL_HEX32(reg_expected[n - 1], reg); + } +} + +void test_read_n_fixed_addr_rw32_wrapped(void) { + uint8_t pattern[8] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87}; + uint32_t reg_expected[8] = { + 0x000000F0, 0x0000E1F0, 0x00D2E1F0, 0xC3D2E1F0, 0x000000B4, 0x0000A5B4, 0x0096A5B4, 0x8796A5B4}; + + for (uint8_t n = 1; n <= 8; n++) { + tu_fifo_clear(ff); + ff->rd_idx = FIFO_SIZE - 2; + ff->wr_idx = (uint16_t)(ff->rd_idx + n); + + for (uint8_t i = 0; i < n; i++) { + uint8_t idx = (uint8_t)((ff->rd_idx + i) % FIFO_SIZE); + ff->buffer[idx] = pattern[i]; + } + + uint32_t reg = 0; + uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, ®, n, &hwfifo_access_32); + TEST_ASSERT_EQUAL(n, read_cnt); + TEST_ASSERT_EQUAL(0, tu_fifo_count(ff)); + + TEST_ASSERT_EQUAL_HEX32(reg_expected[n - 1], reg); + } +} + +void test_write_n_fixed_addr_rw16_nowrap(void) { + tu_fifo_clear(ff); + + volatile uint16_t reg = 0x1122; + uint8_t expected[6] = {0x22, 0x11, 0x22, 0x11, 0x22, 0x11}; + + for (uint8_t n = 1; n <= 6; n++) { + tu_fifo_clear(ff); + uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)®, n, &hwfifo_access_16); + TEST_ASSERT_EQUAL(n, written); + TEST_ASSERT_EQUAL(n, tu_fifo_count(ff)); + + uint8_t out[6] = {0}; + tu_fifo_read_n(ff, out, n); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n); + } +} + +void test_write_n_fixed_addr_rw16_wrapped(void) { + tu_fifo_clear(ff); + + volatile uint16_t reg = 0xA1B2; + uint8_t expected[6] = {0xB2, 0xA1, 0xB2, 0xA1, 0xB2, 0xA1}; + + for (uint8_t n = 1; n <= 6; n++) { + tu_fifo_clear(ff); + // Position the fifo near the end so writes wrap + ff->wr_idx = FIFO_SIZE - 3; + ff->rd_idx = FIFO_SIZE - 3; + + uint16_t written = tu_fifo_write_n_access_mode(ff, (const void *)®, n, &hwfifo_access_16); + TEST_ASSERT_EQUAL(n, written); + TEST_ASSERT_EQUAL(n, tu_fifo_count(ff)); + + uint8_t out[6] = {0}; + tu_fifo_read_n(ff, out, n); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected, out, n); + } +} + +void test_read_n_fixed_addr_rw16_nowrap(void) { + uint8_t pattern[6] = {0x10, 0x21, 0x32, 0x43, 0x54, 0x65}; + uint16_t reg_expected[6] = {0x0010, 0x2110, 0x0032, 0x4332, 0x0054, 0x6554}; + + for (uint8_t n = 1; n <= 6; n++) { + tu_fifo_clear(ff); + tu_fifo_write_n(ff, pattern, 6); + + uint16_t reg = 0; + uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, ®, n, &hwfifo_access_16); + TEST_ASSERT_EQUAL(n, read_cnt); + TEST_ASSERT_EQUAL(6 - n, tu_fifo_count(ff)); + + TEST_ASSERT_EQUAL_HEX16(reg_expected[n - 1], reg); + } +} + +void test_read_n_fixed_addr_rw16_wrapped(void) { + uint8_t pattern[6] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5}; + uint16_t reg_expected[6] = {0x00F0, 0xE1F0, 0x00D2, 0xC3D2, 0x00B4, 0xA5B4}; + + for (uint8_t n = 1; n <= 6; n++) { + tu_fifo_clear(ff); + ff->rd_idx = FIFO_SIZE - 1; + ff->wr_idx = (uint16_t)(ff->rd_idx + n); + + for (uint8_t i = 0; i < n; i++) { + uint8_t idx = (uint8_t)((ff->rd_idx + i) % FIFO_SIZE); + ff->buffer[idx] = pattern[i]; + } + + uint16_t reg = 0; + uint16_t read_cnt = tu_fifo_read_n_access_mode(ff, ®, n, &hwfifo_access_16); + TEST_ASSERT_EQUAL(n, read_cnt); + TEST_ASSERT_EQUAL(0, tu_fifo_count(ff)); + + TEST_ASSERT_EQUAL_HEX16(reg_expected[n - 1], reg); + } +} + +void test_get_read_info_advanced_cases(void) { + tu_fifo_clear(ff); + + ff->wr_idx = 20; + ff->rd_idx = 2; + tu_fifo_get_read_info(ff, &info); + TEST_ASSERT_EQUAL(18, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 2, info.linear.ptr); + TEST_ASSERT_NULL(info.wrapped.ptr); + + ff->wr_idx = 68; // ptr = 4 + ff->rd_idx = 56; // ptr = 56 + tu_fifo_get_read_info(ff, &info); + TEST_ASSERT_EQUAL(8, info.linear.len); + TEST_ASSERT_EQUAL(4, info.wrapped.len); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 56, info.linear.ptr); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr); +} + +void test_get_write_info_advanced_cases(void) { + tu_fifo_clear(ff); + + ff->wr_idx = 10; + ff->rd_idx = 104; // ptr = 40 + tu_fifo_get_write_info(ff, &info); + TEST_ASSERT_EQUAL(30, info.linear.len); + TEST_ASSERT_EQUAL(0, info.wrapped.len); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 10, info.linear.ptr); + TEST_ASSERT_NULL(info.wrapped.ptr); + + ff->wr_idx = 60; + ff->rd_idx = 20; + tu_fifo_get_write_info(ff, &info); + TEST_ASSERT_EQUAL(4, info.linear.len); + TEST_ASSERT_EQUAL(20, info.wrapped.len); + TEST_ASSERT_EQUAL_PTR(ff->buffer + 60, info.linear.ptr); + TEST_ASSERT_EQUAL_PTR(ff->buffer, info.wrapped.ptr); +} + +void test_correct_read_pointer_cases(void) { + tu_fifo_clear(ff); + + // wr beyond depth: rd should be wr - depth + ff->wr_idx = FIFO_SIZE + 6; // 70 + tu_fifo_correct_read_pointer(ff); + TEST_ASSERT_EQUAL(6, ff->rd_idx); + + // wr exactly at depth: rd should wrap to zero + ff->wr_idx = FIFO_SIZE; + tu_fifo_correct_read_pointer(ff); + TEST_ASSERT_EQUAL(0, ff->rd_idx); + + // wr below depth: rd should be wr + depth + ff->wr_idx = 10; + tu_fifo_correct_read_pointer(ff); + TEST_ASSERT_EQUAL(FIFO_SIZE + 10, ff->rd_idx); +} |
