summaryrefslogtreecommitdiff
path: root/test/unit-test
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit-test')
-rw-r--r--test/unit-test/CMakeLists.txt131
-rw-r--r--test/unit-test/project.yml3
-rw-r--r--test/unit-test/test/device/midi2/test_midi2_device.c266
-rw-r--r--test/unit-test/test/device/msc/test_msc_device.c10
-rw-r--r--test/unit-test/test/device/usbd/test_usbd.c68
-rw-r--r--test/unit-test/test/host/midi2/test_midi2_host.c101
-rw-r--r--test/unit-test/test/support/tusb_config.h6
-rw-r--r--test/unit-test/test/test_common_func.c110
-rw-r--r--test/unit-test/test/test_fifo.c497
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 *)&reg, 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 *)&reg, 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, &reg, 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, &reg, 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 *)&reg, 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 *)&reg, 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, &reg, 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, &reg, 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);
+}