diff --git a/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c b/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c index 0a41ffe7b7..87ed9bdd95 100644 --- a/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c +++ b/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c @@ -226,9 +226,11 @@ static void usb_serial_jtag_return_char(int fd, int c) static ssize_t usb_serial_jtag_read(int fd, void* data, size_t size) { + assert(fd == USJ_LOCAL_FD); char *data_c = (char *) data; size_t received = 0; _lock_acquire_recursive(&s_ctx.read_lock); + while (received < size) { int c = usb_serial_jtag_read_char(fd); if (c == '\r') { diff --git a/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/main/test_vfs_usb_serial_jtag.c b/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/main/test_vfs_usb_serial_jtag.c index c349aab14c..9c418c57fe 100644 --- a/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/main/test_vfs_usb_serial_jtag.c +++ b/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/main/test_vfs_usb_serial_jtag.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,11 +23,6 @@ #include "test_utils.h" #include "sdkconfig.h" -struct task_arg_t { - FILE* stream; - SemaphoreHandle_t done; -}; - static int read_bytes_with_select(FILE *stream, void *buf, size_t buf_len, struct timeval tv) { @@ -74,7 +69,7 @@ static int read_bytes_with_select(FILE *stream, void *buf, size_t buf_len, struc * write calls. At the end of the test, the variable is check to make sure the select returned * for each of the write calls. */ -TEST_CASE("test select read, write and timeout", "[usb_serial_jtag]") +TEST_CASE("test select read, write and timeout", "[usb_serial_jtag vfs]") { struct timeval tv; tv.tv_sec = 1; @@ -128,3 +123,55 @@ TEST_CASE("test select read, write and timeout", "[usb_serial_jtag]") usb_serial_jtag_vfs_use_nonblocking(); usb_serial_jtag_driver_uninstall(); } + +TEST_CASE("read with usj driver (non-blocking)", "[usb serial jtag vfs]") +{ + // Send a string with length less than the read requested length + const char in_buffer[] = "!(@*#&(!*@&#((SDasdkjhad\nce"; // read should not early return on \n + const size_t in_buffer_len = sizeof(in_buffer); + + const size_t out_buffer_len = in_buffer_len - 1; // don't compare the null character at the end of in_buffer string + char out_buffer[out_buffer_len] = {}; + + // flush_stdin_stdout(); + + usb_serial_jtag_driver_config_t usj_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&usj_config)); + usb_serial_jtag_vfs_use_driver(); + + usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_LF); + usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_LF); + + int flags = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); + + // trigger the test environment to send the test message + char ready_msg[] = "ready to receive\n"; + write(fileno(stdout), ready_msg, sizeof(ready_msg)); + + // wait for the string to be sent and buffered + vTaskDelay(pdMS_TO_TICKS(500)); + + char *out_buffer_ptr = out_buffer; + size_t bytes_read = 0; + do { + int nread = read(STDIN_FILENO, out_buffer_ptr, out_buffer_len); + printf("%d\n", nread); + if (nread > 0) { + out_buffer_ptr += nread; + bytes_read += nread; + } + } while (bytes_read < in_buffer_len); + + // string compare + for (size_t i = 0; i < out_buffer_len; i++) { + TEST_ASSERT_EQUAL(in_buffer[i], out_buffer[i]); + } + + usb_serial_jtag_vfs_use_nonblocking(); + fcntl(STDIN_FILENO, F_SETFL, flags); + usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_CRLF); + usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + ESP_ERROR_CHECK(usb_serial_jtag_driver_uninstall()); + vTaskDelay(2); // wait for tasks to exit +} diff --git a/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/pytest_usb_serial_jtag_vfs.py b/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/pytest_usb_serial_jtag_vfs.py index 165a54fb97..60ad7bcc3d 100644 --- a/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/pytest_usb_serial_jtag_vfs.py +++ b/components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/pytest_usb_serial_jtag_vfs.py @@ -15,10 +15,28 @@ from pytest_embedded_idf.utils import idf_parametrize ) @pytest.mark.parametrize('test_message', ['test123456789!@#%^&*']) @idf_parametrize('target', ['esp32s3', 'esp32c3', 'esp32c6', 'esp32h2'], indirect=['target']) -def test_usj_vfs_release(dut: Dut, test_message: list) -> None: +def test_usj_vfs_select(dut: Dut, test_message: list) -> None: dut.expect_exact('Press ENTER to see the list of tests') dut.write('"test select read, write and timeout"') dut.expect_exact('select timed out', timeout=2) dut.write(test_message) dut.expect_exact(test_message, timeout=2) dut.expect(r'\d{1} Tests 0 Failures 0 Ignored', timeout=10) + + +@pytest.mark.usb_serial_jtag +@pytest.mark.parametrize( + 'port, config', + [ + pytest.param('/dev/ttyACM1', 'release'), + ], + indirect=True, +) +@pytest.mark.parametrize('test_message', ['!(@*#&(!*@&#((SDasdkjhad\nce']) +@idf_parametrize('target', ['esp32s3', 'esp32c3', 'esp32c6', 'esp32h2'], indirect=['target']) +def test_usj_vfs_read(dut: Dut, test_message: list) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"read with usj driver (non-blocking)"') + dut.expect_exact('ready to receive', timeout=2) + dut.write(test_message) + dut.expect(r'\d{1} Tests 0 Failures 0 Ignored', timeout=10) diff --git a/components/esp_vfs_console/test_apps/usb_cdc_vfs/main/test_app_main.c b/components/esp_vfs_console/test_apps/usb_cdc_vfs/main/test_app_main.c index 67fe5a7fa4..a446d4a871 100644 --- a/components/esp_vfs_console/test_apps/usb_cdc_vfs/main/test_app_main.c +++ b/components/esp_vfs_console/test_apps/usb_cdc_vfs/main/test_app_main.c @@ -175,8 +175,56 @@ static void test_usb_cdc_read_non_blocking(void) TEST_ASSERT(errno != EWOULDBLOCK); } +static void test_usb_cdc_read_no_exit_on_newline_reception(void) +{ + test_setup(__func__, sizeof(__func__)); + + // Send a string with length less than the read requested length + const char in_buffer[] = "!(@*#&(!*@&#((SDasdkjhad\nce"; // read should not early return on \n + const size_t in_buffer_len = sizeof(in_buffer); + + const size_t out_buffer_len = in_buffer_len - 1; // don't compare the null character at the end of in_buffer string + char out_buffer[out_buffer_len] = {}; + + ESP_ERROR_CHECK(esp_vfs_dev_cdcacm_register()); + + esp_vfs_dev_cdcacm_set_rx_line_endings(ESP_LINE_ENDINGS_LF); + esp_vfs_dev_cdcacm_set_tx_line_endings(ESP_LINE_ENDINGS_LF); + + int flags = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); + + // trigger the test environment to send the test message + char ready_msg[] = "ready to receive\n"; + write(fileno(stdout), ready_msg, sizeof(ready_msg)); + + // wait for the string to be sent and buffered + vTaskDelay(pdMS_TO_TICKS(500)); + + char *out_buffer_ptr = out_buffer; + size_t bytes_read = 0; + do { + int nread = read(STDIN_FILENO, out_buffer_ptr, out_buffer_len); + if (nread > 0) { + out_buffer_ptr += nread; + bytes_read += nread; + } + } while (bytes_read < in_buffer_len); + + // string compare + for (size_t i = 0; i < out_buffer_len; i++) { + TEST_ASSERT_EQUAL(in_buffer[i], out_buffer[i]); + } + + fcntl(STDIN_FILENO, F_SETFL, flags); + esp_vfs_dev_cdcacm_set_rx_line_endings(ESP_LINE_ENDINGS_CRLF); + esp_vfs_dev_cdcacm_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); + vTaskDelay(2); // wait for tasks to exit +} + void app_main(void) { test_usb_cdc_select(); test_usb_cdc_read_non_blocking(); + test_usb_cdc_read_no_exit_on_newline_reception(); } diff --git a/components/esp_vfs_console/test_apps/usb_cdc_vfs/pytest_usb_cdc_vfs.py b/components/esp_vfs_console/test_apps/usb_cdc_vfs/pytest_usb_cdc_vfs.py index 5d3d30021f..76ea464780 100644 --- a/components/esp_vfs_console/test_apps/usb_cdc_vfs/pytest_usb_cdc_vfs.py +++ b/components/esp_vfs_console/test_apps/usb_cdc_vfs/pytest_usb_cdc_vfs.py @@ -26,3 +26,8 @@ def test_usb_cdc_vfs_default(dut: Dut, test_message: str) -> None: dut.expect_exact('test_usb_cdc_read_non_blocking', timeout=2) dut.expect_exact('send_bytes', timeout=2) dut.write('abcdefgh') + + # test run: test_usb_cdc_read_no_exit_on_newline_reception + dut.expect_exact('test_usb_cdc_read_no_exit_on_newline_reception', timeout=2) + dut.expect_exact('ready to receive', timeout=2) + dut.write('!(@*#&(!*@&#((SDasdkjhad\nce')