tests: socket: socketpair: tests for socketpair(2) syscall
Tests for issue #24366 Signed-off-by: Christopher Friedt <chrisfriedt@gmail.com>
This commit is contained in:
parent
09f957c47a
commit
3bfc765aad
14 changed files with 1224 additions and 3 deletions
|
@ -498,6 +498,7 @@
|
||||||
/tests/net/lib/http_header_fields/ @jukkar @tbursztyka
|
/tests/net/lib/http_header_fields/ @jukkar @tbursztyka
|
||||||
/tests/net/lib/mqtt_packet/ @jukkar @tbursztyka
|
/tests/net/lib/mqtt_packet/ @jukkar @tbursztyka
|
||||||
/tests/net/lib/coap/ @rveerama1
|
/tests/net/lib/coap/ @rveerama1
|
||||||
|
/tests/net/socket/socketpair/ @cfriedt
|
||||||
/tests/net/socket/ @jukkar @tbursztyka @pfalcon
|
/tests/net/socket/ @jukkar @tbursztyka @pfalcon
|
||||||
/tests/subsys/fs/ @nashif @wentongwu
|
/tests/subsys/fs/ @nashif @wentongwu
|
||||||
/tests/subsys/settings/ @nvlsianpu
|
/tests/subsys/settings/ @nvlsianpu
|
||||||
|
|
|
@ -568,7 +568,7 @@ out:
|
||||||
*/
|
*/
|
||||||
static ssize_t spair_read(void *obj, void *buffer, size_t count)
|
static ssize_t spair_read(void *obj, void *buffer, size_t count)
|
||||||
{
|
{
|
||||||
ssize_t res;
|
int res;
|
||||||
|
|
||||||
bool is_connected;
|
bool is_connected;
|
||||||
size_t avail;
|
size_t avail;
|
||||||
|
@ -783,7 +783,7 @@ static int zsock_poll_update_ctx(struct spair *const spair,
|
||||||
if (pfd->events & ZSOCK_POLLOUT) {
|
if (pfd->events & ZSOCK_POLLOUT) {
|
||||||
if (!sock_is_connected(spair)) {
|
if (!sock_is_connected(spair)) {
|
||||||
pfd->revents |= ZSOCK_POLLHUP;
|
pfd->revents |= ZSOCK_POLLHUP;
|
||||||
goto check_pollin;
|
goto pollout_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
remote = z_get_fd_obj(spair->remote,
|
remote = z_get_fd_obj(spair->remote,
|
||||||
|
@ -850,7 +850,6 @@ pollin_done:
|
||||||
|
|
||||||
(*pev)++;
|
(*pev)++;
|
||||||
|
|
||||||
out:
|
|
||||||
if (remote != NULL && have_remote_sem) {
|
if (remote != NULL && have_remote_sem) {
|
||||||
k_sem_give(&remote->sem);
|
k_sem_give(&remote->sem);
|
||||||
}
|
}
|
||||||
|
|
8
tests/net/socket/socketpair/CMakeLists.txt
Normal file
8
tests/net/socket/socketpair/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13.1)
|
||||||
|
find_package(Zephyr HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(socketpair)
|
||||||
|
|
||||||
|
FILE(GLOB app_sources src/*.c)
|
||||||
|
target_sources(app PRIVATE ${app_sources})
|
34
tests/net/socket/socketpair/prj.conf
Normal file
34
tests/net/socket/socketpair/prj.conf
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
CONFIG_MP_NUM_CPUS=1
|
||||||
|
|
||||||
|
# General config
|
||||||
|
CONFIG_NEWLIB_LIBC=y
|
||||||
|
|
||||||
|
# Networking config
|
||||||
|
CONFIG_NETWORKING=y
|
||||||
|
CONFIG_NET_TEST=y
|
||||||
|
CONFIG_NET_LOOPBACK=y
|
||||||
|
CONFIG_NET_IPV4=y
|
||||||
|
CONFIG_NET_IPV6=y
|
||||||
|
CONFIG_NET_SOCKETS=y
|
||||||
|
CONFIG_NET_SOCKETPAIR=y
|
||||||
|
CONFIG_NET_SOCKETPAIR_BUFFER_SIZE=64
|
||||||
|
CONFIG_NET_SOCKETS_POSIX_NAMES=y
|
||||||
|
# Defines fd_set size
|
||||||
|
CONFIG_POSIX_MAX_FDS=33
|
||||||
|
|
||||||
|
# Network driver config
|
||||||
|
CONFIG_TEST_RANDOM_GENERATOR=y
|
||||||
|
|
||||||
|
# Network address config
|
||||||
|
CONFIG_NET_CONFIG_SETTINGS=y
|
||||||
|
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
|
||||||
|
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
|
||||||
|
|
||||||
|
CONFIG_MAIN_STACK_SIZE=2048
|
||||||
|
CONFIG_ZTEST=y
|
||||||
|
|
||||||
|
# User mode requirements
|
||||||
|
CONFIG_TEST_USERSPACE=y
|
||||||
|
CONFIG_HEAP_MEM_POOL_SIZE=2048
|
||||||
|
|
||||||
|
CONFIG_QEMU_TICKLESS_WORKAROUND=y
|
93
tests/net/socket/socketpair/src/main.c
Normal file
93
tests/net/socket/socketpair/src/main.c
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ztest.h>
|
||||||
|
|
||||||
|
/* in happy_path.c */
|
||||||
|
extern void test_socketpair_AF_LOCAL__SOCK_STREAM__0(void);
|
||||||
|
extern void test_socketpair_AF_UNIX__SOCK_STREAM__0(void);
|
||||||
|
|
||||||
|
/* in nonblock.c */
|
||||||
|
extern void test_socketpair_write_nonblock(void);
|
||||||
|
extern void test_socketpair_read_nonblock(void);
|
||||||
|
|
||||||
|
/* in block.c */
|
||||||
|
extern void test_socketpair_write_block(void);
|
||||||
|
extern void test_socketpair_read_block(void);
|
||||||
|
|
||||||
|
/* in closed_ends.c */
|
||||||
|
extern void test_socketpair_close_one_end_and_write_to_the_other(void);
|
||||||
|
extern void test_socketpair_close_one_end_and_read_from_the_other(void);
|
||||||
|
|
||||||
|
/* in expected_failures.c */
|
||||||
|
extern void test_socketpair_expected_failures(void);
|
||||||
|
|
||||||
|
/* in unsupported_calls.c */
|
||||||
|
extern void test_socketpair_unsupported_calls(void);
|
||||||
|
|
||||||
|
/* in fcntl.c */
|
||||||
|
extern void test_socketpair_fcntl(void);
|
||||||
|
|
||||||
|
/* in poll.c */
|
||||||
|
extern void test_socketpair_poll_timeout(void);
|
||||||
|
extern void test_socketpair_poll_timeout_nonblocking(void);
|
||||||
|
extern void test_socketpair_poll_close_remote_end_POLLIN(void);
|
||||||
|
extern void test_socketpair_poll_close_remote_end_POLLOUT(void);
|
||||||
|
extern void test_socketpair_poll_immediate_data(void);
|
||||||
|
extern void test_socketpair_poll_delayed_data(void);
|
||||||
|
|
||||||
|
void test_main(void)
|
||||||
|
{
|
||||||
|
k_thread_system_pool_assign(k_current_get());
|
||||||
|
|
||||||
|
ztest_test_suite(
|
||||||
|
socketpair,
|
||||||
|
ztest_user_unit_test(test_socketpair_AF_LOCAL__SOCK_STREAM__0),
|
||||||
|
ztest_user_unit_test(test_socketpair_AF_UNIX__SOCK_STREAM__0),
|
||||||
|
|
||||||
|
ztest_user_unit_test(test_socketpair_write_nonblock),
|
||||||
|
ztest_user_unit_test(test_socketpair_read_nonblock),
|
||||||
|
|
||||||
|
ztest_user_unit_test(
|
||||||
|
test_socketpair_close_one_end_and_write_to_the_other),
|
||||||
|
ztest_user_unit_test(
|
||||||
|
test_socketpair_close_one_end_and_read_from_the_other),
|
||||||
|
|
||||||
|
ztest_user_unit_test(test_socketpair_expected_failures),
|
||||||
|
|
||||||
|
ztest_user_unit_test(test_socketpair_unsupported_calls),
|
||||||
|
|
||||||
|
ztest_user_unit_test(test_socketpair_fcntl),
|
||||||
|
|
||||||
|
ztest_user_unit_test(test_socketpair_poll_timeout),
|
||||||
|
ztest_user_unit_test(
|
||||||
|
test_socketpair_poll_timeout_nonblocking),
|
||||||
|
ztest_user_unit_test(test_socketpair_poll_immediate_data)
|
||||||
|
);
|
||||||
|
|
||||||
|
ztest_run_test_suite(socketpair);
|
||||||
|
|
||||||
|
/* 20200509: @cfriedt: experiencing some issues with userspace thread
|
||||||
|
* / memory permissions that will require some sorting out. Currently
|
||||||
|
* these tests succeeed for native_posix_64.
|
||||||
|
*
|
||||||
|
* This feature is experimental at present.
|
||||||
|
*/
|
||||||
|
#if 0
|
||||||
|
ztest_test_suite(
|
||||||
|
socketpair_only_kernel,
|
||||||
|
ztest_user_unit_test(test_socketpair_write_block),
|
||||||
|
ztest_user_unit_test(test_socketpair_read_block),
|
||||||
|
ztest_user_unit_test(test_socketpair_poll_delayed_data),
|
||||||
|
ztest_user_unit_test(
|
||||||
|
test_socketpair_poll_close_remote_end_POLLIN),
|
||||||
|
ztest_user_unit_test(
|
||||||
|
test_socketpair_poll_close_remote_end_POLLOUT)
|
||||||
|
);
|
||||||
|
|
||||||
|
ztest_run_test_suite(socketpair_only_kernel);
|
||||||
|
#endif
|
||||||
|
}
|
194
tests/net/socket/socketpair/src/test_socketpair_block.c
Normal file
194
tests/net/socket/socketpair/src/test_socketpair_block.c
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#define TIMEOUT K_FOREVER
|
||||||
|
|
||||||
|
struct ctx {
|
||||||
|
/* true if test is write_block(), false if test is read_block() */
|
||||||
|
bool write;
|
||||||
|
/* the secondary-side socket of the socketpair */
|
||||||
|
int fd;
|
||||||
|
/* the count of the main thread */
|
||||||
|
atomic_t m;
|
||||||
|
/* the count of the secondary thread */
|
||||||
|
size_t n;
|
||||||
|
/* true when secondary thread is done */
|
||||||
|
bool done;
|
||||||
|
/* true if both main and secondary thread should immediately quit */
|
||||||
|
bool fail;
|
||||||
|
/* thread id of the secondary thread */
|
||||||
|
k_tid_t tid;
|
||||||
|
};
|
||||||
|
ZTEST_BMEM struct ctx ctx;
|
||||||
|
#define STACK_SIZE 512
|
||||||
|
/* thread structure for secondary thread */
|
||||||
|
ZTEST_BMEM struct k_thread th;
|
||||||
|
/* stack for the secondary thread */
|
||||||
|
static K_THREAD_STACK_DEFINE(th_stack, STACK_SIZE);
|
||||||
|
|
||||||
|
static void th_fun(void *arg0, void *arg1, void *arg2)
|
||||||
|
{
|
||||||
|
(void) arg0;
|
||||||
|
(void) arg1;
|
||||||
|
(void) arg2;
|
||||||
|
|
||||||
|
int res;
|
||||||
|
char c = '\0';
|
||||||
|
|
||||||
|
LOG_DBG("secondary thread running");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (ctx.write) {
|
||||||
|
LOG_DBG("ctx.m: %u", ctx.m);
|
||||||
|
if (atomic_get(&ctx.m)
|
||||||
|
< CONFIG_NET_SOCKETPAIR_BUFFER_SIZE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LOG_DBG("ready to read!");
|
||||||
|
} else {
|
||||||
|
LOG_DBG("sleeping for 100ms..");
|
||||||
|
k_sleep(K_MSEC(100));
|
||||||
|
LOG_DBG("ready to write!");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("%sing 1 byte %s fd %d", ctx.write ? "read" : "write",
|
||||||
|
ctx.write ? "from" : "to", ctx.fd);
|
||||||
|
if (ctx.write) {
|
||||||
|
res = read(ctx.fd, &c, 1);
|
||||||
|
} else {
|
||||||
|
res = write(ctx.fd, "x", 1);
|
||||||
|
}
|
||||||
|
if (-1 == res || 1 != res) {
|
||||||
|
LOG_DBG("%s(2) failed: %d", ctx.write ? "read" : "write",
|
||||||
|
errno);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
LOG_DBG("%s 1 byte", ctx.write ? "read" : "wrote");
|
||||||
|
ctx.n = 1;
|
||||||
|
|
||||||
|
out:
|
||||||
|
ctx.done = true;
|
||||||
|
|
||||||
|
LOG_DBG("terminating..");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_write_block(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
|
||||||
|
LOG_DBG("creating socketpair..");
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
|
||||||
|
LOG_DBG("data direction %d -> %d", sv[i], sv[(!i) & 1]);
|
||||||
|
|
||||||
|
LOG_DBG("setting up context");
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
ctx.write = true;
|
||||||
|
ctx.fd = sv[(!i) & 1];
|
||||||
|
|
||||||
|
LOG_DBG("creating secondary thread");
|
||||||
|
ctx.tid = k_thread_create(&th, th_stack,
|
||||||
|
STACK_SIZE, th_fun,
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
CONFIG_MAIN_THREAD_PRIORITY,
|
||||||
|
K_INHERIT_PERMS, K_NO_WAIT);
|
||||||
|
LOG_DBG("created secondary thread %p", ctx.tid);
|
||||||
|
|
||||||
|
/* fill up the buffer */
|
||||||
|
for (ctx.m = 0; atomic_get(&ctx.m)
|
||||||
|
< CONFIG_NET_SOCKETPAIR_BUFFER_SIZE;) {
|
||||||
|
|
||||||
|
res = write(sv[i], "x", 1);
|
||||||
|
zassert_not_equal(res, -1, "write(2) failed: %d",
|
||||||
|
errno);
|
||||||
|
zassert_equal(res, 1, "wrote %d bytes instead of 1",
|
||||||
|
res);
|
||||||
|
|
||||||
|
atomic_inc(&ctx.m);
|
||||||
|
LOG_DBG("have written %u bytes", ctx.m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* try to write one more byte */
|
||||||
|
LOG_DBG("writing to fd %d", sv[i]);
|
||||||
|
res = write(sv[i], "x", 1);
|
||||||
|
zassert_not_equal(res, -1, "write(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 1, "wrote %d bytes instead of 1", res);
|
||||||
|
|
||||||
|
LOG_DBG("success!");
|
||||||
|
|
||||||
|
k_thread_join(&th, K_MSEC(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_read_block(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
char x;
|
||||||
|
|
||||||
|
LOG_DBG("creating socketpair..");
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
|
||||||
|
LOG_DBG("data direction %d -> %d", sv[i], sv[(!i) & 1]);
|
||||||
|
|
||||||
|
LOG_DBG("setting up context");
|
||||||
|
memset(&ctx, 0, sizeof(ctx));
|
||||||
|
ctx.write = false;
|
||||||
|
ctx.fd = sv[(!i) & 1];
|
||||||
|
|
||||||
|
LOG_DBG("creating secondary thread");
|
||||||
|
ctx.tid = k_thread_create(&th, th_stack,
|
||||||
|
STACK_SIZE, th_fun,
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
CONFIG_MAIN_THREAD_PRIORITY,
|
||||||
|
K_INHERIT_PERMS, K_NO_WAIT);
|
||||||
|
LOG_DBG("created secondary thread %p", ctx.tid);
|
||||||
|
|
||||||
|
/* try to read one byte */
|
||||||
|
LOG_DBG("reading from fd %d", sv[i]);
|
||||||
|
x = '\0';
|
||||||
|
res = read(sv[i], &x, 1);
|
||||||
|
zassert_not_equal(res, -1, "read(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 1, "read %d bytes instead of 1", res);
|
||||||
|
|
||||||
|
LOG_DBG("success!");
|
||||||
|
|
||||||
|
k_thread_join(&th, K_MSEC(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*/
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
void test_socketpair_close_one_end_and_write_to_the_other(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
res = close(sv[i]);
|
||||||
|
zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
|
||||||
|
|
||||||
|
res = write(sv[(!i) & 1], "x", 1);
|
||||||
|
zassert_equal(res, -1, "expected write(2) to fail");
|
||||||
|
zassert_equal(res, -1, "errno: expected: EPIPE actual: %d",
|
||||||
|
errno);
|
||||||
|
|
||||||
|
res = close(sv[(!i) & 1]);
|
||||||
|
zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_close_one_end_and_read_from_the_other(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
char xx[16];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
/* We want to write some bytes to the closing end of the
|
||||||
|
* socket before it gets shut down, so that we can prove that
|
||||||
|
* reading is possible from the other end still and that data
|
||||||
|
* is not lost.
|
||||||
|
*/
|
||||||
|
res = write(sv[i], "xx", 2);
|
||||||
|
zassert_not_equal(res, -1, "write(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 2, "write(2) failed to write 2 bytes");
|
||||||
|
|
||||||
|
res = close(sv[i]);
|
||||||
|
zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
|
||||||
|
|
||||||
|
memset(xx, 0, sizeof(xx));
|
||||||
|
res = read(sv[(!i) & 1], xx, sizeof(xx));
|
||||||
|
zassert_not_equal(res, -1, "read(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 2, "expected to read 2 bytes but read %d",
|
||||||
|
res);
|
||||||
|
|
||||||
|
res = read(sv[(!i) & 1], xx, sizeof(xx));
|
||||||
|
zassert_equal(res, 0,
|
||||||
|
"expected read(2) to succeed but read 0 bytes");
|
||||||
|
|
||||||
|
res = close(sv[(!i) & 1]);
|
||||||
|
zassert_equal(res, 0, "close(sv[%u]) failed: %d", i, errno);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
void test_socketpair_expected_failures(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
|
||||||
|
/* Use invalid values in fields starting from left to right */
|
||||||
|
|
||||||
|
res = socketpair(AF_INET, SOCK_STREAM, 0, sv);
|
||||||
|
if (res != -1) {
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
zassert_equal(res, -1, "socketpair with fail with bad address family");
|
||||||
|
zassert_equal(errno, EAFNOSUPPORT,
|
||||||
|
"errno should be EAFNOSUPPORT with bad adddress family");
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, 42, 0, sv);
|
||||||
|
if (res != -1) {
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
zassert_equal(res, -1,
|
||||||
|
"socketpair should fail with unsupported socket type");
|
||||||
|
zassert_equal(errno, EPROTOTYPE,
|
||||||
|
"errno should be EPROTOTYPE with bad socket type");
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, IPPROTO_TLS_1_0, sv);
|
||||||
|
if (res != -1) {
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
zassert_equal(res, -1,
|
||||||
|
"socketpair should fail with unsupported protocol");
|
||||||
|
zassert_equal(errno, EPROTONOSUPPORT,
|
||||||
|
"errno should be EPROTONOSUPPORT with bad protocol");
|
||||||
|
|
||||||
|
/* This is not a POSIX requirement, but should be safe */
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, NULL);
|
||||||
|
if (res != -1) {
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
zassert_equal(res, -1,
|
||||||
|
"socketpair should fail with invalid socket vector");
|
||||||
|
zassert_equal(errno, EFAULT,
|
||||||
|
"errno should be EFAULT with bad socket vector");
|
||||||
|
}
|
54
tests/net/socket/socketpair/src/test_socketpair_fcntl.c
Normal file
54
tests/net/socket/socketpair/src/test_socketpair_fcntl.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
void test_socketpair_fcntl(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0,
|
||||||
|
"socketpair(AF_UNIX, SOCK_STREAM, 0, sv) failed");
|
||||||
|
|
||||||
|
res = fcntl(sv[0], F_GETFL, 0);
|
||||||
|
zassert_not_equal(res, -1,
|
||||||
|
"fcntl(sv[0], F_GETFL) failed. errno: %d", errno);
|
||||||
|
|
||||||
|
flags = res;
|
||||||
|
zassert_equal(res & O_NONBLOCK, 0,
|
||||||
|
"socketpair should block by default");
|
||||||
|
|
||||||
|
res = fcntl(sv[0], F_SETFL, flags | O_NONBLOCK);
|
||||||
|
zassert_not_equal(res, -1,
|
||||||
|
"fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) failed. errno: %d",
|
||||||
|
errno);
|
||||||
|
|
||||||
|
res = fcntl(sv[0], F_GETFL, 0);
|
||||||
|
zassert_equal(res ^ flags, O_NONBLOCK, "expected O_NONBLOCK set");
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
181
tests/net/socket/socketpair/src/test_socketpair_happy_path.c
Normal file
181
tests/net/socket/socketpair/src/test_socketpair_happy_path.c
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
static void happy_path(
|
||||||
|
const int family, const char *family_s,
|
||||||
|
const int type, const char *type_s,
|
||||||
|
const int proto, const char *proto_s
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
|
||||||
|
const char *expected_msg = "Hello, socketpair(2) world!";
|
||||||
|
const unsigned int expected_msg_len = strlen(expected_msg);
|
||||||
|
char actual_msg[32];
|
||||||
|
size_t actual_msg_len;
|
||||||
|
struct iovec iovec;
|
||||||
|
struct msghdr msghdr;
|
||||||
|
|
||||||
|
LOG_DBG("calling socketpair(%u, %u, %u, %p)", family, type, proto, sv);
|
||||||
|
res = socketpair(family, type, proto, sv);
|
||||||
|
zassert_true(res == -1 || res == 0,
|
||||||
|
"socketpair returned an unspecified value");
|
||||||
|
zassert_equal(res, 0, "socketpair failed");
|
||||||
|
LOG_DBG("sv: {%d, %d}", sv[0], sv[1]);
|
||||||
|
|
||||||
|
socklen_t len;
|
||||||
|
|
||||||
|
/* sockets are bidirectional. test functions from both ends */
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test with write(2) / read(2)
|
||||||
|
*/
|
||||||
|
|
||||||
|
LOG_DBG("calling write(%d, '%s', %u)", sv[i], expected_msg,
|
||||||
|
expected_msg_len);
|
||||||
|
res = write(sv[i], expected_msg, expected_msg_len);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "write(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"did not write entire message");
|
||||||
|
|
||||||
|
memset(actual_msg, 0, sizeof(actual_msg));
|
||||||
|
|
||||||
|
LOG_DBG("calling read(%d, %p, %u)", sv[i], actual_msg,
|
||||||
|
(unsigned int)sizeof(actual_msg));
|
||||||
|
res = read(sv[(!i) & 1], actual_msg, sizeof(actual_msg));
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "read(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"wrong return value");
|
||||||
|
|
||||||
|
zassert_true(strncmp(expected_msg, actual_msg,
|
||||||
|
actual_msg_len) == 0,
|
||||||
|
"the wrong message was passed through the socketpair");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test with send(2) / recv(2)
|
||||||
|
*/
|
||||||
|
|
||||||
|
res = send(sv[i], expected_msg, expected_msg_len, 0);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "send(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"did not send entire message");
|
||||||
|
|
||||||
|
memset(actual_msg, 0, sizeof(actual_msg));
|
||||||
|
|
||||||
|
res = recv(sv[(!i) & 1], actual_msg, sizeof(actual_msg), 0);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "recv(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"wrong return value");
|
||||||
|
|
||||||
|
zassert_true(strncmp(expected_msg, actual_msg,
|
||||||
|
actual_msg_len) == 0,
|
||||||
|
"the wrong message was passed through the socketpair");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test with sendto(2) / recvfrom(2)
|
||||||
|
*/
|
||||||
|
|
||||||
|
res = sendto(sv[i], expected_msg, expected_msg_len, 0, NULL, 0);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "sendto(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"did not sendto entire message");
|
||||||
|
|
||||||
|
memset(actual_msg, 0, sizeof(actual_msg));
|
||||||
|
|
||||||
|
res = recvfrom(sv[(!i) & 1], actual_msg, sizeof(actual_msg), 0,
|
||||||
|
NULL, &len);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "recvfrom(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"wrong return value");
|
||||||
|
|
||||||
|
zassert_true(strncmp(expected_msg, actual_msg,
|
||||||
|
actual_msg_len) == 0,
|
||||||
|
"the wrong message was passed through the socketpair");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test with sendmsg(2) / recv(2) - Zephyr lacks recvmsg atm
|
||||||
|
*/
|
||||||
|
|
||||||
|
msghdr.msg_iov = &iovec;
|
||||||
|
msghdr.msg_iovlen = 1;
|
||||||
|
iovec.iov_base = (void *)expected_msg;
|
||||||
|
iovec.iov_len = expected_msg_len;
|
||||||
|
|
||||||
|
res = sendmsg(sv[i], &msghdr, 0);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "sendmsg(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"did not sendmsg entire message");
|
||||||
|
|
||||||
|
res = read(sv[(!i) & 1], actual_msg, sizeof(actual_msg));
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "read(2) failed: %d", errno);
|
||||||
|
actual_msg_len = res;
|
||||||
|
zassert_equal(actual_msg_len, expected_msg_len,
|
||||||
|
"wrong return value");
|
||||||
|
|
||||||
|
zassert_true(strncmp(expected_msg, actual_msg,
|
||||||
|
actual_msg_len) == 0,
|
||||||
|
"the wrong message was passed through the socketpair");
|
||||||
|
}
|
||||||
|
|
||||||
|
res = close(sv[0]);
|
||||||
|
zassert_equal(res, 0, "close failed");
|
||||||
|
|
||||||
|
res = close(sv[1]);
|
||||||
|
zassert_equal(res, 0, "close failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_AF_LOCAL__SOCK_STREAM__0(void)
|
||||||
|
{
|
||||||
|
happy_path(
|
||||||
|
AF_LOCAL, "AF_LOCAL",
|
||||||
|
SOCK_STREAM, "SOCK_STREAM",
|
||||||
|
0, "0"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_AF_UNIX__SOCK_STREAM__0(void)
|
||||||
|
{
|
||||||
|
happy_path(
|
||||||
|
AF_UNIX, "AF_UNIX",
|
||||||
|
SOCK_STREAM, "SOCK_STREAM",
|
||||||
|
0, "0"
|
||||||
|
);
|
||||||
|
}
|
86
tests/net/socket/socketpair/src/test_socketpair_nonblock.c
Normal file
86
tests/net/socket/socketpair/src/test_socketpair_nonblock.c
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
void test_socketpair_write_nonblock(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
/* first, fill up the buffer */
|
||||||
|
for (size_t k = 0; k < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE;
|
||||||
|
++k) {
|
||||||
|
res = write(sv[i], "x", 1);
|
||||||
|
zassert_equal(res, 1, "write(2) failed: %d", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* then set the O_NONBLOCK flag */
|
||||||
|
res = fcntl(sv[i], F_GETFL, 0);
|
||||||
|
zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
|
||||||
|
|
||||||
|
res = fcntl(sv[i], F_SETFL, res | O_NONBLOCK);
|
||||||
|
zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
|
||||||
|
|
||||||
|
/* then, try to write one more byte */
|
||||||
|
res = write(sv[i], "x", 1);
|
||||||
|
zassert_equal(res, -1, "expected write to fail");
|
||||||
|
zassert_equal(errno, EAGAIN, "errno: exected: EAGAIN "
|
||||||
|
"actual: %d", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_read_nonblock(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
char c;
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
/* set the O_NONBLOCK flag */
|
||||||
|
res = fcntl(sv[i], F_GETFL, 0);
|
||||||
|
zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
|
||||||
|
|
||||||
|
res = fcntl(sv[i], F_SETFL, res | O_NONBLOCK);
|
||||||
|
zassert_not_equal(res, -1, "fcntl() failed: %d", i, errno);
|
||||||
|
|
||||||
|
/* then, try to read one byte */
|
||||||
|
res = read(sv[i], &c, 1);
|
||||||
|
zassert_equal(res, -1, "expected read to fail");
|
||||||
|
zassert_equal(errno, EAGAIN, "errno: exected: EAGAIN "
|
||||||
|
"actual: %d", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
341
tests/net/socket/socketpair/src/test_socketpair_poll.c
Normal file
341
tests/net/socket/socketpair/src/test_socketpair_poll.c
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#define STACK_SIZE 512
|
||||||
|
/* stack for the secondary thread */
|
||||||
|
static K_THREAD_STACK_DEFINE(th_stack, STACK_SIZE);
|
||||||
|
static struct k_thread th;
|
||||||
|
static k_tid_t tid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timeout should work the same for blocking & non-blocking threads
|
||||||
|
*
|
||||||
|
* - no bytes available to read after timeout, r: 0 (timeout)
|
||||||
|
* - no bytes available to write after timeout, r: 0 (timeout)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void test_socketpair_poll_timeout_common(int sv[2])
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
struct pollfd fds[1];
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLIN;
|
||||||
|
res = poll(fds, 1, 1);
|
||||||
|
zassert_equal(res, 0, "poll: expected: 0 actual: %d", res);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE; ++i) {
|
||||||
|
res = write(sv[0], "x", 1);
|
||||||
|
zassert_equal(res, 1, "write failed: %d", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLOUT;
|
||||||
|
res = poll(fds, 1, 1);
|
||||||
|
zassert_equal(res, 0, "poll: expected: 0 actual: %d", res);
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_poll_timeout(void)
|
||||||
|
{
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "socketpair failed: %d", errno);
|
||||||
|
|
||||||
|
test_socketpair_poll_timeout_common(sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* O_NONBLOCK should have no affect on poll(2) */
|
||||||
|
void test_socketpair_poll_timeout_nonblocking(void)
|
||||||
|
{
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
|
||||||
|
zassert_not_equal(res, -1, "socketpair failed: %d", errno);
|
||||||
|
|
||||||
|
res = fcntl(sv[0], F_GETFL, 0);
|
||||||
|
zassert_not_equal(res, -1, "fcntl failed: %d", errno);
|
||||||
|
|
||||||
|
int flags = res;
|
||||||
|
|
||||||
|
res = fcntl(sv[0], F_SETFL, O_NONBLOCK | flags);
|
||||||
|
zassert_not_equal(res, -1, "fcntl failed: %d", errno);
|
||||||
|
|
||||||
|
res = fcntl(sv[1], F_SETFL, O_NONBLOCK | flags);
|
||||||
|
zassert_not_equal(res, -1, "fcntl failed: %d", errno);
|
||||||
|
|
||||||
|
test_socketpair_poll_timeout_common(sv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_fun(void *arg1, void *arg2, void *arg3)
|
||||||
|
{
|
||||||
|
(void)arg2;
|
||||||
|
(void)arg3;
|
||||||
|
|
||||||
|
const int *const fd = (const int *)arg1;
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(1000));
|
||||||
|
|
||||||
|
LOG_DBG("about to close fd %d", *fd);
|
||||||
|
close(*fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hangup should cause the following behaviour
|
||||||
|
* - close remote fd while the local fd is blocking in poll. r: 1,
|
||||||
|
* POLLIN, read -> r: 0, errno: 0 -> EOF
|
||||||
|
* - close remote fd while the local fd is blocking in poll. r: 1,
|
||||||
|
* POLLOUT, write -> r: -1, errno: EPIPE.
|
||||||
|
*/
|
||||||
|
void test_socketpair_poll_close_remote_end_POLLIN(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
char c;
|
||||||
|
struct pollfd fds[1];
|
||||||
|
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* poll until there are bytes to read.
|
||||||
|
* But rather than writing, close the other end of the channel
|
||||||
|
*/
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLIN;
|
||||||
|
|
||||||
|
tid = k_thread_create(&th, th_stack,
|
||||||
|
K_THREAD_STACK_SIZEOF(th_stack), close_fun,
|
||||||
|
&sv[1], NULL, NULL,
|
||||||
|
CONFIG_MAIN_THREAD_PRIORITY + 1,
|
||||||
|
K_USER, K_NO_WAIT);
|
||||||
|
|
||||||
|
res = poll(fds, 1, -1);
|
||||||
|
zassert_equal(res, 1, "poll(2) failed: %d", res);
|
||||||
|
zassert_equal(fds[0].revents & POLLIN, POLLIN, "POLLIN not set");
|
||||||
|
|
||||||
|
res = k_thread_join(&th, K_MSEC(5000));
|
||||||
|
zassert_false(res < 0, "k_thread_join failed: %d", res);
|
||||||
|
|
||||||
|
res = read(sv[0], &c, 1);
|
||||||
|
zassert_equal(res, 0, "read did not return EOF");
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_socketpair_poll_close_remote_end_POLLOUT(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
struct pollfd fds[1];
|
||||||
|
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill up the remote q and then poll until write space is available.
|
||||||
|
* But rather than reading, close the other end of the channel
|
||||||
|
*/
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE; ++i) {
|
||||||
|
res = write(sv[0], "x", 1);
|
||||||
|
zassert_equal(res, 1, "write failed: %d", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLOUT;
|
||||||
|
|
||||||
|
tid = k_thread_create(&th, th_stack,
|
||||||
|
K_THREAD_STACK_SIZEOF(th_stack), close_fun,
|
||||||
|
&sv[1], NULL, NULL,
|
||||||
|
CONFIG_MAIN_THREAD_PRIORITY + 1,
|
||||||
|
K_USER, K_NO_WAIT);
|
||||||
|
|
||||||
|
res = poll(fds, 1, -1);
|
||||||
|
zassert_equal(res, 1, "poll(2) failed: %d", res);
|
||||||
|
zassert_equal(fds[0].revents & POLLHUP, POLLHUP, "POLLHUP not set");
|
||||||
|
|
||||||
|
res = k_thread_join(&th, K_MSEC(5000));
|
||||||
|
zassert_false(res < 0, "k_thread_join failed: %d", res);
|
||||||
|
|
||||||
|
res = write(sv[0], "x", 1);
|
||||||
|
zassert_equal(res, -1, "write(2): expected: -1 actual: %d", res);
|
||||||
|
zassert_equal(errno, EPIPE, "errno: expected: EPIPE actual: %d", errno);
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data available immediately
|
||||||
|
* - even with a timeout value of 0 us, poll should return immediately with
|
||||||
|
* a value of 1 (for either read or write cases)
|
||||||
|
* - even with a timeout value of 0us, poll should return immediately with
|
||||||
|
* a value of 2 if both read and write are available
|
||||||
|
*/
|
||||||
|
void test_socketpair_poll_immediate_data(void)
|
||||||
|
{
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
int res;
|
||||||
|
|
||||||
|
struct pollfd fds[2];
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLOUT;
|
||||||
|
res = poll(fds, 1, 0);
|
||||||
|
zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
|
||||||
|
zassert_not_equal(fds[0].revents & POLLOUT, 0, "POLLOUT not set");
|
||||||
|
|
||||||
|
res = write(sv[0], "x", 1);
|
||||||
|
zassert_not_equal(res, -1, "write(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 1, "write(2): expected: 1 actual: %d", res);
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[1];
|
||||||
|
fds[0].events |= POLLIN;
|
||||||
|
res = poll(fds, 1, 0);
|
||||||
|
zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
|
||||||
|
zassert_not_equal(fds[0].revents & POLLIN, 0, "POLLIN not set");
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLOUT;
|
||||||
|
fds[1].fd = sv[1];
|
||||||
|
fds[1].events |= POLLIN;
|
||||||
|
res = poll(fds, 2, 0);
|
||||||
|
zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 2, "poll(2): expected: 1 actual: %d", res);
|
||||||
|
zassert_not_equal(fds[0].revents & POLLOUT, 0, "POLLOUT not set");
|
||||||
|
zassert_not_equal(fds[1].revents & POLLIN, 0, "POLLIN not set");
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rw_fun(void *arg1, void *arg2, void *arg3)
|
||||||
|
{
|
||||||
|
(void)arg3;
|
||||||
|
|
||||||
|
const bool *const should_write = (const bool *) arg1;
|
||||||
|
const int *const fd = (const int *)arg2;
|
||||||
|
|
||||||
|
int res;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(1000));
|
||||||
|
|
||||||
|
if (*should_write) {
|
||||||
|
LOG_DBG("about to write 1 byte");
|
||||||
|
res = write(*fd, "x", 1);
|
||||||
|
if (-1 == res) {
|
||||||
|
LOG_DBG("write(2) failed: %d", errno);
|
||||||
|
} else {
|
||||||
|
LOG_DBG("wrote 1 byte");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_DBG("about to read 1 byte");
|
||||||
|
res = read(*fd, &c, 1);
|
||||||
|
if (-1 == res) {
|
||||||
|
LOG_DBG("read(2) failed: %d", errno);
|
||||||
|
} else {
|
||||||
|
LOG_DBG("read 1 byte");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data only available but after some short period
|
||||||
|
* - say there is a timeout value of 5 s, poll should return immediately
|
||||||
|
* with the a value of 1 (for either read or write cases)
|
||||||
|
*/
|
||||||
|
void test_socketpair_poll_delayed_data(void)
|
||||||
|
{
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
int res;
|
||||||
|
|
||||||
|
bool should_write;
|
||||||
|
|
||||||
|
struct pollfd fds[1];
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_not_equal(res, -1, "socketpair(2) failed: %d", errno);
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLIN;
|
||||||
|
should_write = true;
|
||||||
|
|
||||||
|
tid = k_thread_create(&th, th_stack,
|
||||||
|
K_THREAD_STACK_SIZEOF(th_stack), rw_fun,
|
||||||
|
&should_write, &sv[1], NULL,
|
||||||
|
CONFIG_MAIN_THREAD_PRIORITY + 1,
|
||||||
|
K_USER, K_NO_WAIT);
|
||||||
|
|
||||||
|
res = poll(fds, 1, 5000);
|
||||||
|
zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
|
||||||
|
zassert_not_equal(fds[0].revents & POLLIN, 0, "POLLIN not set");
|
||||||
|
k_thread_join(&th, K_FOREVER);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < CONFIG_NET_SOCKETPAIR_BUFFER_SIZE; ++i) {
|
||||||
|
res = write(sv[0], "x", 1);
|
||||||
|
zassert_equal(res, 1, "write failed: %d", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(fds, 0, sizeof(fds));
|
||||||
|
fds[0].fd = sv[0];
|
||||||
|
fds[0].events |= POLLOUT;
|
||||||
|
should_write = false;
|
||||||
|
|
||||||
|
tid = k_thread_create(&th, th_stack,
|
||||||
|
K_THREAD_STACK_SIZEOF(th_stack), rw_fun,
|
||||||
|
&should_write, &sv[1], NULL,
|
||||||
|
CONFIG_MAIN_THREAD_PRIORITY + 1,
|
||||||
|
K_USER, K_NO_WAIT);
|
||||||
|
|
||||||
|
res = poll(fds, 1, 5000);
|
||||||
|
zassert_not_equal(res, -1, "poll(2) failed: %d", errno);
|
||||||
|
zassert_equal(res, 1, "poll(2): expected: 1 actual: %d", res);
|
||||||
|
zassert_not_equal(fds[0].revents & POLLOUT, 0, "POLLOUT was not set");
|
||||||
|
k_thread_join(&th, K_FOREVER);
|
||||||
|
|
||||||
|
close(sv[0]);
|
||||||
|
close(sv[1]);
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Friedt Professional Engineering Services, Inc
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(net_test, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <net/socket.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
#include <posix/unistd.h>
|
||||||
|
|
||||||
|
#include <ztest_assert.h>
|
||||||
|
|
||||||
|
#undef read
|
||||||
|
#define read(fd, buf, len) zsock_recv(fd, buf, len, 0)
|
||||||
|
|
||||||
|
#undef write
|
||||||
|
#define write(fd, buf, len) zsock_send(fd, buf, len, 0)
|
||||||
|
|
||||||
|
void test_socketpair_unsupported_calls(void)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
int sv[2] = {-1, -1};
|
||||||
|
struct sockaddr_un addr = {
|
||||||
|
.sun_family = AF_UNIX,
|
||||||
|
};
|
||||||
|
socklen_t len = sizeof(addr);
|
||||||
|
|
||||||
|
res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
|
||||||
|
zassert_equal(res, 0,
|
||||||
|
"socketpair(AF_UNIX, SOCK_STREAM, 0, sv) failed");
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i) {
|
||||||
|
|
||||||
|
res = bind(sv[i], (struct sockaddr *)&addr, len);
|
||||||
|
zassert_equal(res, -1,
|
||||||
|
"bind should fail on a socketpair endpoint");
|
||||||
|
zassert_equal(errno, EISCONN,
|
||||||
|
"bind should set errno to EISCONN");
|
||||||
|
|
||||||
|
res = connect(sv[i], (struct sockaddr *)&addr, len);
|
||||||
|
zassert_equal(res, -1,
|
||||||
|
"connect should fail on a socketpair endpoint");
|
||||||
|
zassert_equal(errno, EISCONN,
|
||||||
|
"connect should set errno to EISCONN");
|
||||||
|
|
||||||
|
res = listen(sv[i], 1);
|
||||||
|
zassert_equal(res, -1,
|
||||||
|
"listen should fail on a socketpair endpoint");
|
||||||
|
zassert_equal(errno, EINVAL,
|
||||||
|
"listen should set errno to EINVAL");
|
||||||
|
|
||||||
|
res = accept(sv[i], (struct sockaddr *)&addr, &len);
|
||||||
|
zassert_equal(res, -1,
|
||||||
|
"accept should fail on a socketpair endpoint");
|
||||||
|
zassert_equal(errno, EOPNOTSUPP,
|
||||||
|
"accept should set errno to EOPNOTSUPP");
|
||||||
|
}
|
||||||
|
|
||||||
|
res = close(sv[0]);
|
||||||
|
zassert_equal(res, 0, "close failed");
|
||||||
|
|
||||||
|
res = close(sv[1]);
|
||||||
|
zassert_equal(res, 0, "close failed");
|
||||||
|
}
|
5
tests/net/socket/socketpair/testcase.yaml
Normal file
5
tests/net/socket/socketpair/testcase.yaml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
common:
|
||||||
|
tags: net socket userspace
|
||||||
|
tests:
|
||||||
|
net.socket.socketpair:
|
||||||
|
min_ram: 21
|
Loading…
Add table
Add a link
Reference in a new issue