summaryrefslogtreecommitdiff
path: root/test/regression/bsd_test/netx_bsd_select_improvements_test.c
diff options
context:
space:
mode:
authorFrédéric Desbiens <[email protected]>2026-06-08 10:03:52 +0200
committerGitHub <[email protected]>2026-06-08 10:03:52 +0200
commit1a0e9e43a03e37ddecfb6b49e9b078d6e1803d05 (patch)
treec60ec41b4ff4fd2d7566c505cbdd423ceddd65f3 /test/regression/bsd_test/netx_bsd_select_improvements_test.c
parent8b6e03ac30ab688bec02c69d42f2304b7f72a202 (diff)
parent4d88828b06d164a5b7e82152395a92254abcd6e4 (diff)
Merge pull request #391 from eclipse-threadx/devv6.5.1.202602_reldev
Merging changes for the v.6.5.1.202602 release
Diffstat (limited to 'test/regression/bsd_test/netx_bsd_select_improvements_test.c')
-rw-r--r--test/regression/bsd_test/netx_bsd_select_improvements_test.c316
1 files changed, 316 insertions, 0 deletions
diff --git a/test/regression/bsd_test/netx_bsd_select_improvements_test.c b/test/regression/bsd_test/netx_bsd_select_improvements_test.c
new file mode 100644
index 00000000..86aba67f
--- /dev/null
+++ b/test/regression/bsd_test/netx_bsd_select_improvements_test.c
@@ -0,0 +1,316 @@
+/***************************************************************************
+ * Copyright (C) 2026 Eclipse ThreadX contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the MIT License which is available at
+ * https://opensource.org/licenses/MIT.
+ *
+ * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
+ * The AI-generated portions may be considered public domain (CC0-1.0)
+ * and not subject to the project's licence. The human contributor has
+ * reviewed and verified that the code is correct.
+ *
+ * SPDX-License-Identifier: MIT and CC0-1.0
+ **************************************************************************/
+
+/* This test covers select() improvements from PR #375:
+ *
+ * 1. TCP readfds + exceptfds in one select() call — normal (non-urgent) data
+ * must set readfds but MUST NOT set exceptfds.
+ *
+ * 2. select() with zero timeout and no ready sockets must return 0 and must
+ * clear all output fd sets (TX_NO_EVENTS path).
+ *
+ * 3. A socket that is not in use must be reported in writefds (regression for
+ * the writefds "not in use" restore).
+ */
+
+#include "tx_api.h"
+#include "nx_api.h"
+#if defined(NX_BSD_ENABLE) && !defined(NX_DISABLE_IPV4)
+#include "nxd_bsd.h"
+
+#define DEMO_STACK_SIZE 4096
+#define BSD_THREAD_PRIORITY 2
+
+static TX_THREAD ntest_0;
+static TX_THREAD ntest_1;
+
+static NX_PACKET_POOL pool_0;
+static NX_IP ip_0;
+static NX_IP ip_1;
+static TX_SEMAPHORE sema_0;
+static TX_SEMAPHORE sema_1;
+
+static ULONG error_counter;
+
+static ULONG packet_pool_area[(512 + sizeof(NX_PACKET)) * 32 / 4];
+
+static void ntest_0_entry(ULONG thread_input);
+static void ntest_1_entry(ULONG thread_input);
+extern void test_control_return(UINT status);
+extern void _nx_ram_network_driver_256(struct NX_IP_DRIVER_STRUCT *driver_req);
+
+static ULONG bsd_thread_area[DEMO_STACK_SIZE / sizeof(ULONG)];
+
+#ifdef CTEST
+VOID test_application_define(void *first_unused_memory)
+#else
+void netx_bsd_select_improvements_test_application_define(void *first_unused_memory)
+#endif
+{
+CHAR *pointer = (CHAR *)first_unused_memory;
+UINT status;
+
+ error_counter = 0;
+
+ tx_thread_create(&ntest_0, "thread 0", ntest_0_entry, 0,
+ pointer, DEMO_STACK_SIZE, 3, 3,
+ TX_NO_TIME_SLICE, TX_AUTO_START);
+ pointer += DEMO_STACK_SIZE;
+
+ tx_thread_create(&ntest_1, "thread 1", ntest_1_entry, 0,
+ pointer, DEMO_STACK_SIZE, 3, 3,
+ TX_NO_TIME_SLICE, TX_AUTO_START);
+ pointer += DEMO_STACK_SIZE;
+
+ nx_system_initialize();
+
+ status = nx_packet_pool_create(&pool_0, "NetX Main Packet Pool", 512,
+ packet_pool_area, sizeof(packet_pool_area));
+ if (status)
+ error_counter++;
+
+ status = nx_ip_create(&ip_0, "NetX IP Instance 0",
+ IP_ADDRESS(1, 2, 3, 4), 0xFFFFFF00UL,
+ &pool_0, _nx_ram_network_driver_256,
+ pointer, 2048, 1);
+ pointer += 2048;
+ if (status)
+ error_counter++;
+
+ status = nx_ip_create(&ip_1, "NetX IP Instance 1",
+ IP_ADDRESS(1, 2, 3, 5), 0xFFFFFF00UL,
+ &pool_0, _nx_ram_network_driver_256,
+ pointer, 2048, 1);
+ pointer += 2048;
+ if (status)
+ error_counter++;
+
+ status = nx_arp_enable(&ip_0, (void *)pointer, 1024);
+ pointer += 1024;
+ if (status)
+ error_counter++;
+
+ status = nx_arp_enable(&ip_1, (void *)pointer, 1024);
+ pointer += 1024;
+ if (status)
+ error_counter++;
+
+ status = nx_tcp_enable(&ip_0);
+ status += nx_tcp_enable(&ip_1);
+ if (status)
+ error_counter++;
+
+ status = bsd_initialize(&ip_0, &pool_0, (CHAR *)&bsd_thread_area[0],
+ sizeof(bsd_thread_area), BSD_THREAD_PRIORITY);
+ if (status)
+ error_counter++;
+
+ status = tx_semaphore_create(&sema_0, "SEMA 0", 0);
+ status += tx_semaphore_create(&sema_1, "SEMA 1", 0);
+ if (status)
+ error_counter++;
+}
+
+/* Server thread: exercises select() with readfds + exceptfds. */
+static void ntest_0_entry(ULONG thread_input)
+{
+struct sockaddr_in server_addr;
+struct sockaddr_in client_addr;
+INT server_sock;
+INT conn_sock;
+INT client_addr_len;
+INT n;
+INT nfd;
+struct timeval tv;
+fd_set readfds;
+fd_set writefds;
+fd_set exceptfds;
+char buf[64];
+
+ NX_PARAMETER_NOT_USED(thread_input);
+
+ /* ================================================================
+ * Set up TCP server socket.
+ * ================================================================ */
+ server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (server_sock < 0)
+ error_counter++;
+
+ memset(&server_addr, 0, sizeof(server_addr));
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(5555);
+ server_addr.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
+ error_counter++;
+
+ if (listen(server_sock, 1) < 0)
+ error_counter++;
+
+ /* Signal client thread to connect. */
+ tx_semaphore_put(&sema_1);
+
+ /* Accept the connection. */
+ client_addr_len = sizeof(client_addr);
+ conn_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_len);
+ if (conn_sock < 0)
+ error_counter++;
+
+ /* ================================================================
+ * Test 1: zero-timeout select with no data ready must return 0
+ * and clear all output fd sets.
+ * ================================================================ */
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ FD_SET(conn_sock, &readfds);
+ FD_SET(conn_sock, &exceptfds);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ nfd = conn_sock + 1;
+
+ n = select(nfd, &readfds, &writefds, &exceptfds, &tv);
+
+ if (n != 0)
+ error_counter++;
+
+ /* All sets must be cleared when select returns 0 (timeout). */
+ if (FD_ISSET(conn_sock, &readfds))
+ error_counter++;
+ if (FD_ISSET(conn_sock, &exceptfds))
+ error_counter++;
+
+ /* ================================================================
+ * Test 2: normal TCP data must set readfds and NOT exceptfds.
+ * ================================================================ */
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ FD_SET(conn_sock, &readfds);
+ FD_SET(conn_sock, &exceptfds);
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ /* Tell the client we are ready to receive. */
+ tx_semaphore_put(&sema_0);
+
+ n = select(nfd, &readfds, NX_NULL, &exceptfds, &tv);
+
+ if (n <= 0)
+ error_counter++;
+
+ /* readfds must be set (data arrived). */
+ if (!FD_ISSET(conn_sock, &readfds))
+ error_counter++;
+
+ /* exceptfds must NOT be set (no urgent data). */
+ if (FD_ISSET(conn_sock, &exceptfds))
+ error_counter++;
+
+ /* Consume the data. */
+ recv(conn_sock, buf, sizeof(buf), 0);
+
+ /* ================================================================
+ * Test 3: a socket that is not in use must appear in writefds.
+ * ================================================================ */
+ {
+ INT closed_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ INT open_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ INT max_fd;
+
+ if (closed_sock < 0 || open_sock < 0)
+ error_counter++;
+
+ /* Close one socket so its slot is free. */
+ soc_close(closed_sock);
+
+ FD_ZERO(&writefds);
+ FD_SET(closed_sock, &writefds);
+ FD_SET(open_sock, &writefds);
+
+ max_fd = (closed_sock > open_sock ? closed_sock : open_sock) + 1;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ n = select(max_fd, NX_NULL, &writefds, NX_NULL, &tv);
+
+ /* The not-in-use socket must be reported writable. */
+ if (!FD_ISSET(closed_sock, &writefds))
+ error_counter++;
+
+ soc_close(open_sock);
+ }
+
+ soc_close(conn_sock);
+ soc_close(server_sock);
+
+ if (error_counter)
+ test_control_return(1);
+ else
+ test_control_return(0);
+}
+
+/* Client thread: connects and sends normal (non-urgent) TCP data. */
+static void ntest_1_entry(ULONG thread_input)
+{
+struct sockaddr_in server_addr;
+INT client_sock;
+ULONG actual_status;
+
+ NX_PARAMETER_NOT_USED(thread_input);
+
+ nx_ip_status_check(&ip_1, NX_IP_INITIALIZE_DONE, &actual_status,
+ 5 * NX_IP_PERIODIC_RATE);
+
+ /* Wait for server to be ready. */
+ tx_semaphore_get(&sema_1, 5 * NX_IP_PERIODIC_RATE);
+
+ client_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (client_sock < 0)
+ error_counter++;
+
+ memset(&server_addr, 0, sizeof(server_addr));
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(5555);
+ server_addr.sin_addr.s_addr = htonl(IP_ADDRESS(1, 2, 3, 4));
+
+ if (connect(client_sock, (struct sockaddr *)&server_addr,
+ sizeof(server_addr)) < 0)
+ error_counter++;
+
+ /* Wait for server to enter select(). */
+ tx_semaphore_get(&sema_0, 5 * NX_IP_PERIODIC_RATE);
+
+ /* Send normal (non-urgent) data. */
+ if (send(client_sock, "Hello", 5, 0) < 0)
+ error_counter++;
+
+ soc_close(client_sock);
+}
+
+#else
+
+#ifdef CTEST
+VOID test_application_define(void *first_unused_memory)
+#else
+void netx_bsd_select_improvements_test_application_define(void *first_unused_memory)
+#endif
+{
+ NX_PARAMETER_NOT_USED(first_unused_memory);
+ test_control_return(3);
+}
+
+#endif /* NX_BSD_ENABLE && !NX_DISABLE_IPV4 */