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 659f682cbf12339e4e66f8aa41098351d7905231 Author: Marco Casaroli <[email protected]> AuthorDate: Thu Nov 5 11:30:29 2026 +0100 testing/nettest: Test more of connect() and poll() of datagram sockets. Add to cmocka_net_local_dgram: - connect() to an address with no name fails. - poll() reports only what is true and what the caller asks for: an unbound and a bound socket, POLLIN until the last datagram is read, short reads, MSG_PEEK, an array of descriptors, one socket twice, a dup() of a socket, an abstract name. - poll() wakes up for send() and sendto(), for a series of datagrams and in two threads at once. It keeps its timeout and ends with EINTR. - ppoll(), select() and pselect() with all sets, and epoll, also when a socket joins a set unbound or closes in a set. - Another thread calls bind() or connect() during a poll(). - A failed send and many poll() calls leave nothing behind. - Two senders and one receiver: each datagram comes out whole, once and in the order of its sender. - A FIFO with buffer policy 1 gives no POLLHUP when its last writer closes: each send to a datagram socket closes one. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- testing/nettest/CMakeLists.txt | 1 + testing/nettest/Kconfig | 2 +- testing/nettest/Makefile | 2 +- testing/nettest/local/test_local_dgram.c | 34 + testing/nettest/local/test_local_dgram.h | 37 + testing/nettest/local/test_local_dgram_pollext.c | 1364 ++++++++++++++++++++++ 6 files changed, 1438 insertions(+), 2 deletions(-) diff --git a/testing/nettest/CMakeLists.txt b/testing/nettest/CMakeLists.txt index 12b2b1256..8703b51ac 100644 --- a/testing/nettest/CMakeLists.txt +++ b/testing/nettest/CMakeLists.txt @@ -79,6 +79,7 @@ if(CONFIG_TESTING_NET_TEST) ${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_pollext.c ${CMAKE_CURRENT_LIST_DIR}/local/test_local_dgram_regress.c) nuttx_add_application( diff --git a/testing/nettest/Kconfig b/testing/nettest/Kconfig index 49ba29e74..b4e8a2f3d 100644 --- a/testing/nettest/Kconfig +++ b/testing/nettest/Kconfig @@ -46,7 +46,7 @@ config TESTING_NET_LOCAL_DGRAM depends on !DISABLE_PTHREAD && !DISABLE_POSIX_TIMERS default y ---help--- - Test connect(), send(), getpeername(), poll() and select() + Test connect(), send(), getpeername(), poll(), select() and epoll 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 diff --git a/testing/nettest/Makefile b/testing/nettest/Makefile index 6f4a35397..31dd32261 100644 --- a/testing/nettest/Makefile +++ b/testing/nettest/Makefile @@ -55,7 +55,7 @@ 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_pollext.c \ local/test_local_dgram_regress.c endif diff --git a/testing/nettest/local/test_local_dgram.c b/testing/nettest/local/test_local_dgram.c index f0e3339b4..df464786c 100644 --- a/testing/nettest/local/test_local_dgram.c +++ b/testing/nettest/local/test_local_dgram.c @@ -80,6 +80,40 @@ 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_connect_empty), + DGRAM_TEST(test_local_dgram_poll_unbound), + DGRAM_TEST(test_local_dgram_poll_bound), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_level), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_trunc), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_peek), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_mask), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_fds), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_same), + DGRAM_TEST(test_local_dgram_poll_sendto), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_many), + DGRAM_TEST(test_local_dgram_poll_timeout), + DGRAM_TEST(test_local_dgram_poll_eintr), + DGRAM_TEST(test_local_dgram_ppoll), + DGRAM_TEST_CONNECTED(test_local_dgram_select_rw), + DGRAM_TEST_CONNECTED(test_local_dgram_epoll), + DGRAM_TEST(test_local_dgram_epoll_bind), + DGRAM_TEST(test_local_dgram_epoll_close), + DGRAM_TEST(test_local_dgram_poll_bindwait), + DGRAM_TEST(test_local_dgram_poll_connwait), + DGRAM_TEST(test_local_dgram_poll_unspecwait), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_closepeer), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_closesend), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_threads), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_slots), + DGRAM_TEST(test_local_dgram_poll_cycle), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_big), + DGRAM_TEST(test_local_dgram_poll_abstract), + DGRAM_TEST(test_local_dgram_poll_self), + DGRAM_TEST(test_local_dgram_poll_nonblock), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_dup), + DGRAM_TEST_CONNECTED(test_local_dgram_poll_sendmsg), + DGRAM_TEST(test_local_dgram_poll_stress), + DGRAM_TEST(test_local_dgram_fifo_policy), DGRAM_TEST(test_local_dgram_reg_inuse), DGRAM_TEST(test_local_dgram_reg_names), DGRAM_TEST(test_local_dgram_reg_sendrecv), diff --git a/testing/nettest/local/test_local_dgram.h b/testing/nettest/local/test_local_dgram.h index bae80fabd..9d6e995b6 100644 --- a/testing/nettest/local/test_local_dgram.h +++ b/testing/nettest/local/test_local_dgram.h @@ -299,6 +299,43 @@ void test_local_dgram_dontwait(FAR void **state); void test_local_dgram_nonblock(FAR void **state); +/* The cases of test_local_dgram_pollext.c */ + +void test_local_dgram_connect_empty(FAR void **state); +void test_local_dgram_poll_unbound(FAR void **state); +void test_local_dgram_poll_bound(FAR void **state); +void test_local_dgram_poll_level(FAR void **state); +void test_local_dgram_poll_trunc(FAR void **state); +void test_local_dgram_poll_peek(FAR void **state); +void test_local_dgram_poll_mask(FAR void **state); +void test_local_dgram_poll_fds(FAR void **state); +void test_local_dgram_poll_same(FAR void **state); +void test_local_dgram_poll_sendto(FAR void **state); +void test_local_dgram_poll_many(FAR void **state); +void test_local_dgram_poll_timeout(FAR void **state); +void test_local_dgram_poll_eintr(FAR void **state); +void test_local_dgram_ppoll(FAR void **state); +void test_local_dgram_select_rw(FAR void **state); +void test_local_dgram_epoll(FAR void **state); +void test_local_dgram_epoll_bind(FAR void **state); +void test_local_dgram_epoll_close(FAR void **state); +void test_local_dgram_poll_bindwait(FAR void **state); +void test_local_dgram_poll_connwait(FAR void **state); +void test_local_dgram_poll_unspecwait(FAR void **state); +void test_local_dgram_poll_closepeer(FAR void **state); +void test_local_dgram_poll_closesend(FAR void **state); +void test_local_dgram_poll_threads(FAR void **state); +void test_local_dgram_poll_slots(FAR void **state); +void test_local_dgram_poll_cycle(FAR void **state); +void test_local_dgram_poll_big(FAR void **state); +void test_local_dgram_poll_abstract(FAR void **state); +void test_local_dgram_poll_self(FAR void **state); +void test_local_dgram_poll_nonblock(FAR void **state); +void test_local_dgram_poll_dup(FAR void **state); +void test_local_dgram_poll_sendmsg(FAR void **state); +void test_local_dgram_poll_stress(FAR void **state); +void test_local_dgram_fifo_policy(FAR void **state); + /* The cases of test_local_dgram_regress.c */ void test_local_dgram_reg_inuse(FAR void **state); diff --git a/testing/nettest/local/test_local_dgram_pollext.c b/testing/nettest/local/test_local_dgram_pollext.c new file mode 100644 index 000000000..7f0dd998e --- /dev/null +++ b/testing/nettest/local/test_local_dgram_pollext.c @@ -0,0 +1,1364 @@ +/**************************************************************************** + * apps/testing/nettest/local/test_local_dgram_pollext.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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <pthread.h> +#include <setjmp.h> +#include <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/epoll.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/uio.h> +#include <cmocka.h> + +#include "test_local_dgram.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define LOCAL_DGRAM_STRESSLEN 64 +#define LOCAL_DGRAM_STRESSNUM 100 +#define LOCAL_DGRAM_STRESSTIME 120 + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int local_dgram_do_bind(FAR struct local_dgram_job_s *job) +{ + struct sockaddr_un addr; + socklen_t addrlen = local_dgram_addr(&addr, job->path, false); + + unlink(job->path); + return bind(job->fd, (FAR struct sockaddr *)&addr, addrlen); +} + +static int local_dgram_do_connect(FAR struct local_dgram_job_s *job) +{ + return local_dgram_connect_to(job->fd, job->path, false); +} + +static int local_dgram_do_unspec(FAR struct local_dgram_job_s *job) +{ + struct sockaddr addr; + + memset(&addr, 0, sizeof(addr)); + addr.sa_family = AF_UNSPEC; + return connect(job->fd, &addr, sizeof(addr)); +} + +static int local_dgram_do_signal(FAR struct local_dgram_job_s *job) +{ + return pthread_kill(job->target, SIGUSR1); +} + +static int local_dgram_do_poll(FAR struct local_dgram_job_s *job) +{ + struct pollfd pfd; + int ret; + + pfd.fd = job->fd; + pfd.events = POLLIN; + pfd.revents = 0; + ret = poll(&pfd, 1, job->arg); + job->revents = pfd.revents; + return ret; +} + +/* Send 20 datagrams, 20 ms apart */ + +static int local_dgram_do_send20(FAR struct local_dgram_job_s *job) +{ + char buf[16]; + int i; + + for (i = 0; i < 20; i++) + { + snprintf(buf, sizeof(buf), "m%d", i); + if (send(job->fd, buf, strlen(buf), 0) < 0) + { + return -1; + } + + usleep(20000); + } + + return 0; +} + +/* Send numbered datagrams of different lengths to one receiver */ + +static int local_dgram_do_stress(FAR struct local_dgram_job_s *job) +{ + unsigned char buf[LOCAL_DGRAM_STRESSLEN]; + size_t len; + int i; + + for (i = 0; i < LOCAL_DGRAM_STRESSNUM; i++) + { + len = 4 + i % (sizeof(buf) - 4); + buf[0] = job->arg; + buf[1] = i; + buf[2] = len; + buf[3] = 0; + memset(buf + 4, job->arg + i, len - 4); + if (local_dgram_sendto(job->fd, job->path, buf, len, 0) < 0) + { + return -1; + } + } + + return 0; +} + +static void local_dgram_nosignal(int signo) +{ +} + +static long local_dgram_msec(FAR const struct timespec *t0) +{ + struct timespec t1; + + clock_gettime(CLOCK_MONOTONIC, &t1); + return (t1.tv_sec - t0->tv_sec) * 1000 + + (t1.tv_nsec - t0->tv_nsec) / 1000000; +} + +static int local_dgram_epwait(int epfd, int ms, FAR uint32_t *events) +{ + struct epoll_event ev; + int ret; + + memset(&ev, 0, sizeof(ev)); + ret = epoll_wait(epfd, &ev, 1, ms); + assert_return_code(ret, errno); + *events = ret > 0 ? ev.events : 0; + return ret; +} + +static void local_dgram_epadd(int epfd, int fd, uint32_t events) +{ + struct epoll_event ev; + int ret; + + memset(&ev, 0, sizeof(ev)); + ev.events = events; + ev.data.fd = fd; + ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); + assert_return_code(ret, errno); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: test_local_dgram_connect_empty + * + * Description: + * connect() to an address with no name fails: it must not match an unbound + * socket. + * + ****************************************************************************/ + +void test_local_dgram_connect_empty(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr_un addr; + FAR struct sockaddr *sa = (FAR struct sockaddr *)&addr; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + assert_int_equal(connect(st->s, sa, sizeof(sa_family_t)), -1); + assert_int_equal(connect(st->s, sa, sizeof(sa_family_t) + 1), -1); + assert_int_equal(connect(st->s, sa, sizeof(addr)), -1); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_unbound + * + * Description: + * An unbound socket can send and cannot receive. + * + ****************************************************************************/ + +void test_local_dgram_poll_unbound(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + assert_int_equal(local_dgram_revents(st->s, POLLIN, 0), 0); + assert_int_equal(local_dgram_revents(st->s, POLLIN, 100), 0); + assert_int_equal(local_dgram_revents(st->s, POLLOUT, 0), POLLOUT); + assert_int_equal(local_dgram_revents(st->s, POLLIN | POLLOUT, 0), POLLOUT); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_bound + * + * Description: + * A bound socket with no datagram reports POLLOUT only. + * + ****************************************************************************/ + +void test_local_dgram_poll_bound(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); + assert_int_equal(local_dgram_revents(st->r1, POLLOUT, 0), POLLOUT); + assert_int_equal(local_dgram_revents(st->r1, POLLIN | POLLOUT, 0), + POLLOUT); + assert_int_equal(local_dgram_revents(st->r1, 0, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_level + * + * Description: + * POLLIN stays until the last datagram is read. + * + ****************************************************************************/ + +void test_local_dgram_poll_level(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + assert_int_equal(send(st->s, "one", 3, 0), 3); + assert_int_equal(send(st->s, "two", 3, 0), 3); + assert_int_equal(send(st->s, "three", 5, 0), 5); + + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), POLLIN); + local_dgram_expect(st->r1, "one"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), POLLIN); + local_dgram_expect(st->r1, "two"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), POLLIN); + local_dgram_expect(st->r1, "three"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_trunc + * + * Description: + * A short recv() discards the rest of the datagram, and the next datagram + * stays whole. + * + ****************************************************************************/ + +void test_local_dgram_poll_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(send(st->s, big, sizeof(big), 0), sizeof(big)); + assert_int_equal(send(st->s, "next", 4, 0), 4); + + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + assert_int_equal(recv(st->r1, buf, sizeof(buf), 0), sizeof(buf)); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), POLLIN); + local_dgram_expect(st->r1, "next"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_peek + * + * Description: + * recv(MSG_PEEK) leaves the datagram, so POLLIN stays. + * + ****************************************************************************/ + +void test_local_dgram_poll_peek(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + char buf[16]; + + assert_int_equal(send(st->s, "peek", 4, 0), 4); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + assert_int_equal(recv(st->r1, buf, sizeof(buf), MSG_PEEK), 4); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), POLLIN); + local_dgram_expect(st->r1, "peek"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_mask + * + * Description: + * poll() reports only the events that the caller asks for. + * + ****************************************************************************/ + +void test_local_dgram_poll_mask(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + assert_int_equal(send(st->s, "mask", 4, 0), 4); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + assert_int_equal(local_dgram_revents(st->r1, POLLOUT, 0), POLLOUT); + assert_int_equal(local_dgram_revents(st->r1, 0, 0), 0); + assert_int_equal(local_dgram_revents(st->r1, POLLIN | POLLOUT, 0), + POLLIN | POLLOUT); + assert_int_equal(local_dgram_revents(st->s, POLLIN, 0), 0); + local_dgram_expect(st->r1, "mask"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_fds + * + * Description: + * poll() of an array: a negative fd is skipped, and each socket gives its + * own events. + * + ****************************************************************************/ + +void test_local_dgram_poll_fds(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct pollfd pfd[4]; + int ret; + + st->r2 = local_dgram_bind(st->p2, false); + assert_return_code(st->r2, errno); + + assert_int_equal(send(st->s, "fds", 3, 0), 3); + assert_true(local_dgram_readable(st->r1, 1000)); + + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = -1; + pfd[0].events = POLLIN; + pfd[1].fd = st->r1; + pfd[1].events = POLLIN; + pfd[2].fd = st->s; + pfd[2].events = POLLOUT; + pfd[3].fd = st->r2; + pfd[3].events = POLLIN; + + ret = poll(pfd, 4, 0); + assert_return_code(ret, errno); + assert_int_equal(ret, 2); + assert_int_equal(pfd[0].revents, 0); + assert_int_equal(pfd[1].revents, POLLIN); + assert_int_equal(pfd[2].revents, POLLOUT); + assert_int_equal(pfd[3].revents, 0); + local_dgram_expect(st->r1, "fds"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_same + * + * Description: + * One socket twice in the array of one poll(). + * + ****************************************************************************/ + +void test_local_dgram_poll_same(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct pollfd pfd[2]; + int ret; + + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = st->r1; + pfd[0].events = POLLIN; + pfd[1].fd = st->r1; + pfd[1].events = POLLIN; + + ret = poll(pfd, 2, 0); + assert_return_code(ret, errno); + assert_int_equal(ret, 0); + + assert_int_equal(send(st->s, "same", 4, 0), 4); + assert_true(local_dgram_readable(st->r1, 1000)); + + ret = poll(pfd, 2, 0); + assert_return_code(ret, errno); + assert_int_equal(ret, 2); + assert_int_equal(pfd[0].revents, POLLIN); + assert_int_equal(pfd[1].revents, POLLIN); + local_dgram_expect(st->r1, "same"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_sendto + * + * Description: + * sendto() from a socket that is not connected wakes up poll(). + * + ****************************************************************************/ + +void test_local_dgram_poll_sendto(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + int revents; + + local_dgram_job_start(&job, local_dgram_do_sendto, st->s, st->p1, 100); + revents = local_dgram_revents(st->r1, POLLIN, 3000); + local_dgram_job_join(&job); + + assert_int_equal(job.ret, 5); + assert_int_equal(revents, POLLIN); + local_dgram_expect(st->r1, "later"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_many + * + * Description: + * poll() and recv() in a loop take a series of datagrams in order. + * + ****************************************************************************/ + +void test_local_dgram_poll_many(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + char want[16]; + char buf[16]; + int revents = 0; + ssize_t ret = 0; + int bad = -1; + int i; + + local_dgram_job_start(&job, local_dgram_do_send20, st->s, NULL, 0); + for (i = 0; i < 20 && bad < 0; i++) + { + struct pollfd pfd; + + pfd.fd = st->r1; + pfd.events = POLLIN; + pfd.revents = 0; + poll(&pfd, 1, 3000); + revents = pfd.revents; + if (revents != POLLIN) + { + bad = i; + break; + } + + ret = recv(st->r1, buf, sizeof(buf) - 1, 0); + snprintf(want, sizeof(want), "m%d", i); + if (ret != (ssize_t)strlen(want) || strncmp(buf, want, ret) != 0) + { + bad = i; + } + } + + local_dgram_job_join(&job); + assert_int_equal(job.ret, 0); + assert_int_equal(bad, -1); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_timeout + * + * Description: + * The timeout of poll(): 0 returns at once, a value waits, and -1 returns + * at once when a datagram waits. + * + ****************************************************************************/ + +void test_local_dgram_poll_timeout(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct timespec t0; + + clock_gettime(CLOCK_MONOTONIC, &t0); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); + assert_in_range(local_dgram_msec(&t0), 0, 150); + + clock_gettime(CLOCK_MONOTONIC, &t0); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 300), 0); + assert_in_range(local_dgram_msec(&t0), 250, 3000); + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "t", 1, 0), 1); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, -1), POLLIN); + local_dgram_expect(st->r1, "t"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_eintr + * + * Description: + * A signal ends poll() with EINTR, and the socket still works after that. + * + ****************************************************************************/ + +void test_local_dgram_poll_eintr(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + struct sigaction act; + struct sigaction old; + struct pollfd pfd; + int err; + int ret; + + memset(&act, 0, sizeof(act)); + act.sa_handler = local_dgram_nosignal; + sigemptyset(&act.sa_mask); + ret = sigaction(SIGUSR1, &act, &old); + assert_return_code(ret, errno); + + pfd.fd = st->r1; + pfd.events = POLLIN; + pfd.revents = 0; + + local_dgram_job_start(&job, local_dgram_do_signal, -1, NULL, 200); + ret = poll(&pfd, 1, 3000); + err = errno; + local_dgram_job_join(&job); + sigaction(SIGUSR1, &old, NULL); + + assert_int_equal(job.ret, 0); + assert_int_equal(ret, -1); + assert_int_equal(err, EINTR); + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "i", 1, 0), 1); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r1, "i"); +} + +/**************************************************************************** + * Name: test_local_dgram_ppoll + * + * Description: + * ppoll() with a timeout and a signal mask. + * + ****************************************************************************/ + +void test_local_dgram_ppoll(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct timespec ts; + struct pollfd pfd; + sigset_t mask; + int ret; + + sigemptyset(&mask); + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + pfd.fd = st->r1; + pfd.events = POLLIN; + pfd.revents = 0; + + ret = ppoll(&pfd, 1, &ts, &mask); + assert_return_code(ret, errno); + assert_int_equal(ret, 0); + + assert_int_equal(local_dgram_sendto(st->s, st->p1, "p", 1, 0), 1); + ts.tv_sec = 1; + ret = ppoll(&pfd, 1, &ts, NULL); + assert_return_code(ret, errno); + assert_int_equal(ret, 1); + assert_int_equal(pfd.revents, POLLIN); + local_dgram_expect(st->r1, "p"); +} + +/**************************************************************************** + * Name: test_local_dgram_select_rw + * + * Description: + * select() and pselect() with read, write and exception sets. + * + ****************************************************************************/ + +void test_local_dgram_select_rw(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + struct timespec ts; + struct timeval tv; + fd_set rset; + fd_set wset; + fd_set eset; + int nfds = (st->r1 > st->s ? st->r1 : st->s) + 1; + int ret; + + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_ZERO(&eset); + FD_SET(st->r1, &rset); + FD_SET(st->r1, &wset); + FD_SET(st->s, &wset); + FD_SET(st->r1, &eset); + tv.tv_sec = 0; + tv.tv_usec = 0; + ret = select(nfds, &rset, &wset, &eset, &tv); + assert_return_code(ret, errno); + assert_int_equal(ret, 2); + assert_false(FD_ISSET(st->r1, &rset)); + assert_true(FD_ISSET(st->r1, &wset)); + assert_true(FD_ISSET(st->s, &wset)); + assert_false(FD_ISSET(st->r1, &eset)); + + assert_int_equal(send(st->s, "rw", 2, 0), 2); + assert_true(local_dgram_readable(st->r1, 1000)); + + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(st->r1, &rset); + FD_SET(st->s, &wset); + ts.tv_sec = 0; + ts.tv_nsec = 0; + ret = pselect(nfds, &rset, &wset, NULL, &ts, NULL); + assert_return_code(ret, errno); + assert_int_equal(ret, 2); + assert_true(FD_ISSET(st->r1, &rset)); + assert_true(FD_ISSET(st->s, &wset)); + local_dgram_expect(st->r1, "rw"); + + /* A send wakes up select() */ + + FD_ZERO(&rset); + FD_SET(st->r1, &rset); + tv.tv_sec = 3; + tv.tv_usec = 0; + local_dgram_job_start(&job, local_dgram_do_send, st->s, NULL, 100); + ret = select(st->r1 + 1, &rset, NULL, NULL, &tv); + local_dgram_job_join(&job); + assert_return_code(ret, errno); + assert_int_equal(ret, 1); + assert_true(FD_ISSET(st->r1, &rset)); + local_dgram_expect(st->r1, "later"); +} + +/**************************************************************************** + * Name: test_local_dgram_epoll + * + * Description: + * epoll: level events, a wake up, EPOLLOUT and EPOLL_CTL_DEL. + * + ****************************************************************************/ + +void test_local_dgram_epoll(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + uint32_t events; + int ret; + + st->u = epoll_create1(EPOLL_CLOEXEC); + assert_return_code(st->u, errno); + local_dgram_epadd(st->u, st->r1, EPOLLIN); + + assert_int_equal(local_dgram_epwait(st->u, 0, &events), 0); + + assert_int_equal(send(st->s, "ep", 2, 0), 2); + assert_int_equal(local_dgram_epwait(st->u, 1000, &events), 1); + assert_int_equal(events, EPOLLIN); + assert_int_equal(local_dgram_epwait(st->u, 0, &events), 1); + assert_int_equal(events, EPOLLIN); + local_dgram_expect(st->r1, "ep"); + assert_int_equal(local_dgram_epwait(st->u, 0, &events), 0); + + local_dgram_job_start(&job, local_dgram_do_send, st->s, NULL, 100); + ret = local_dgram_epwait(st->u, 3000, &events); + local_dgram_job_join(&job); + assert_int_equal(ret, 1); + assert_int_equal(events, EPOLLIN); + local_dgram_expect(st->r1, "later"); + + ret = epoll_ctl(st->u, EPOLL_CTL_DEL, st->r1, NULL); + assert_return_code(ret, errno); + assert_int_equal(send(st->s, "ep", 2, 0), 2); + assert_true(local_dgram_readable(st->r1, 1000)); + assert_int_equal(local_dgram_epwait(st->u, 0, &events), 0); + local_dgram_expect(st->r1, "ep"); + + local_dgram_epadd(st->u, st->s, EPOLLOUT); + assert_int_equal(local_dgram_epwait(st->u, 1000, &events), 1); + assert_int_equal(events, EPOLLOUT); + ret = epoll_ctl(st->u, EPOLL_CTL_DEL, st->s, NULL); + assert_return_code(ret, errno); +} + +/**************************************************************************** + * Name: test_local_dgram_epoll_bind + * + * Description: + * A socket joins an epoll set unbound, is bound after that, and leaves the + * set. Setup saw no FIFO, and teardown sees one. + * + ****************************************************************************/ + +void test_local_dgram_epoll_bind(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + uint32_t events; + int ret; + + st->u = epoll_create1(EPOLL_CLOEXEC); + assert_return_code(st->u, errno); + local_dgram_epadd(st->u, st->s, EPOLLIN); + assert_int_equal(local_dgram_epwait(st->u, 0, &events), 0); + + local_dgram_job_start(&job, local_dgram_do_bind, st->s, st->p2, 0); + local_dgram_job_join(&job); + assert_int_equal(job.ret, 0); + + assert_int_equal(local_dgram_sendto(st->r1, st->p2, "b", 1, 0), 1); + ret = local_dgram_epwait(st->u, 100, &events); + assert_return_code(ret, errno); + + ret = epoll_ctl(st->u, EPOLL_CTL_DEL, st->s, NULL); + assert_return_code(ret, errno); + + /* The bound socket joins the set again and reports the datagram */ + + local_dgram_epadd(st->u, st->s, EPOLLIN); + assert_int_equal(local_dgram_epwait(st->u, 1000, &events), 1); + assert_int_equal(events, EPOLLIN); + local_dgram_expect(st->s, "b"); + ret = epoll_ctl(st->u, EPOLL_CTL_DEL, st->s, NULL); + assert_return_code(ret, errno); +} + +/**************************************************************************** + * Name: test_local_dgram_epoll_close + * + * Description: + * A socket is closed while it is in an epoll set. + * + ****************************************************************************/ + +void test_local_dgram_epoll_close(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + uint32_t events; + int ret; + + st->u = epoll_create1(EPOLL_CLOEXEC); + assert_return_code(st->u, errno); + st->r2 = local_dgram_bind(st->p2, false); + assert_return_code(st->r2, errno); + local_dgram_epadd(st->u, st->r2, EPOLLIN); + assert_int_equal(local_dgram_epwait(st->u, 0, &events), 0); + + ret = close(st->r2); + st->r2 = -1; + assert_return_code(ret, errno); + ret = local_dgram_epwait(st->u, 0, &events); + assert_return_code(ret, errno); + + ret = close(st->u); + st->u = -1; + assert_return_code(ret, errno); + + /* The name can be bound again */ + + st->r2 = local_dgram_bind(st->p2, false); + assert_return_code(st->r2, errno); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_bindwait + * + * Description: + * Another thread binds a socket while poll() waits on it. + * + ****************************************************************************/ + +void test_local_dgram_poll_bindwait(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + struct pollfd pfd; + int err; + int ret; + + pfd.fd = st->s; + pfd.events = POLLIN; + pfd.revents = 0; + local_dgram_job_start(&job, local_dgram_do_bind, st->s, st->p2, 100); + ret = poll(&pfd, 1, 500); + err = errno; + local_dgram_job_join(&job); + + assert_int_equal(job.ret, 0); + assert_return_code(ret, err); + + assert_int_equal(local_dgram_sendto(st->r1, st->p2, "bw", 2, 0), 2); + local_dgram_expect(st->s, "bw"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_connwait + * + * Description: + * Another thread connects a bound socket while poll() waits on it, and + * poll() of the connected socket still reports datagrams from its peer. + * + ****************************************************************************/ + +void test_local_dgram_poll_connwait(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + struct pollfd pfd; + int revents; + int err; + int ret; + + st->r2 = local_dgram_bind(st->p2, false); + assert_return_code(st->r2, errno); + + pfd.fd = st->r2; + pfd.events = POLLIN; + pfd.revents = 0; + local_dgram_job_start(&job, local_dgram_do_connect, st->r2, st->p1, 100); + ret = poll(&pfd, 1, 300); + err = errno; + local_dgram_job_join(&job); + + assert_int_equal(job.ret, 0); + assert_return_code(ret, err); + + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), 0); + local_dgram_job_start(&job, local_dgram_do_sendto, st->r1, st->p2, 100); + revents = local_dgram_revents(st->r2, POLLIN, 3000); + local_dgram_job_join(&job); + + assert_int_equal(job.ret, 5); + assert_int_equal(revents, POLLIN); + local_dgram_expect(st->r2, "later"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_unspecwait + * + * Description: + * Another thread dissolves the peer of a socket while poll() waits on it. + * + ****************************************************************************/ + +void test_local_dgram_poll_unspecwait(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + struct pollfd pfd; + int err; + int ret; + + st->r2 = local_dgram_bind(st->p2, false); + assert_return_code(st->r2, errno); + ret = local_dgram_connect_to(st->r2, st->p1, false); + assert_return_code(ret, errno); + + pfd.fd = st->r2; + pfd.events = POLLIN; + pfd.revents = 0; + local_dgram_job_start(&job, local_dgram_do_unspec, st->r2, NULL, 100); + ret = poll(&pfd, 1, 300); + err = errno; + local_dgram_job_join(&job); + + assert_int_equal(job.ret, 0); + assert_return_code(ret, err); + + /* It has no peer now, so it takes a datagram from any socket */ + + assert_int_equal(local_dgram_sendto(st->s, st->p2, "u", 1, 0), 1); + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r2, "u"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_closepeer + * + * Description: + * The receiver of a connected sender closes. The sender is still valid. + * + ****************************************************************************/ + +void test_local_dgram_poll_closepeer(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + int revents; + + assert_int_equal(close(st->r1), 0); + st->r1 = -1; + + revents = local_dgram_revents(st->s, POLLIN | POLLOUT, 0); + assert_int_equal(revents & POLLNVAL, 0); + assert_int_equal(revents & POLLIN, 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_closesend + * + * Description: + * The sender closes. The receiver reads the datagram with no POLLHUP. + * + ****************************************************************************/ + +void test_local_dgram_poll_closesend(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + assert_int_equal(send(st->s, "bye", 3, 0), 3); + assert_int_equal(close(st->s), 0); + st->s = -1; + + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r1, "bye"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_threads + * + * Description: + * Two threads wait in poll() on one socket, and one datagram wakes both. + * + ****************************************************************************/ + +void test_local_dgram_poll_threads(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job[2]; + ssize_t ret; + int i; + + for (i = 0; i < 2; i++) + { + local_dgram_job_arg(&job[i], local_dgram_do_poll, st->r1, NULL, 0, + 3000); + } + + usleep(300000); + ret = send(st->s, "both", 4, 0); + + for (i = 0; i < 2; i++) + { + local_dgram_job_join(&job[i]); + } + + assert_int_equal(ret, 4); + for (i = 0; i < 2; i++) + { + assert_int_equal(job[i].ret, 1); + assert_int_equal(job[i].revents, POLLIN); + } + + local_dgram_expect(st->r1, "both"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_slots + * + * Description: + * More waiters than the FIFO may have slots for: the call may fail with + * EBUSY, but no slot stays taken after it. + * + ****************************************************************************/ + +void test_local_dgram_poll_slots(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + struct pollfd pfd[4]; + int revents; + int ret; + int i; + + for (i = 0; i < 20; i++) + { + int j; + + for (j = 0; j < 4; j++) + { + pfd[j].fd = st->r1; + pfd[j].events = POLLIN; + pfd[j].revents = 0; + } + + errno = 0; + ret = poll(pfd, 4, 0); + assert_true(ret == 0 || (ret == -1 && errno == EBUSY)); + } + + local_dgram_job_start(&job, local_dgram_do_send, st->s, NULL, 100); + revents = local_dgram_revents(st->r1, POLLIN, 3000); + local_dgram_job_join(&job); + assert_int_equal(revents, POLLIN); + local_dgram_expect(st->r1, "later"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_cycle + * + * Description: + * Many poll() calls on one socket and many sockets on one name: nothing + * stays taken. + * + ****************************************************************************/ + +void test_local_dgram_poll_cycle(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + int i; + + for (i = 0; i < 200; i++) + { + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); + assert_int_equal(local_dgram_revents(st->s, POLLIN, 0), 0); + } + + 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_revents(st->r2, POLLIN, 0), 0); + assert_int_equal(local_dgram_sendto(st->s, st->p2, "c", 1, 0), 1); + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r2, "c"); + assert_int_equal(close(st->r2), 0); + st->r2 = -1; + } +} + +/**************************************************************************** + * Name: test_local_dgram_poll_big + * + * Description: + * A datagram too large for the receiver leaves it clean: no event for it, + * and the next datagram is whole. + * + ****************************************************************************/ + +void test_local_dgram_poll_big(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + ssize_t ret; + + ret = send(st->s, st->big, LOCAL_DGRAM_BIGLEN, 0); + if (ret < 0) + { + assert_int_equal(errno, EMSGSIZE); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); + } + else + { + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + ret = recv(st->r1, st->big, LOCAL_DGRAM_BIGLEN, 0); + assert_int_equal(ret, LOCAL_DGRAM_BIGLEN); + } + + assert_int_equal(send(st->s, "next", 4, 0), 4); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r1, "next"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_abstract + * + * Description: + * poll() of a socket with an abstract name. + * + ****************************************************************************/ + +void test_local_dgram_poll_abstract(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct sockaddr_un addr; + socklen_t addrlen; + ssize_t ret; + + st->r2 = local_dgram_bind(st->abs, true); + assert_return_code(st->r2, errno); + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), 0); + + addrlen = local_dgram_addr(&addr, st->abs, true); + ret = sendto(st->s, "ab", 2, 0, (FAR struct sockaddr *)&addr, addrlen); + assert_int_equal(ret, 2); + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r2, "ab"); + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_self + * + * Description: + * A socket sends to its own name. + * + ****************************************************************************/ + +void test_local_dgram_poll_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); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r1, "me"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_nonblock + * + * Description: + * poll() of an O_NONBLOCK socket still waits, and a send wakes it up. + * + ****************************************************************************/ + +void test_local_dgram_poll_nonblock(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + int revents; + int flags; + int ret; + + flags = fcntl(st->r1, F_GETFL); + assert_return_code(flags, errno); + ret = fcntl(st->r1, F_SETFL, flags | O_NONBLOCK); + assert_return_code(ret, errno); + + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); + local_dgram_job_start(&job, local_dgram_do_sendto, st->s, st->p1, 100); + revents = local_dgram_revents(st->r1, POLLIN, 3000); + local_dgram_job_join(&job); + assert_int_equal(revents, POLLIN); + local_dgram_expect(st->r1, "later"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_dup + * + * Description: + * poll() of a dup() of a socket, also after the first descriptor closes. + * + ****************************************************************************/ + +void test_local_dgram_poll_dup(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + + st->u = dup(st->r1); + assert_return_code(st->u, errno); + + assert_int_equal(local_dgram_revents(st->u, POLLIN, 0), 0); + assert_int_equal(send(st->s, "d1", 2, 0), 2); + assert_int_equal(local_dgram_revents(st->u, POLLIN, 1000), POLLIN); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), POLLIN); + + assert_int_equal(close(st->r1), 0); + st->r1 = -1; + assert_int_equal(local_dgram_revents(st->u, POLLIN, 0), POLLIN); + local_dgram_expect(st->u, "d1"); + assert_int_equal(local_dgram_revents(st->u, POLLIN, 0), 0); + + assert_int_equal(send(st->s, "d2", 2, 0), 2); + assert_int_equal(local_dgram_revents(st->u, POLLIN, 1000), POLLIN); + local_dgram_expect(st->u, "d2"); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_sendmsg + * + * Description: + * sendmsg() with two parts makes one datagram. + * + ****************************************************************************/ + +void test_local_dgram_poll_sendmsg(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct msghdr msg; + struct iovec iov[2]; + char part1[] = "ab"; + char part2[] = "cd"; + 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_iov = iov; + msg.msg_iovlen = 2; + + ret = sendmsg(st->s, &msg, 0); + assert_int_equal(ret, 4); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 1000), POLLIN); + local_dgram_expect(st->r1, "abcd"); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_poll_stress + * + * Description: + * Two senders on two sockets, one receiver in a poll() and recv() loop. + * Each datagram comes out whole, once and in the order of its sender. + * + ****************************************************************************/ + +void test_local_dgram_poll_stress(FAR void **state) +{ + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job[2]; + unsigned char buf[LOCAL_DGRAM_STRESSLEN + 16]; + int next[2]; + int bad = 0; + int got = 0; + int i; + + alarm(LOCAL_DGRAM_STRESSTIME); + + st->u = socket(AF_UNIX, SOCK_DGRAM, 0); + assert_return_code(st->u, errno); + + next[0] = 0; + next[1] = 0; + local_dgram_job_arg(&job[0], local_dgram_do_stress, st->s, st->p1, 0, 0); + local_dgram_job_arg(&job[1], local_dgram_do_stress, st->u, st->p1, 0, 1); + + while (got < 2 * LOCAL_DGRAM_STRESSNUM) + { + struct pollfd pfd; + ssize_t ret; + int who; + int seq; + int j; + + pfd.fd = st->r1; + pfd.events = POLLIN; + pfd.revents = 0; + if (poll(&pfd, 1, 5000) <= 0 || pfd.revents != POLLIN) + { + bad++; + break; + } + + ret = recv(st->r1, buf, sizeof(buf), 0); + if (ret < 4) + { + bad++; + break; + } + + got++; + who = buf[0]; + seq = buf[1]; + if (who > 1 || seq != next[who] || ret != buf[2]) + { + bad++; + continue; + } + + next[who]++; + for (j = 4; j < ret; j++) + { + if (buf[j] != (unsigned char)(who + seq)) + { + bad++; + break; + } + } + } + + for (i = 0; i < 2; i++) + { + local_dgram_job_join(&job[i]); + } + + assert_int_equal(job[0].ret, 0); + assert_int_equal(job[1].ret, 0); + assert_int_equal(bad, 0); + assert_int_equal(got, 2 * LOCAL_DGRAM_STRESSNUM); + assert_int_equal(local_dgram_revents(st->r1, POLLIN, 0), 0); +} + +/**************************************************************************** + * Name: test_local_dgram_fifo_policy + * + * Description: + * A FIFO with buffer policy 1 keeps its data with no writer, so a close of + * the last writer gives no POLLHUP. + * + ****************************************************************************/ + +void test_local_dgram_fifo_policy(FAR void **state) +{ +#ifdef PIPEIOC_POLICY + FAR struct nettest_local_dgram_state_s *st = *state; + struct local_dgram_job_s job; + char path[LOCAL_DGRAM_PATHLEN]; + char buf[4]; + int revents; + int ret; + + local_dgram_mkfifo(path, sizeof(path), st->pf); + + st->r2 = open(path, O_RDONLY | O_NONBLOCK); + assert_return_code(st->r2, errno); + ret = ioctl(st->r2, PIPEIOC_POLICY, 1); + assert_return_code(ret, errno); + st->r3 = open(path, O_WRONLY | O_NONBLOCK); + assert_return_code(st->r3, errno); + assert_int_equal(write(st->r3, "p", 1), 1); + + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), POLLIN); + assert_int_equal(close(st->r3), 0); + st->r3 = -1; + assert_int_equal(local_dgram_revents(st->r2, POLLIN, 0), POLLIN); + + /* A close during the wait gives no event either */ + + assert_int_equal(read(st->r2, buf, 1), 1); + st->r3 = open(path, O_WRONLY | O_NONBLOCK); + assert_return_code(st->r3, errno); + local_dgram_job_start(&job, local_dgram_do_close, st->r3, NULL, 100); + revents = local_dgram_revents(st->r2, POLLIN, 400); + local_dgram_job_join(&job); + st->r3 = -1; + unlink(path); + assert_int_equal(job.ret, 0); + assert_int_equal(revents, 0); +#else + skip(); +#endif +}
