This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit ec86ec797fa7cc850f92af5a3005f7db621fb85e Author: Marco Casaroli <[email protected]> AuthorDate: Thu Nov 5 11:30:29 2026 +0100 testing/nettest: Test what must not change for datagram sockets. Add to cmocka_net_local_dgram the cases that work without connect() and poll() of a datagram socket, so that a change to these sockets shows what it breaks. The datagram cases use neither connect() nor poll(): - bind() errors, getsockname(), sendto(), recv() and recvfrom(), datagram limits and order, short reads, datagrams with no data or too large, MSG_DONTWAIT, MSG_PEEK, sendmsg() and recvmsg(). - Stream sockets: names, connect(AF_UNSPEC), two threads that send on one socket, and POLLHUP. - Pipes and FIFOs: POLLHUP and POLLERR. The second optional argument of the program is a cmocka filter, to run some of the cases. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- testing/nettest/CMakeLists.txt | 3 +- testing/nettest/Kconfig | 10 +- testing/nettest/Makefile | 3 +- testing/nettest/local/test_local_dgram.c | 29 +- testing/nettest/local/test_local_dgram.h | 65 ++ testing/nettest/local/test_local_dgram_common.c | 149 +++++ testing/nettest/local/test_local_dgram_regress.c | 771 +++++++++++++++++++++++ 7 files changed, 1023 insertions(+), 7 deletions(-) diff --git a/testing/nettest/CMakeLists.txt b/testing/nettest/CMakeLists.txt index 9a679f494..12b2b1256 100644 --- a/testing/nettest/CMakeLists.txt +++ b/testing/nettest/CMakeLists.txt @@ -78,7 +78,8 @@ if(CONFIG_TESTING_NET_TEST) ${CMAKE_CURRENT_LIST_DIR}/local/test_local_dgram.c ${CMAKE_CURRENT_LIST_DIR}/local/test_local_dgram_common.c ${CMAKE_CURRENT_LIST_DIR}/local/test_local_dgram_connect.c - ${CMAKE_CURRENT_LIST_DIR}/local/test_local_dgram_poll.c) + ${CMAKE_CURRENT_LIST_DIR}/local/test_local_dgram_poll.c + ${CMAKE_CURRENT_LIST_DIR}/local/test_local_dgram_regress.c) nuttx_add_application( NAME diff --git a/testing/nettest/Kconfig b/testing/nettest/Kconfig index 8d6c0a3b8..49ba29e74 100644 --- a/testing/nettest/Kconfig +++ b/testing/nettest/Kconfig @@ -46,10 +46,12 @@ config TESTING_NET_LOCAL_DGRAM depends on !DISABLE_PTHREAD && !DISABLE_POSIX_TIMERS default y ---help--- - Test connect(), send(), getpeername(), poll() and select() of - Unix domain datagram sockets (cmocka_net_local_dgram). The - optional argument of the program is a writable directory for - the socket names (default P_tmpdir). + Test connect(), send(), getpeername(), poll() and select() + of Unix domain datagram sockets, and that POLLHUP of pipes and + stream sockets stays (cmocka_net_local_dgram). The first optional + argument of the program is a writable directory for the socket + names (default P_tmpdir). The second is a cmocka filter, to run + some of the cases. config TESTING_NET_OTHERS bool "Enable cmocka net other test" diff --git a/testing/nettest/Makefile b/testing/nettest/Makefile index 642ba2009..6f4a35397 100644 --- a/testing/nettest/Makefile +++ b/testing/nettest/Makefile @@ -55,7 +55,8 @@ ifeq ($(CONFIG_TESTING_NET_LOCAL_DGRAM),y) MAINSRC += local/test_local_dgram.c PROGNAME += cmocka_net_local_dgram CSRCS += local/test_local_dgram_common.c local/test_local_dgram_connect.c \ - local/test_local_dgram_poll.c + local/test_local_dgram_poll.c \ + local/test_local_dgram_regress.c endif ifeq ($(CONFIG_TESTING_NET_OTHERS),y) diff --git a/testing/nettest/local/test_local_dgram.c b/testing/nettest/local/test_local_dgram.c index fe388ec49..f0e3339b4 100644 --- a/testing/nettest/local/test_local_dgram.c +++ b/testing/nettest/local/test_local_dgram.c @@ -51,7 +51,8 @@ * Name: main * * Description: - * The optional argument is a writable directory for the socket names. + * The first optional argument is a writable directory for the socket + * names. The second is a cmocka filter, to run some of the cases. * ****************************************************************************/ @@ -79,6 +80,27 @@ int main(int argc, FAR char *argv[]) DGRAM_TEST(test_local_dgram_notsock), DGRAM_TEST_CONNECTED(test_local_dgram_closed), DGRAM_TEST(test_local_dgram_abstract), + DGRAM_TEST(test_local_dgram_reg_inuse), + DGRAM_TEST(test_local_dgram_reg_names), + DGRAM_TEST(test_local_dgram_reg_sendrecv), + DGRAM_TEST(test_local_dgram_reg_source), + DGRAM_TEST(test_local_dgram_reg_order), + DGRAM_TEST(test_local_dgram_reg_trunc), + DGRAM_TEST(test_local_dgram_reg_zero), + DGRAM_TEST(test_local_dgram_reg_big), + DGRAM_TEST(test_local_dgram_reg_dontwait), + DGRAM_TEST(test_local_dgram_reg_peek), + DGRAM_TEST(test_local_dgram_reg_missing), + DGRAM_TEST(test_local_dgram_reg_unbound), + DGRAM_TEST(test_local_dgram_reg_self), + DGRAM_TEST(test_local_dgram_reg_msg), + DGRAM_TEST(test_local_dgram_reg_cycle), + DGRAM_TEST(test_local_dgram_stream_names), + DGRAM_TEST(test_local_dgram_stream_unspec), + DGRAM_TEST(test_local_dgram_stream_threads), + DGRAM_TEST(test_local_dgram_stream_hup), + DGRAM_TEST(test_local_dgram_pipe_hup), + DGRAM_TEST(test_local_dgram_fifo_hup), }; if (argc > 1) @@ -86,6 +108,11 @@ int main(int argc, FAR char *argv[]) g_local_dgram_dir = argv[1]; } + if (argc > 2) + { + cmocka_set_test_filter(argv[2]); + } + return cmocka_run_group_tests(local_dgram_tests, test_local_dgram_group_setup, test_local_dgram_group_teardown); diff --git a/testing/nettest/local/test_local_dgram.h b/testing/nettest/local/test_local_dgram.h index b17099a83..bae80fabd 100644 --- a/testing/nettest/local/test_local_dgram.h +++ b/testing/nettest/local/test_local_dgram.h @@ -29,6 +29,7 @@ #include <nuttx/compiler.h> +#include <pthread.h> #include <stdbool.h> #include <sys/socket.h> #include <sys/un.h> @@ -59,6 +60,28 @@ struct nettest_local_dgram_state_s int u; }; +/* One action of another thread, after a delay. A failed assert in the main + * thread does not stop this thread, so each case joins before it asserts. + */ + +struct local_dgram_job_s; +typedef int (*local_dgram_action_t)(FAR struct local_dgram_job_s *job); + +struct local_dgram_job_s +{ + local_dgram_action_t action; + FAR const char *path; + FAR const char *msg; + pthread_t target; + pthread_t thread; + int fd; + int ms; + int arg; + int ret; + int err; + int revents; +}; + /**************************************************************************** * Public Data ****************************************************************************/ @@ -108,6 +131,24 @@ void local_dgram_expect(int fd, FAR const char *want); void local_dgram_drain(int fd, FAR char *buf, size_t size); +/* The threads of the cases, and helpers */ + +void local_dgram_job_arg(FAR struct local_dgram_job_s *job, + local_dgram_action_t action, int fd, + FAR const char *path, int ms, int arg); +void local_dgram_job_start(FAR struct local_dgram_job_s *job, + local_dgram_action_t action, int fd, + FAR const char *path, int ms); +void local_dgram_job_join(FAR struct local_dgram_job_s *job); +int local_dgram_do_send(FAR struct local_dgram_job_s *job); +int local_dgram_do_sendto(FAR struct local_dgram_job_s *job); +int local_dgram_do_close(FAR struct local_dgram_job_s *job); +ssize_t local_dgram_sendto(int fd, FAR const char *path, + FAR const void *buf, size_t len, int flags); +int local_dgram_revents(int fd, int events, int ms); +void local_dgram_mkfifo(FAR char *path, size_t size, + FAR const char *first); + /**************************************************************************** * Name: test_local_dgram_group_setup ****************************************************************************/ @@ -258,4 +299,28 @@ void test_local_dgram_dontwait(FAR void **state); void test_local_dgram_nonblock(FAR void **state); +/* The cases of test_local_dgram_regress.c */ + +void test_local_dgram_reg_inuse(FAR void **state); +void test_local_dgram_reg_names(FAR void **state); +void test_local_dgram_reg_sendrecv(FAR void **state); +void test_local_dgram_reg_source(FAR void **state); +void test_local_dgram_reg_order(FAR void **state); +void test_local_dgram_reg_trunc(FAR void **state); +void test_local_dgram_reg_zero(FAR void **state); +void test_local_dgram_reg_big(FAR void **state); +void test_local_dgram_reg_dontwait(FAR void **state); +void test_local_dgram_reg_peek(FAR void **state); +void test_local_dgram_reg_missing(FAR void **state); +void test_local_dgram_reg_unbound(FAR void **state); +void test_local_dgram_reg_self(FAR void **state); +void test_local_dgram_reg_msg(FAR void **state); +void test_local_dgram_reg_cycle(FAR void **state); +void test_local_dgram_stream_names(FAR void **state); +void test_local_dgram_stream_unspec(FAR void **state); +void test_local_dgram_stream_threads(FAR void **state); +void test_local_dgram_stream_hup(FAR void **state); +void test_local_dgram_pipe_hup(FAR void **state); +void test_local_dgram_fifo_hup(FAR void **state); + #endif /* __APPS_TESTING_NETTEST_LOCAL_TEST_LOCAL_DGRAM_H */ diff --git a/testing/nettest/local/test_local_dgram_common.c b/testing/nettest/local/test_local_dgram_common.c index 1d527ddf2..475cc1766 100644 --- a/testing/nettest/local/test_local_dgram_common.c +++ b/testing/nettest/local/test_local_dgram_common.c @@ -26,6 +26,7 @@ #include <errno.h> #include <poll.h> +#include <pthread.h> #include <setjmp.h> #include <signal.h> #include <stdarg.h> @@ -36,6 +37,7 @@ #include <string.h> #include <time.h> #include <unistd.h> +#include <sys/stat.h> #include <cmocka.h> #include "test_local_dgram.h" @@ -58,6 +60,17 @@ FAR const char *g_local_dgram_dir = P_tmpdir; * Private Functions ****************************************************************************/ +static FAR void *local_dgram_job_main(FAR void *arg) +{ + FAR struct local_dgram_job_s *job = arg; + + usleep(job->ms * 1000); + errno = 0; + job->ret = job->action(job); + job->err = errno; + return NULL; +} + static void local_dgram_watchdog(int signo) { static const char msg[] = "cmocka_net_local_dgram: watchdog: " @@ -229,6 +242,142 @@ void local_dgram_drain(int fd, FAR char *buf, size_t size) } } +/**************************************************************************** + * Name: local_dgram_mkfifo + * + * Description: + * Make a FIFO. A FIFO cannot be inside a mounted file system (EPERM), so + * fall back to the pseudo file system. path gets the name that worked. + * + ****************************************************************************/ + +void local_dgram_mkfifo(FAR char *path, size_t size, + FAR const char *first) +{ + int ret; + + strlcpy(path, first, size); + unlink(path); + ret = mkfifo(path, 0666); + if (ret < 0 && errno == EPERM) + { + snprintf(path, size, "/dev/dgramf-%d", getpid()); + unlink(path); + ret = mkfifo(path, 0666); + } + + assert_return_code(ret, errno); +} + +/**************************************************************************** + * Name: local_dgram_job_arg + * + * Description: + * Start another thread that does an action after ms milliseconds. + * + ****************************************************************************/ + +void local_dgram_job_arg(FAR struct local_dgram_job_s *job, + local_dgram_action_t action, int fd, + FAR const char *path, int ms, int arg) +{ + int ret; + + memset(job, 0, sizeof(*job)); + job->action = action; + job->fd = fd; + job->path = path; + job->ms = ms; + job->arg = arg; + job->msg = "later"; + job->target = pthread_self(); + ret = pthread_create(&job->thread, NULL, local_dgram_job_main, job); + assert_int_equal(ret, 0); +} + +/**************************************************************************** + * Name: local_dgram_job_start + ****************************************************************************/ + +void local_dgram_job_start(FAR struct local_dgram_job_s *job, + local_dgram_action_t action, int fd, + FAR const char *path, int ms) +{ + local_dgram_job_arg(job, action, fd, path, ms, 0); +} + +/**************************************************************************** + * Name: local_dgram_job_join + ****************************************************************************/ + +void local_dgram_job_join(FAR struct local_dgram_job_s *job) +{ + pthread_join(job->thread, NULL); +} + +/**************************************************************************** + * Name: local_dgram_sendto + ****************************************************************************/ + +ssize_t local_dgram_sendto(int fd, FAR const char *path, + FAR const void *buf, size_t len, int flags) +{ + struct sockaddr_un addr; + socklen_t addrlen = local_dgram_addr(&addr, path, false); + + return sendto(fd, buf, len, flags, (FAR struct sockaddr *)&addr, addrlen); +} + +/**************************************************************************** + * Name: local_dgram_do_send + ****************************************************************************/ + +int local_dgram_do_send(FAR struct local_dgram_job_s *job) +{ + return send(job->fd, job->msg, strlen(job->msg), 0); +} + +/**************************************************************************** + * Name: local_dgram_do_sendto + ****************************************************************************/ + +int local_dgram_do_sendto(FAR struct local_dgram_job_s *job) +{ + return local_dgram_sendto(job->fd, job->path, job->msg, strlen(job->msg), + 0); +} + +/**************************************************************************** + * Name: local_dgram_do_close + ****************************************************************************/ + +int local_dgram_do_close(FAR struct local_dgram_job_s *job) +{ + return close(job->fd); +} + +/**************************************************************************** + * Name: local_dgram_revents + * + * Description: + * Return revents of one poll() and check that the result agrees with it. + * + ****************************************************************************/ + +int local_dgram_revents(int fd, int events, int ms) +{ + struct pollfd pfd; + int ret; + + pfd.fd = fd; + pfd.events = events; + pfd.revents = 0; + ret = poll(&pfd, 1, ms); + assert_return_code(ret, errno); + assert_int_equal(ret, pfd.revents != 0 ? 1 : 0); + return pfd.revents; +} + /**************************************************************************** * Name: test_local_dgram_group_setup ****************************************************************************/ diff --git a/testing/nettest/local/test_local_dgram_regress.c b/testing/nettest/local/test_local_dgram_regress.c new file mode 100644 index 000000000..3dfbc498d --- /dev/null +++ b/testing/nettest/local/test_local_dgram_regress.c @@ -0,0 +1,771 @@ +/**************************************************************************** + * apps/testing/nettest/local/test_local_dgram_regress.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/* What works without connect() and poll() of a datagram socket: bind(), + * sendto() and recv() of datagram sockets, and POLLHUP of pipes, FIFOs and + * stream sockets. The datagram cases in this file call neither connect() + * nor poll(). + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <pthread.h> +#include <setjmp.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <cmocka.h> + +#include "test_local_dgram.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LOCAL_DGRAM_RECLEN 32 +#define LOCAL_DGRAM_RECNUM 50 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/* Wait for one datagram with recv() only */ + +static void local_dgram_recvstr(int fd, FAR const char *want) +{ + char buf[128]; + ssize_t n; + + n = recv(fd, buf, sizeof(buf) - 1, 0); + assert_return_code(n, errno); + buf[n] = '\0'; + assert_string_equal(buf, want); +} + +/* recv(MSG_DONTWAIT) must find nothing */ + +static void local_dgram_recvnone(int fd) +{ + char buf[16]; + + errno = 0; + assert_int_equal(recv(fd, buf, sizeof(buf), MSG_DONTWAIT), -1); + assert_true(errno == EAGAIN || errno == EWOULDBLOCK); +} + +#ifdef CONFIG_NET_LOCAL_STREAM +static int local_dgram_do_streamconn(FAR struct local_dgram_job_s *job) +{ + struct sockaddr_un addr; + socklen_t addrlen = local_dgram_addr(&addr, job->path, false); + + return connect(job->fd, (FAR struct sockaddr *)&addr, addrlen); +} + +/* Send records of one letter on a stream socket */ + +static int local_dgram_do_records(FAR struct local_dgram_job_s *job) +{ + char buf[LOCAL_DGRAM_RECLEN]; + int i; + + memset(buf, job->arg, sizeof(buf)); + for (i = 0; i < LOCAL_DGRAM_RECNUM; i++) + { + if (send(job->fd, buf, sizeof(buf), 0) != sizeof(buf)) + { + return -1; + } + } + + return 0; +} + +/* Make a connected pair of stream sockets: u, the client, and r2, the + * accepted socket. r3 is the listener. + */ + +static void +local_dgram_streampair(FAR struct nettest_local_dgram_state_s *st) +{ + struct local_dgram_job_s job; + struct sockaddr_un addr; + socklen_t addrlen; + int ret; + + addrlen = local_dgram_addr(&addr, st->p3, false); + unlink(st->p3); + + st->r3 = socket(AF_UNIX, SOCK_STREAM, 0); + assert_return_code(st->r3, errno); + ret = bind(st->r3, (FAR struct sockaddr *)&addr, addrlen); + assert_return_code(ret, errno); + ret = listen(st->r3, 2); + assert_return_code(ret, errno); + + st->u = socket(AF_UNIX, SOCK_STREAM, 0); + assert_return_code(st->u, errno); + + local_dgram_job_start(&job, local_dgram_do_streamconn, st->u, st->p3, 0); + st->r2 = accept(st->r3, NULL, NULL); + local_dgram_job_join(&job); + assert_return_code(st->r2, errno); + assert_int_equal(job.ret, 0); +} +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: test_local_dgram_reg_inuse + * + * Description: + * bind() to a name that is in use fails with EADDRINUSE. + * + ****************************************************************************/ + +void test_local_dgram_reg_inuse(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr_un addr; + socklen_t addrlen = local_dgram_addr(&addr, st->p1, false); + + errno = 0; + assert_int_equal(bind(st->s, (FAR struct sockaddr *)&addr, addrlen), -1); + assert_int_equal(errno, EADDRINUSE); + + /* The socket is not changed by the error */ + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "free", 4, 0), 4); + local_dgram_recvstr(st->r1, "free"); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_names + * + * Description: + * getsockname() of a bound socket gives its name, and of an unbound socket + * gives no name. + * + ****************************************************************************/ + +void test_local_dgram_reg_names(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr_un addr; + socklen_t addrlen; + int ret; + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = getsockname(st->r1, (FAR struct sockaddr *)&addr, &addrlen); + assert_return_code(ret, errno); + assert_int_equal(addr.sun_family, AF_UNIX); + assert_string_equal(addr.sun_path, st->p1); + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = getsockname(st->s, (FAR struct sockaddr *)&addr, &addrlen); + assert_return_code(ret, errno); + assert_int_equal(addr.sun_family, AF_UNIX); + assert_true(addrlen <= sizeof(sa_family_t) || addr.sun_path[0] == '\0'); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_sendrecv + * + * Description: + * sendto() and recv(), with the datagram first and with recv() first. + * + ****************************************************************************/ + +void test_local_dgram_reg_sendrecv(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "first", 5, 0), 5); + local_dgram_recvstr(st->r1, "first"); + + local_dgram_job_start(&job, local_dgram_do_sendto, st->s, st->p1, 100); + local_dgram_recvstr(st->r1, "later"); + local_dgram_job_join(&job); + assert_int_equal(job.ret, 5); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_source + * + * Description: + * recvfrom() gives no name for an unbound sender. + * + ****************************************************************************/ + +void test_local_dgram_reg_source(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr_un addr; + socklen_t addrlen; + char buf[16]; + ssize_t ret; + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "a", 1, 0), 1); + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = recvfrom(st->r1, buf, sizeof(buf), 0, (FAR struct sockaddr *)&addr, + &addrlen); + assert_int_equal(ret, 1); + assert_true(addrlen <= sizeof(sa_family_t) || addr.sun_path[0] == '\0'); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_order + * + * Description: + * Datagrams keep their order and their limits. + * + ****************************************************************************/ + +void test_local_dgram_reg_order(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "one", 3, 0), 3); + assert_int_equal(local_dgram_sendto(st->s, st->p1, "two", 3, 0), 3); + assert_int_equal(local_dgram_sendto(st->s, st->p1, "three", 5, 0), 5); + local_dgram_recvstr(st->r1, "one"); + local_dgram_recvstr(st->r1, "two"); + local_dgram_recvstr(st->r1, "three"); + local_dgram_recvnone(st->r1); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_trunc + * + * Description: + * A short recv() discards the rest of the datagram. + * + ****************************************************************************/ + +void test_local_dgram_reg_trunc(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + char big[64]; + char buf[4]; + + memset(big, 'x', sizeof(big)); + assert_int_equal(local_dgram_sendto(st->s, st->p1, big, sizeof(big), 0), + sizeof(big)); + assert_int_equal(local_dgram_sendto(st->s, st->p1, "next", 4, 0), 4); + assert_int_equal(recv(st->r1, buf, sizeof(buf), 0), sizeof(buf)); + local_dgram_recvstr(st->r1, "next"); + local_dgram_recvnone(st->r1); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_zero + * + * Description: + * A datagram with no data: recv() returns 0. + * + ****************************************************************************/ + +void test_local_dgram_reg_zero(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + char buf[16]; + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "", 0, 0), 0); + assert_int_equal(recv(st->r1, buf, sizeof(buf), 0), 0); + assert_int_equal(local_dgram_sendto(st->s, st->p1, "next", 4, 0), 4); + local_dgram_recvstr(st->r1, "next"); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_big + * + * Description: + * A datagram too large for the receiver leaves the next datagram whole. + * + ****************************************************************************/ + +void test_local_dgram_reg_big(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + ssize_t ret; + + ret = local_dgram_sendto(st->s, st->p1, st->big, LOCAL_DGRAM_BIGLEN, 0); + if (ret < 0) + { + assert_int_equal(errno, EMSGSIZE); + } + else + { + ret = recv(st->r1, st->big, LOCAL_DGRAM_BIGLEN, 0); + assert_int_equal(ret, LOCAL_DGRAM_BIGLEN); + } + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "next", 4, 0), 4); + local_dgram_recvstr(st->r1, "next"); + local_dgram_recvnone(st->r1); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_dontwait + * + * Description: + * recv(MSG_DONTWAIT) takes a datagram that waits. + * + ****************************************************************************/ + +void test_local_dgram_reg_dontwait(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + char buf[16]; + + local_dgram_recvnone(st->r1); + assert_int_equal(local_dgram_sendto(st->s, st->p1, "dw", 2, 0), 2); + assert_int_equal(recv(st->r1, buf, sizeof(buf), MSG_DONTWAIT), 2); + local_dgram_recvnone(st->r1); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_peek + * + * Description: + * recv(MSG_PEEK) leaves the datagram. + * + ****************************************************************************/ + +void test_local_dgram_reg_peek(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + char buf[16]; + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "peek", 4, 0), 4); + assert_int_equal(recv(st->r1, buf, sizeof(buf), MSG_PEEK), 4); + local_dgram_recvstr(st->r1, "peek"); + local_dgram_recvnone(st->r1); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_missing + * + * Description: + * sendto() of a name with no socket fails, and send() with no address + * fails. + * + ****************************************************************************/ + +void test_local_dgram_reg_missing(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + errno = 0; + assert_int_equal(local_dgram_sendto(st->s, st->p2, "x", 1, 0), -1); + assert_int_equal(errno, ENOENT); + + errno = 0; + assert_int_equal(send(st->s, "x", 1, 0), -1); + assert_true(errno == EDESTADDRREQ || errno == ENOTCONN); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_unbound + * + * Description: + * recv(MSG_DONTWAIT) of an unbound socket returns an error and does not + * wait. + * + ****************************************************************************/ + +void test_local_dgram_reg_unbound(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + char buf[16]; + + assert_int_equal(recv(st->s, buf, sizeof(buf), MSG_DONTWAIT), -1); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_self + * + * Description: + * A socket sends to its own name. + * + ****************************************************************************/ + +void test_local_dgram_reg_self(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + assert_int_equal(local_dgram_sendto(st->r1, st->p1, "me", 2, 0), 2); + local_dgram_recvstr(st->r1, "me"); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_msg + * + * Description: + * sendmsg() with two parts makes one datagram. + * + ****************************************************************************/ + +void test_local_dgram_reg_msg(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr_un addr; + struct msghdr msg; + struct iovec iov[2]; + char part1[] = "ab"; + char part2[] = "cd"; + char buf[8]; + ssize_t ret; + + iov[0].iov_base = part1; + iov[0].iov_len = 2; + iov[1].iov_base = part2; + iov[1].iov_len = 2; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &addr; + msg.msg_namelen = local_dgram_addr(&addr, st->p1, false); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + ret = sendmsg(st->s, &msg, 0); + assert_int_equal(ret, 4); + + memset(buf, 0, sizeof(buf)); + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(buf) - 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iovlen = 1; + ret = recvmsg(st->r1, &msg, 0); + assert_int_equal(ret, 4); + assert_string_equal(buf, "abcd"); +} + +/**************************************************************************** + * Name: test_local_dgram_reg_cycle + * + * Description: + * Many sockets on one name: nothing stays taken. + * + ****************************************************************************/ + +void test_local_dgram_reg_cycle(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + int i; + + for (i = 0; i < 30; i++) + { + st->r2 = local_dgram_bind(st->p2, false); + assert_return_code(st->r2, errno); + assert_int_equal(local_dgram_sendto(st->s, st->p2, "c", 1, 0), 1); + local_dgram_recvstr(st->r2, "c"); + assert_int_equal(close(st->r2), 0); + st->r2 = -1; + } +} + +/**************************************************************************** + * Name: test_local_dgram_stream_names + * + * Description: + * An unbound stream client has no name, on either end of the connection. + * + ****************************************************************************/ + +void test_local_dgram_stream_names(FAR void **state) +{ +#ifdef CONFIG_NET_LOCAL_STREAM + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr_un addr; + socklen_t addrlen; + int ret; + + local_dgram_streampair(st); + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = getsockname(st->u, (FAR struct sockaddr *)&addr, &addrlen); + assert_return_code(ret, errno); + assert_true(addrlen <= sizeof(sa_family_t) || addr.sun_path[0] == '\0'); + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = getpeername(st->r2, (FAR struct sockaddr *)&addr, &addrlen); + assert_return_code(ret, errno); + assert_true(addrlen <= sizeof(sa_family_t) || addr.sun_path[0] == '\0'); + + memset(&addr, 0, sizeof(addr)); + addrlen = sizeof(addr); + ret = getpeername(st->u, (FAR struct sockaddr *)&addr, &addrlen); + assert_return_code(ret, errno); + assert_string_equal(addr.sun_path, st->p3); +#else + skip(); +#endif +} + +/**************************************************************************** + * Name: test_local_dgram_stream_unspec + * + * Description: + * connect(AF_UNSPEC) of a stream socket fails. + * + ****************************************************************************/ + +void test_local_dgram_stream_unspec(FAR void **state) +{ +#ifdef CONFIG_NET_LOCAL_STREAM + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr addr; + + st->u = socket(AF_UNIX, SOCK_STREAM, 0); + assert_return_code(st->u, errno); + + memset(&addr, 0, sizeof(addr)); + addr.sa_family = AF_UNSPEC; + assert_int_equal(connect(st->u, &addr, sizeof(addr)), -1); +#else + skip(); +#endif +} + +/**************************************************************************** + * Name: test_local_dgram_stream_threads + * + * Description: + * Two threads send() on one stream socket: each short record stays whole. + * + ****************************************************************************/ + +void test_local_dgram_stream_threads(FAR void **state) +{ +#ifdef CONFIG_NET_LOCAL_STREAM + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job[2]; + char buf[LOCAL_DGRAM_RECLEN]; + int total = 2 * LOCAL_DGRAM_RECNUM; + int bad = 0; + int got; + int i; + + local_dgram_streampair(st); + + local_dgram_job_arg(&job[0], local_dgram_do_records, st->u, NULL, 0, 'a'); + local_dgram_job_arg(&job[1], local_dgram_do_records, st->u, NULL, 0, 'b'); + + for (got = 0; got < total; got++) + { + size_t len = 0; + + while (len < sizeof(buf)) + { + ssize_t ret = recv(st->r2, buf + len, sizeof(buf) - len, 0); + + if (ret <= 0) + { + bad++; + break; + } + + len += ret; + } + + if (len < sizeof(buf)) + { + break; + } + + for (i = 1; i < LOCAL_DGRAM_RECLEN; i++) + { + if (buf[i] != buf[0]) + { + bad++; + break; + } + } + } + + local_dgram_job_join(&job[0]); + local_dgram_job_join(&job[1]); + assert_int_equal(job[0].ret, 0); + assert_int_equal(job[1].ret, 0); + assert_int_equal(bad, 0); + assert_int_equal(got, total); +#else + skip(); +#endif +} + +/**************************************************************************** + * Name: test_local_dgram_stream_hup + * + * Description: + * A stream socket gives POLLHUP when its peer closes: the data first, then + * recv() returns 0. + * + ****************************************************************************/ + +void test_local_dgram_stream_hup(FAR void **state) +{ +#ifdef CONFIG_NET_LOCAL_STREAM + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + char buf[4]; + int revents; + + local_dgram_streampair(st); + + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), 0); + assert_int_equal(local_dgram_revents(st->r2, POLLOUT, 0), POLLOUT); + + assert_int_equal(send(st->u, "s", 1, 0), 1); + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 1000), POLLIN); + assert_int_equal(recv(st->r2, buf, sizeof(buf), 0), 1); + + local_dgram_job_start(&job, local_dgram_do_close, st->u, NULL, 100); + revents = local_dgram_revents(st->r2, POLLIN, 3000); + local_dgram_job_join(&job); + st->u = -1; + assert_int_equal(job.ret, 0); + assert_int_not_equal(revents & POLLHUP, 0); + assert_int_equal(recv(st->r2, buf, sizeof(buf), 0), 0); +#else + skip(); +#endif +} + +/**************************************************************************** + * Name: test_local_dgram_pipe_hup + * + * Description: + * A pipe gives POLLHUP when the last writer closes and POLLERR when the + * last reader closes. + * + ****************************************************************************/ + +void test_local_dgram_pipe_hup(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + char buf[4]; + int fd[2]; + int revents; + int ret; + + /* The data comes first, and POLLHUP after it */ + + ret = pipe(fd); + assert_return_code(ret, errno); + st->r2 = fd[0]; + st->r3 = fd[1]; + + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), 0); + assert_int_equal(write(st->r3, "x", 1), 1); + assert_int_not_equal(local_dgram_revents(st->r2, POLLIN, 0) & POLLIN, 0); + assert_int_equal(close(st->r3), 0); + st->r3 = -1; + assert_int_not_equal(local_dgram_revents(st->r2, POLLIN, 0) & POLLIN, 0); + assert_int_equal(read(st->r2, buf, sizeof(buf)), 1); + assert_int_not_equal(local_dgram_revents(st->r2, POLLIN, 0) & POLLHUP, 0); + assert_int_equal(close(st->r2), 0); + st->r2 = -1; + + /* A close of the last writer wakes up poll() */ + + ret = pipe(fd); + assert_return_code(ret, errno); + st->r2 = fd[0]; + st->r3 = fd[1]; + + local_dgram_job_start(&job, local_dgram_do_close, st->r3, NULL, 100); + revents = local_dgram_revents(st->r2, POLLIN, 3000); + local_dgram_job_join(&job); + st->r3 = -1; + assert_int_equal(job.ret, 0); + assert_int_not_equal(revents & POLLHUP, 0); + assert_int_equal(close(st->r2), 0); + st->r2 = -1; + + /* The writer sees POLLERR when the last reader is gone */ + + ret = pipe(fd); + assert_return_code(ret, errno); + st->r2 = fd[0]; + st->r3 = fd[1]; + + assert_int_equal(close(st->r2), 0); + st->r2 = -1; + revents = local_dgram_revents(st->r3, POLLOUT, 0); + assert_int_not_equal(revents & POLLERR, 0); +} + +/**************************************************************************** + * Name: test_local_dgram_fifo_hup + * + * Description: + * A named FIFO gives POLLHUP when its last writer closes. + * + ****************************************************************************/ + +void test_local_dgram_fifo_hup(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + char path[LOCAL_DGRAM_PATHLEN]; + int revents; + + local_dgram_mkfifo(path, sizeof(path), st->pf); + + st->r2 = open(path, O_RDONLY | O_NONBLOCK); + assert_return_code(st->r2, errno); + st->r3 = open(path, O_WRONLY | O_NONBLOCK); + assert_return_code(st->r3, errno); + + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), 0); + + local_dgram_job_start(&job, local_dgram_do_close, st->r3, NULL, 100); + revents = local_dgram_revents(st->r2, POLLIN, 3000); + local_dgram_job_join(&job); + st->r3 = -1; + unlink(path); + assert_int_equal(job.ret, 0); + assert_int_not_equal(revents & POLLHUP, 0); +}
