diff --git a/examples/nettest/Kconfig b/examples/nettest/Kconfig index 7f5d8d8b0..d12d4a618 100644 --- a/examples/nettest/Kconfig +++ b/examples/nettest/Kconfig @@ -12,21 +12,37 @@ config EXAMPLES_NETTEST if EXAMPLES_NETTEST +config EXAMPLES_NETTEST_PROGNAME + string "Program name" + default "nettest" + depends on BUILD_KERNEL + ---help--- + This is the name of the program that will be use when the Nettest + program is installed. + +config EXAMPLES_NETTEST_STACKSIZE + int "Nettest stack size" + default 2048 + +config EXAMPLES_NETTEST_PRIORITY + int "Nettest priority" + default 100 + config EXAMPLES_NETTEST_LOOPBACK bool "Loopback test" default n - depends on NET_LOOPBACK + depends on NET_LOOPBACK || IEEE802154_LOOPBACK ---help--- Perform the test using the local loopback device. In this case, both the client and the server reside on the target. if EXAMPLES_NETTEST_LOOPBACK -config EXAMPLES_NETTEST_STACKSIZE +config EXAMPLES_NETTEST_SERVER_STACKSIZE int "Server stack size" default 2048 -config EXAMPLES_NETTEST_PRIORITY +config EXAMPLES_NETTEST_SERVER_PRIORITY int "Server priority" default 100 @@ -369,7 +385,7 @@ config EXAMPLES_NETTEST_IPv6NETMASK_8 endif # NET_ICMPv6_AUTOCONF endif # EXAMPLES_NETTEST_INIT -if !EXAMPLES_NETTEST_LOOPBACK +if !EXAMPLES_NETTEST_LOOPBACK || !NET_LOOPBACK comment "Client IPv6 address" diff --git a/examples/nettest/Makefile b/examples/nettest/Makefile index 08a6a8737..6fc5a26af 100644 --- a/examples/nettest/Makefile +++ b/examples/nettest/Makefile @@ -106,9 +106,15 @@ ROOTDEPPATH = --dep-path . # NET test built-in application info +CONFIG_EXAMPLES_NETTEST_STACKSIZE ?= 2048 +CONFIG_EXAMPLES_NETTEST_PRIORITY ?= 100 + APPNAME = nettest -PRIORITY = SCHED_PRIORITY_DEFAULT -STACKSIZE = 2048 +PRIORITY = $(CONFIG_EXAMPLES_NETTEST_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_NETTEST_STACKSIZE) + +CONFIG_EXAMPLES_NETTEST_PROGNAME ?= nettest$(EXEEXT) +PROGNAME = $(CONFIG_EXAMPLES_HELLO_PROGNAME) # Common build diff --git a/examples/nettest/nettest.c b/examples/nettest/nettest.c index e9f0d5652..12b33e02b 100644 --- a/examples/nettest/nettest.c +++ b/examples/nettest/nettest.c @@ -219,8 +219,8 @@ int nettest_main(int argc, char *argv[]) #if defined(CONFIG_EXAMPLES_NETTEST_LOOPBACK) /* Then perform the server side of the test on a child task */ - child = task_create("Nettest Child", CONFIG_EXAMPLES_NETTEST_PRIORITY, - CONFIG_EXAMPLES_NETTEST_STACKSIZE, server_child, + child = task_create("Nettest Child", CONFIG_EXAMPLES_NETTEST_SERVER_PRIORITY, + CONFIG_EXAMPLES_NETTEST_SERVER_STACKSIZE, server_child, NULL); if (child < 0) { diff --git a/examples/nettest/nettest_client.c b/examples/nettest/nettest_client.c index d377a9b3a..b27ea0c4f 100644 --- a/examples/nettest/nettest_client.c +++ b/examples/nettest/nettest_client.c @@ -106,8 +106,8 @@ void send_client(void) myaddr.sin6_family = AF_INET6; myaddr.sin6_port = HTONS(PORTNO); -#ifdef CONFIG_EXAMPLES_NETTEST_LOOPBACK - myaddr.sin6_addr.s6_addr16[0] = 0; +#if defined(CONFIG_EXAMPLES_NETTEST_LOOPBACK) && defined(NET_LOOPBACK) + myaddr.sin6_addr.s6_addr16[0] = 0; /* Use the loopback address */ myaddr.sin6_addr.s6_addr16[1] = 0; myaddr.sin6_addr.s6_addr16[2] = 0; myaddr.sin6_addr.s6_addr16[3] = 0; @@ -126,17 +126,25 @@ void send_client(void) myaddr.sin6_addr.s6_addr16[7] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_8); #endif + printf("IPv6 Address: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + myaddr.sin6_addr.s6_addr16[0], myaddr.sin6_addr.s6_addr16[1], + myaddr.sin6_addr.s6_addr16[2], myaddr.sin6_addr.s6_addr16[3], + myaddr.sin6_addr.s6_addr16[4], myaddr.sin6_addr.s6_addr16[5], + myaddr.sin6_addr.s6_addr16[6], myaddr.sin6_addr.s6_addr16[7]); + addrlen = sizeof(struct sockaddr_in6); #else myaddr.sin_family = AF_INET; myaddr.sin_port = HTONS(PORTNO); -#ifdef CONFIG_EXAMPLES_NETTEST_LOOPBACK +#ifdef CONFIG_EXAMPLES_NETTEST_LOOPBACK && defined(NET_LOOPBACK) myaddr.sin_addr.s_addr = HTONL(0x7f000001); #else myaddr.sin_addr.s_addr = HTONL(CONFIG_EXAMPLES_NETTEST_CLIENTIP); #endif + printf("IPv4 Address: %08x\n", myaddr.sin_addr.s_addr); + addrlen = sizeof(struct sockaddr_in); #endif diff --git a/examples/nettest/nettest_server.c b/examples/nettest/nettest_server.c index 990168a5b..ad55506a1 100644 --- a/examples/nettest/nettest_server.c +++ b/examples/nettest/nettest_server.c @@ -110,15 +110,33 @@ void recv_server(void) /* Bind the socket to a local address */ #ifdef CONFIG_EXAMPLES_NETTEST_IPv6 - myaddr.sin6_family = AF_INET6; - myaddr.sin6_port = HTONS(PORTNO); + + myaddr.sin6_family = AF_INET6; + myaddr.sin6_port = HTONS(PORTNO); + +#if defined(CONFIG_EXAMPLES_NETTEST_LOOPBACK) && !defined(NET_LOOPBACK) + myaddr.sin6_addr.s6_addr16[0] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_1); + myaddr.sin6_addr.s6_addr16[1] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_2); + myaddr.sin6_addr.s6_addr16[2] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_3); + myaddr.sin6_addr.s6_addr16[3] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_4); + myaddr.sin6_addr.s6_addr16[4] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_5); + myaddr.sin6_addr.s6_addr16[5] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_6); + myaddr.sin6_addr.s6_addr16[6] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_7); + myaddr.sin6_addr.s6_addr16[7] = HTONS(CONFIG_EXAMPLES_NETTEST_CLIENTIPv6ADDR_8); +#else memset(&myaddr.sin6_addr, 0, sizeof(struct in6_addr)); +#endif addrlen = sizeof(struct sockaddr_in6); #else - myaddr.sin_family = AF_INET; - myaddr.sin_port = HTONS(PORTNO); - myaddr.sin_addr.s_addr = INADDR_ANY; + myaddr.sin_family = AF_INET; + myaddr.sin_port = HTONS(PORTNO); + +#if defined(CONFIG_EXAMPLES_NETTEST_LOOPBACK) && !defined(NET_LOOPBACK) + myaddr.sin_addr.s_addr = HTONL(CONFIG_EXAMPLES_NETTEST_CLIENTIP); +#else + myaddr.sin_addr.s_addr = INADDR_ANY; +#endif addrlen = sizeof(struct sockaddr_in); #endif diff --git a/examples/usrsocktest/.gitignore b/examples/usrsocktest/.gitignore new file mode 100644 index 000000000..52ae9aa90 --- /dev/null +++ b/examples/usrsocktest/.gitignore @@ -0,0 +1,14 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.obj +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src +/*.hobj +/*.exe +/*.dSYM diff --git a/examples/usrsocktest/Kconfig b/examples/usrsocktest/Kconfig new file mode 100644 index 000000000..b6fee20af --- /dev/null +++ b/examples/usrsocktest/Kconfig @@ -0,0 +1,36 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config EXAMPLES_USRSOCKTEST + bool "USRSOCK test example" + default n + depends on NET && NET_USRSOCK && !DISABLE_POLL + select NET_USRSOCK_TCP + select NET_USRSOCK_UDP + select NET_SOCKOPTS + select PIPES + ---help--- + Enable the User Socket test example. This example application runs + unit-tests for /dev/usrsock. + +if EXAMPLES_USRSOCKTEST + +config EXAMPLES_USRSOCKTEST_PROGNAME + string "Program name" + default "usrsocktest" + depends on BUILD_KERNEL + ---help--- + This is the name of the program that will be use when the NSH ELF + program is installed. + +config EXAMPLES_USRSOCKTEST_PRIORITY + int "usrsocktest task priority" + default 100 + +config EXAMPLES_USRSOCKTEST_STACKSIZE + int "usrsocktest stack size" + default 4096 + +endif diff --git a/examples/usrsocktest/Make.defs b/examples/usrsocktest/Make.defs new file mode 100644 index 000000000..74f505b51 --- /dev/null +++ b/examples/usrsocktest/Make.defs @@ -0,0 +1,39 @@ +############################################################################ +# examples/usrsocktest/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2017 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_EXAMPLES_USRSOCKTEST),y) +CONFIGURED_APPS += examples/usrsocktest +endif diff --git a/examples/usrsocktest/Makefile b/examples/usrsocktest/Makefile new file mode 100644 index 000000000..5060cbfe4 --- /dev/null +++ b/examples/usrsocktest/Makefile @@ -0,0 +1,65 @@ +############################################################################ +# apps/examples/usrsocktest/Makefile +# +# Copyright (C) 2008, 2010-2013 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/Make.defs + +# USRSOCK Test built-in application info + +CONFIG_EXAMPLES_USRSOCKTEST_PRIORITY ?= SCHED_PRIORITY_DEFAULT +CONFIG_EXAMPLES_USRSOCKTEST_STACKSIZE ?= 4096 + +APPNAME = usrsocktest +PRIORITY = $(CONFIG_EXAMPLES_USRSOCKTEST_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_USRSOCKTEST_STACKSIZE) + +# NuttX USRSOCK Test + +ASRCS = +CSRCS = usrsocktest_daemon.c usrsocktest_basic_connect.c +CSRCS += usrsocktest_basic_daemon.c usrsocktest_basic_getsockname.c +CSRCS += usrsocktest_basic_getsockopt.c usrsocktest_basic_send.c +CSRCS += usrsocktest_basic_setsockopt.c usrsocktest_block_recv.c +CSRCS += usrsocktest_block_send.c usrsocktest_chardev.c +CSRCS += usrsocktest_multi_thread.c usrsocktest_noblock_connect.c +CSRCS += usrsocktest_noblock_recv.c usrsocktest_noblock_send.c +CSRCS += usrsocktest_nodaemon.c usrsocktest_poll.c +CSRCS += usrsocktest_remote_disconnect.c usrsocktest_wake_with_signal.c + +MAINSRC = usrsocktest_main.c + +CONFIG_EXAMPLES_USRSOCKTEST_PROGNAME ?= usrsocktest$(EXEEXT) +PROGNAME = $(CONFIG_EXAMPLES_USRSOCKTEST_PROGNAME) + +include $(APPDIR)/Application.mk diff --git a/examples/usrsocktest/defines.h b/examples/usrsocktest/defines.h new file mode 100644 index 000000000..271e39bc8 --- /dev/null +++ b/examples/usrsocktest/defines.h @@ -0,0 +1,212 @@ +/**************************************************************************** + * examples/usrsocktest/defines.h + * Common defines for all usrsock testcases + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __EXAMPLES_USRSOCKTEST_DEFINES_H +#define __EXAMPLES_USRSOCKTEST_DEFINES_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define USRSOCK_NODE "/dev/usrsock" + +#define USRSOCKTEST_DAEMON_CONF_DEFAULTS \ + { \ + .max_sockets = UINT_MAX, \ + .supported_domain = AF_INET, \ + .supported_type = SOCK_STREAM, \ + .supported_protocol = 0, \ + .delay_all_responses = false, \ + .endpoint_addr = "127.0.0.1", \ + .endpoint_port = 255, \ + .endpoint_block_connect = false, \ + .endpoint_block_send = false, \ + .endpoint_recv_avail_from_start = true, \ + .endpoint_recv_avail = 4, \ + } + +/* Test case macros */ + +#define utest_match(type, should_be_value, test_value) do { \ + type tmp_utest_test_value = (type)(test_value); \ + type tmp_utest_should_value = (type)(should_be_value); \ + if (!usrsocktest_assert_print_value(__func__, __LINE__, \ + "(" #type ")(" #test_value ") == " \ + "(" #type ")(" #should_be_value ")", \ + tmp_utest_test_value, \ + tmp_utest_should_value)) \ + { \ + usrsocktest_test_failed = true; \ + return; \ + } \ + } while(false) + +#define utest_match_buf(should_be_valuebuf, test_valuebuf, test_valuebuflen) do { \ + if (!usrsocktest_assert_print_buf(__func__, __LINE__, \ + "memcmp(" #should_be_valuebuf ", " \ + #test_valuebuf ", " \ + #test_valuebuflen ") == 0", \ + should_be_valuebuf, \ + test_valuebuf, \ + test_valuebuflen)) \ + { \ + usrsocktest_test_failed = true; \ + return; \ + } \ + } while(false) + +#define RUN_TEST_CASE(g,t) do { \ + if (usrsocktest_test_failed) \ + { \ + return; \ + } \ + usrsocktest_group_##g##_setup(); \ + usrsocktest_test_##g##_##t(); \ + usrsocktest_group_##g##_teardown(); \ + if (usrsocktest_test_failed) \ + { \ + return; \ + } \ + } while (false) + +#define RUN_TEST_GROUP(g) do { \ + extern const char *usrsocktest_group_##g; \ + void usrsocktest_group_##g##_run(void); \ + run_tests(usrsocktest_group_##g, usrsocktest_group_##g##_run); \ + } while (false) + +#define TEST_GROUP(g) const char *usrsocktest_group_##g = #g; \ + void usrsocktest_group_##g##_run(void) +#define TEST_SETUP(g) static void usrsocktest_group_##g##_setup(void) +#define TEST_TEAR_DOWN(g) static void usrsocktest_group_##g##_teardown(void) +#define TEST(g,t) static void usrsocktest_test_##g##_##t(void) + +#define TEST_ASSERT_TRUE(v) utest_match(bool, true, (v)) +#define TEST_ASSERT_FALSE(v) utest_match(bool, false, (v)) +#define TEST_ASSERT_EQUAL(e,v) utest_match(ssize_t, (e), (v)) +#define TEST_ASSERT_EQUAL_UINT8_ARRAY(e,v,s) utest_match_buf((e), (v), (s)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct usrsocktest_daemon_conf_s +{ + unsigned int max_sockets; + int8_t supported_domain:8; + int8_t supported_type:8; + int8_t supported_protocol:8; + bool delay_all_responses:1; + bool endpoint_block_connect:1; + bool endpoint_block_send:1; + bool endpoint_recv_avail_from_start:1; + uint8_t endpoint_recv_avail:8; + const char *endpoint_addr; + uint16_t endpoint_port; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern int usrsocktest_endp_malloc_cnt; +extern int usrsocktest_dcmd_malloc_cnt; +extern const struct usrsocktest_daemon_conf_s usrsocktest_daemon_defconf; +extern struct usrsocktest_daemon_conf_s usrsocktest_daemon_config; + +extern bool usrsocktest_test_failed; + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +bool usrsocktest_assert_print_value(FAR const char *func, + const int line, + FAR const char *check_str, + long int test_value, + long int should_be); + +bool usrsocktest_assert_print_buf(FAR const char *func, + const int line, + FAR const char *check_str, + FAR const void *test_buf, + FAR const void *expect_buf, + size_t buflen); + +int usrsocktest_daemon_start(FAR const struct usrsocktest_daemon_conf_s *conf); + +int usrsocktest_daemon_stop(void); + +int usrsocktest_daemon_get_num_active_sockets(void); + +int usrsocktest_daemon_get_num_connected_sockets(void); + +int usrsocktest_daemon_get_num_waiting_connect_sockets(void); + +int usrsocktest_daemon_get_num_recv_empty_sockets(void); + +ssize_t usrsocktest_daemon_get_send_bytes(void); + +ssize_t usrsocktest_daemon_get_recv_bytes(void); + +int usrsocktest_daemon_get_num_unreachable_sockets(void); + +int usrsocktest_daemon_get_num_remote_disconnected_sockets(void); + +bool usrsocktest_daemon_establish_waiting_connections(void); + +bool usrsocktest_send_delayed_command(char cmd, unsigned int delay_msec); + +int usrsocktest_daemon_pause_usrsock_handling(bool pause); + +#endif /* __EXAMPLES_USRSOCKTEST_DEFINES_H */ diff --git a/examples/usrsocktest/usrsocktest_basic_connect.c b/examples/usrsocktest/usrsocktest_basic_connect.c new file mode 100644 index 000000000..0875cbd6b --- /dev/null +++ b/examples/usrsocktest/usrsocktest_basic_connect.c @@ -0,0 +1,488 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_basic_connect.c + * Basic connect tests with socket daemon + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: Setup + * + * Description: + * Run before every testcase + * + * Input Parameters: + * dconf - test daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Setup(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); +} + +/**************************************************************************** + * Name: Teardown + * + * Description: + * Run after every testcase + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Teardown(void) +{ + int ret; + + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +/**************************************************************************** + * Name: NotConnected + * + * Description: + * Opened socket is not connected + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void NotConnected(void) +{ + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); +} + +/**************************************************************************** + * Name: Connect + * + * Description: + * Open and connect the socket + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Connect(void) +{ + int ret; + struct sockaddr_in addr; + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + + /* Attempt reconnect to same address */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Attempt reconnect to different address */ + + inet_pton(AF_INET, "1.1.1.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(1); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: WrongAF + * + * Description: + * Open and connect the socket with wrong AF + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void WrongAF(void) +{ + int ret; + struct sockaddr_in addr; + + /* Try connect socket with wrong AF */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_UNIX; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAFNOSUPPORT, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: WrongPort + * + * Description: + * Open and connect the socket with wrong port + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void WrongPort(void) +{ + int ret; + struct sockaddr_in addr; + + /* Try connect socket to wrong port */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(127); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(ECONNREFUSED, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: WrongEndpoint + * + * Description: + * Open and connect the socket with wrong endpoint + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void WrongEndpoint(void) +{ + int ret; + struct sockaddr_in addr; + + /* Try connect socket to unreachable endpoint */ + + inet_pton(AF_INET, "1.1.1.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(1); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(ENETUNREACH, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Connect */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +TEST_SETUP(BasicConnect) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Setup(&usrsocktest_daemon_config); +} + +TEST_TEAR_DOWN(BasicConnect) +{ + Teardown(); +} + +TEST(BasicConnect, NotConnected) +{ + NotConnected(); +} + +TEST(BasicConnect, Connect) +{ + Connect(); +} + +TEST(BasicConnect, WrongAF) +{ + WrongAF(); +} + +TEST(BasicConnect, WrongPort) +{ + WrongPort(); +} + +TEST(BasicConnect, WrongEndpoint) +{ + WrongEndpoint(); +} + +TEST_SETUP(BasicConnectDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Setup(&usrsocktest_daemon_config); +} + +TEST_TEAR_DOWN(BasicConnectDelay) +{ + Teardown(); +} + +TEST(BasicConnectDelay, NotConnected) +{ + NotConnected(); +} + +TEST(BasicConnectDelay, Connect) +{ + Connect(); +} + +TEST(BasicConnectDelay, WrongAF) +{ + WrongAF(); +} + +TEST(BasicConnectDelay, WrongPort) +{ + WrongPort(); +} + +TEST(BasicConnectDelay, WrongEndpoint) +{ + WrongEndpoint(); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BasicConnect) +{ + RUN_TEST_CASE(BasicConnect, NotConnected); + RUN_TEST_CASE(BasicConnect, Connect); + RUN_TEST_CASE(BasicConnect, WrongAF); + RUN_TEST_CASE(BasicConnect, WrongPort); + RUN_TEST_CASE(BasicConnect, WrongEndpoint); +} + +TEST_GROUP(BasicConnectDelay) +{ + RUN_TEST_CASE(BasicConnectDelay, NotConnected); + RUN_TEST_CASE(BasicConnectDelay, Connect); + RUN_TEST_CASE(BasicConnectDelay, WrongAF); + RUN_TEST_CASE(BasicConnectDelay, WrongPort); + RUN_TEST_CASE(BasicConnectDelay, WrongEndpoint); +} + diff --git a/examples/usrsocktest/usrsocktest_basic_daemon.c b/examples/usrsocktest/usrsocktest_basic_daemon.c new file mode 100644 index 000000000..10dbeacf4 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_basic_daemon.c @@ -0,0 +1,686 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_basic_daemon.c + * Basic tests with socket daemon + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd, sd2, sd3; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: NoActiveSockets + * + * Description: + * Checks there is no active sockets on daemon startup + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void NoActiveSockets(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); +} + +/**************************************************************************** + * Name: OpenClose + * + * Description: + * Open and close AF_INET socket, check active socket counter updates + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void OpenClose(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: UnsupportedType + * + * Description: + * Try open socket for unsupported type + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void UnsupportedType(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_RDM, 0); + TEST_ASSERT_TRUE(sd < 0); + TEST_ASSERT_EQUAL(EPROTONOSUPPORT, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: UnsupportedProto + * + * Description: + * Try open socket for unsupported protocol + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void UnsupportedProto(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 1); + TEST_ASSERT_TRUE(sd < 0); + TEST_ASSERT_EQUAL(EPROTONOSUPPORT, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: OpenThree + * + * Description: + * Open multiple sockets + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void OpenThree(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + + dconf->max_sockets = 3; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + sd2 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + sd3 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd3 >= 0); + TEST_ASSERT_EQUAL(3, usrsocktest_daemon_get_num_active_sockets()); + ret = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_FALSE(ret >= 0); + TEST_ASSERT_EQUAL(3, usrsocktest_daemon_get_num_active_sockets()); + + ret = close(sd2); + TEST_ASSERT_EQUAL(0, ret); + sd2 = -1; + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + ret = close(sd); + TEST_ASSERT_EQUAL(0, ret); + sd = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + ret = close(sd3); + TEST_ASSERT_EQUAL(0, ret); + sd3 = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Dup + * + * Description: + * Dup opened socket + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Dup(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + sd2 = dup(sd); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + sd3 = dup(sd); + TEST_ASSERT_TRUE(sd3 >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + ret = close(sd2); + TEST_ASSERT_EQUAL(0, ret); + sd2 = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + ret = close(sd); + TEST_ASSERT_EQUAL(0, ret); + sd = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + ret = close(sd3); + TEST_ASSERT_EQUAL(0, ret); + sd3 = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Dup2 + * + * Description: + * Clone opened socket with dup2 + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Dup2(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + sd2 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + + ret = dup2(sd2, sd); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + ret = close(sd2); + TEST_ASSERT_EQUAL(0, ret); + sd2 = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + ret = close(sd); + TEST_ASSERT_EQUAL(0, ret); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Stops + * + * Description: + * Daemon stops unexpectedly + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Stops(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + sd2 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + + ret = usrsocktest_daemon_stop(); + TEST_ASSERT_EQUAL(OK, ret); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + + + TEST_ASSERT_EQUAL(0, close(sd)); + sd = -1; + TEST_ASSERT_EQUAL(0, close(sd2)); + sd2 = -1; +} + +/**************************************************************************** + * Name: StopsStarts + * + * Description: + * Daemon stops and restarts unexpectedly + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void StopsStarts(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + struct sockaddr_in addr; + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + sd2 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = 255; + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EPIPE, errno); + + ret = connect(sd2, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EPIPE, errno); + + TEST_ASSERT_EQUAL(0, close(sd)); + sd = -1; + TEST_ASSERT_EQUAL(0, close(sd2)); + sd2 = -1; + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: BasicDaemon test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(BasicDaemon) +{ + sd = -1; + sd2 = -1; + sd3 = -1; + started = false; +} + +/**************************************************************************** + * Name: BasicDaemon test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(BasicDaemon) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (sd2 >= 0) + { + ret = close(sd2); + assert(ret >= 0); + } + if (sd3 >= 0) + { + ret = close(sd3); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(BasicDaemon, NoActiveSockets) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + NoActiveSockets(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, NoActiveSocketsDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + NoActiveSockets(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, OpenClose) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + OpenClose(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, OpenCloseDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + OpenClose(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, UnsupportedType) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + UnsupportedType(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, UnsupportedTypeDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + UnsupportedType(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, UnsupportedProto) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + UnsupportedProto(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, UnsupportedProtoDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + UnsupportedProto(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, OpenThree) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + OpenThree(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, OpenThreeDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + OpenThree(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, Dup) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Dup(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, DupDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Dup(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, Dup2) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Dup2(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, Dup2Delay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Dup2(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, Stops) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Stops(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, StopsDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Stops(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, StopsStarts) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + StopsStarts(&usrsocktest_daemon_config); +} + +TEST(BasicDaemon, StopsStartsDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + StopsStarts(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BasicDaemon) +{ + RUN_TEST_CASE(BasicDaemon, NoActiveSockets); + RUN_TEST_CASE(BasicDaemon, NoActiveSocketsDelay); + RUN_TEST_CASE(BasicDaemon, OpenClose); + RUN_TEST_CASE(BasicDaemon, OpenCloseDelay); + RUN_TEST_CASE(BasicDaemon, UnsupportedType); + RUN_TEST_CASE(BasicDaemon, UnsupportedTypeDelay); + RUN_TEST_CASE(BasicDaemon, UnsupportedProto); + RUN_TEST_CASE(BasicDaemon, UnsupportedProtoDelay); + RUN_TEST_CASE(BasicDaemon, OpenThree); + RUN_TEST_CASE(BasicDaemon, OpenThreeDelay); + RUN_TEST_CASE(BasicDaemon, Dup); + RUN_TEST_CASE(BasicDaemon, DupDelay); + RUN_TEST_CASE(BasicDaemon, Dup2); + RUN_TEST_CASE(BasicDaemon, Dup2Delay); + RUN_TEST_CASE(BasicDaemon, Stops); + RUN_TEST_CASE(BasicDaemon, StopsDelay); + RUN_TEST_CASE(BasicDaemon, StopsStarts); + RUN_TEST_CASE(BasicDaemon, StopsStartsDelay); +} + diff --git a/examples/usrsocktest/usrsocktest_basic_getsockname.c b/examples/usrsocktest/usrsocktest_basic_getsockname.c new file mode 100644 index 000000000..993dbe00a --- /dev/null +++ b/examples/usrsocktest/usrsocktest_basic_getsockname.c @@ -0,0 +1,287 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_basic_getsockname.c + * Basic getsockname tests + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: Open + * + * Description: + * Open and get socket options + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Open(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + socklen_t addrlen; + union { + struct sockaddr_storage storage; + struct sockaddr_in in; + } addr; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get socket name */ + + memset(&addr, 0xff, sizeof(addr)); + addrlen = sizeof(addr.in); + ret = getsockname(sd, (FAR void *)&addr.in, &addrlen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(sizeof(addr.in), addrlen); + TEST_ASSERT_EQUAL(AF_INET, addr.in.sin_family); + TEST_ASSERT_EQUAL(htons(12345), addr.in.sin_port); + TEST_ASSERT_EQUAL(htonl(0x7f000001), addr.in.sin_addr.s_addr); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get socket name, short buffer */ + + memset(&addr, 0xff, sizeof(addr)); + addrlen = 1; + ret = getsockname(sd, (FAR void *)&addr.in, &addrlen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(sizeof(addr.in), addrlen); + TEST_ASSERT_EQUAL(AF_INET, ((FAR uint8_t *)&addr.in.sin_family)[0]); + TEST_ASSERT_EQUAL(0xff, ((FAR uint8_t *)&addr.in.sin_family)[1]); + TEST_ASSERT_EQUAL(htons(0xffff), addr.in.sin_port); + TEST_ASSERT_EQUAL(htonl(0xffffffff), addr.in.sin_addr.s_addr); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get socket name, larger buffer */ + + memset(&addr, 0xff, sizeof(addr)); + addrlen = sizeof(addr.storage); + ret = getsockname(sd, (FAR void *)&addr.in, &addrlen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(sizeof(addr.in), addrlen); + TEST_ASSERT_EQUAL(AF_INET, addr.in.sin_family); + TEST_ASSERT_EQUAL(htons(12345), addr.in.sin_port); + TEST_ASSERT_EQUAL(htonl(0x7f000001), addr.in.sin_addr.s_addr); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get socket name, NULL addr */ + + memset(&addr, 0xff, sizeof(addr)); + addrlen = sizeof(addr.in); + ret = getsockname(sd, NULL, &addrlen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINVAL, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get socket name, NULL addrlen */ + + memset(&addr, 0xff, sizeof(addr)); + addrlen = sizeof(addr.in); + ret = getsockname(sd, (FAR void *)&addr.in, NULL); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINVAL, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get socket name, zero length buffer */ + + memset(&addr, 0xff, sizeof(addr)); + addrlen = 0; + ret = getsockname(sd, (FAR void *)&addr.in, &addrlen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(sizeof(addr.in), addrlen); + TEST_ASSERT_EQUAL(0xffff, addr.in.sin_family); + TEST_ASSERT_EQUAL(htons(0xffff), addr.in.sin_port); + TEST_ASSERT_EQUAL(htonl(0xffffffff), addr.in.sin_addr.s_addr); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: BasicGetSockName test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(BasicGetSockName) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: BasicGetSockName test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(BasicGetSockName) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(BasicGetSockName, Open) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Open(&usrsocktest_daemon_config); +} + +TEST(BasicGetSockName, OpenDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Open(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BasicGetSockName) +{ + RUN_TEST_CASE(BasicGetSockName, Open); + RUN_TEST_CASE(BasicGetSockName, OpenDelay); +} diff --git a/examples/usrsocktest/usrsocktest_basic_getsockopt.c b/examples/usrsocktest/usrsocktest_basic_getsockopt.c new file mode 100644 index 000000000..2278bfabf --- /dev/null +++ b/examples/usrsocktest/usrsocktest_basic_getsockopt.c @@ -0,0 +1,266 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_basic_getsockopt.c + * Basic getsockopt tests + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: Open + * + * Description: + * Open and get socket options + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Open(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + int value; + int valuelarge[2]; + socklen_t valuelen; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get supported option (SO_TYPE handled by nuttx) */ + + value = -2; + valuelen = sizeof(value); + ret = getsockopt(sd, SOL_SOCKET, SO_TYPE, &value, &valuelen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(sizeof(value), valuelen); + TEST_ASSERT_EQUAL(SOCK_STREAM, value); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get supported option */ + + value = -1; + valuelen = sizeof(value); + ret = getsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &value, &valuelen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(sizeof(value), valuelen); + TEST_ASSERT_EQUAL(0, value); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get supported option, too short buffer */ + + value = -1; + valuelen = 1; + ret = getsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &value, &valuelen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINVAL, errno); + TEST_ASSERT_EQUAL(-1, value); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get supported option, larger buffer */ + + valuelarge[0] = -1; + valuelarge[1] = -1; + valuelen = sizeof(valuelarge); + ret = getsockopt(sd, SOL_SOCKET, SO_REUSEADDR, valuelarge, &valuelen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(sizeof(value), valuelen); + TEST_ASSERT_EQUAL(0, valuelarge[0]); + TEST_ASSERT_EQUAL(-1, valuelarge[1]); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Get unsupported option */ + + value = -1; + valuelen = sizeof(value); + ret = getsockopt(sd, SOL_SOCKET, SO_BROADCAST, &value, &valuelen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(ENOPROTOOPT, errno); + TEST_ASSERT_EQUAL(-1, value); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: BasicGetSockOpt test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(BasicGetSockOpt) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: BasicGetSockOpt test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(BasicGetSockOpt) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(BasicGetSockOpt, Open) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Open(&usrsocktest_daemon_config); +} + +TEST(BasicGetSockOpt, OpenDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Open(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BasicGetSockOpt) +{ + RUN_TEST_CASE(BasicGetSockOpt, Open); + RUN_TEST_CASE(BasicGetSockOpt, OpenDelay); +} diff --git a/examples/usrsocktest/usrsocktest_basic_send.c b/examples/usrsocktest/usrsocktest_basic_send.c new file mode 100644 index 000000000..b54470f0e --- /dev/null +++ b/examples/usrsocktest/usrsocktest_basic_send.c @@ -0,0 +1,359 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_basic_send.c + * Basic sending tests + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: Send + * + * Description: + * Open socket and send + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Send(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + struct sockaddr_in addr; + ssize_t ret; + size_t datalen; + const void *data; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Send on non-connected socket */ + + data = "data"; + datalen = strlen("data"); + ret = send(sd, data, datalen, 0); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(ENOTCONN, errno); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Sendto with address */ + + data = "data"; + datalen = strlen("data"); + ret = sendto(sd, data, datalen, 0, (FAR const struct sockaddr *)&addr, + sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(ENOTCONN, errno); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Connect socket to available endpoint */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + + /* Sendto with address */ + + data = "data"; + datalen = strlen("data"); + ret = sendto(sd, data, datalen, 0, (FAR const struct sockaddr *)&addr, + sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, + usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_send_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + +} + +/**************************************************************************** + * Name: ConnectSend + * + * Description: + * Send over connected socket + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ +static void ConnectSend(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + struct sockaddr_in addr; + ssize_t ret; + size_t datalen; + const void *data; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Connect socket to available endpoint */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Send data to remote */ + + data = "data"; + datalen = strlen("data"); + ret = send(sd, data, datalen, 0); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(datalen, usrsocktest_daemon_get_send_bytes()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(datalen, usrsocktest_daemon_get_send_bytes()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, + usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: BasicSend test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(BasicSend) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: BasicSend test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(BasicSend) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(BasicSend, Send) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Send(&usrsocktest_daemon_config); +} + +TEST(BasicSend, SendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Send(&usrsocktest_daemon_config); +} + +TEST(BasicSend, ConnectSend) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + ConnectSend(&usrsocktest_daemon_config); +} + +TEST(BasicSend, ConnectSendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + ConnectSend(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BasicSend) +{ + RUN_TEST_CASE(BasicSend, Send); + RUN_TEST_CASE(BasicSend, SendDelay); + RUN_TEST_CASE(BasicSend, ConnectSend); + RUN_TEST_CASE(BasicSend, ConnectSendDelay); +} diff --git a/examples/usrsocktest/usrsocktest_basic_setsockopt.c b/examples/usrsocktest/usrsocktest_basic_setsockopt.c new file mode 100644 index 000000000..bae4d444b --- /dev/null +++ b/examples/usrsocktest/usrsocktest_basic_setsockopt.c @@ -0,0 +1,235 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_basic_setsockopt.c + * Basic setsockopt tests + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: Open + * + * Description: + * Open and set socket options + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Open(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + int ret; + int value; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Set supported option */ + + value = 1; + ret = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Set supported option, too small buffer */ + + value = 1; + ret = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &value, 1); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINVAL, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Set unsupported option */ + + value = 1; + ret = setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &value, sizeof(value)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(ENOPROTOOPT, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: BasicSetSockOpt test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(BasicSetSockOpt) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: BasicSetSockOpt test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(BasicSetSockOpt) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(BasicSetSockOpt, Open) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Open(&usrsocktest_daemon_config); +} + +TEST(BasicSetSockOpt, OpenDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Open(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BasicSetSockOpt) +{ + RUN_TEST_CASE(BasicSetSockOpt, Open); + RUN_TEST_CASE(BasicSetSockOpt, OpenDelay); +} + diff --git a/examples/usrsocktest/usrsocktest_block_recv.c b/examples/usrsocktest/usrsocktest_block_recv.c new file mode 100644 index 000000000..2d7cf9713 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_block_recv.c @@ -0,0 +1,514 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_block_recv.c + * Receive from the socket in blocking mode + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ConnectReceive + * + * Description: + * Blocking connect and receive + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void ConnectReceive(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[5]; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = true; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 7; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Do connect, should succeed (after connect block released). */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('E', 100)); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote */ + + data = databuf; + datalen = sizeof(databuf); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('r', 100)); + ret = recvfrom(sd, data, datalen, 0, NULL, 0); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(5, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abcde", data, 5); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(5, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, 0); + TEST_ASSERT_EQUAL(2, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("ab", data, 2); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(7, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: NoBlockConnect + * + * Description: + * Non-blocking connect and blocking receive + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void NoBlockConnect(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[5]; + struct sockaddr_in remoteaddr; + socklen_t addrlen; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = true; + dconf->endpoint_recv_avail_from_start = true; + dconf->endpoint_recv_avail = 6; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Do connect, should succeed (after connect block released). */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('W', 100)); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('E', 100)); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote */ + + data = databuf; + datalen = sizeof(databuf); + ret = read(sd, data, datalen); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(5, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abcde", data, 5); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(5, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote */ + + data = databuf; + datalen = sizeof(databuf); + addrlen = sizeof(remoteaddr); + ret = recvfrom(sd, data, datalen, 0, (FAR struct sockaddr *)&remoteaddr, + &addrlen); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("a", data, 1); + TEST_ASSERT_EQUAL(sizeof(remoteaddr), addrlen); + TEST_ASSERT_EQUAL_UINT8_ARRAY(&remoteaddr, &addr, addrlen); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(6, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote */ + + data = databuf; + datalen = sizeof(databuf); + addrlen = sizeof(remoteaddr); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('r', 100)); + ret = recvfrom(sd, data, datalen, 0, (FAR struct sockaddr *)&remoteaddr, + &addrlen); + TEST_ASSERT_EQUAL(5, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abcde", data, 5); + TEST_ASSERT_EQUAL(sizeof(remoteaddr), addrlen); + TEST_ASSERT_EQUAL_UINT8_ARRAY(&remoteaddr, &addr, addrlen); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(11, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(11, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: ReceiveTimeout + * + * Description: + * Blocking connect and receive with SO_RCVTIMEO + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void ReceiveTimeout(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[5]; + struct timeval tv; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = true; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 7; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Do connect, should succeed (after connect block released). */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('E', 100)); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Setup recv timeout. */ + + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + ret = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (FAR const void *)&tv, + sizeof(tv)); + TEST_ASSERT_EQUAL(0, ret); + + /* Receive data from remote */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, 0); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: BlockRecv test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(BlockRecv) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: BlockRecv test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(BlockRecv) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(BlockRecv, ConnectReceive) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + ConnectReceive(&usrsocktest_daemon_config); +} + +TEST(BlockRecv, ConnectReceiveDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + ConnectReceive(&usrsocktest_daemon_config); +} + +TEST(BlockRecv, NoBlockConnect) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + NoBlockConnect(&usrsocktest_daemon_config); +} + +TEST(BlockRecv, NoBlockConnectDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + NoBlockConnect(&usrsocktest_daemon_config); +} + +TEST(BlockRecv, ReceiveTimeout) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + ReceiveTimeout(&usrsocktest_daemon_config); +} + +TEST(BlockRecv, ReceiveTimeoutDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + ReceiveTimeout(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BlockRecv) +{ + RUN_TEST_CASE(BlockRecv, ConnectReceive); + RUN_TEST_CASE(BlockRecv, ConnectReceiveDelay); + RUN_TEST_CASE(BlockRecv, NoBlockConnect); + RUN_TEST_CASE(BlockRecv, NoBlockConnectDelay); + RUN_TEST_CASE(BlockRecv, ReceiveTimeout); + RUN_TEST_CASE(BlockRecv, ReceiveTimeoutDelay); +} + diff --git a/examples/usrsocktest/usrsocktest_block_send.c b/examples/usrsocktest/usrsocktest_block_send.c new file mode 100644 index 000000000..d917607e5 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_block_send.c @@ -0,0 +1,442 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_block_send.c + * Send through the socket in blocking mode + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ConnectSend + * + * Description: + * Open socket, connect in blocking mode and send + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void ConnectSend(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + const void *data; + struct sockaddr_in addr; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Do connect, should succeed (after connect block released). */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('E', 100)); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Send data to remote, */ + + data = "abcde"; + datalen = strlen("abcde"); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('W', 100)); + ret = sendto(sd, data, datalen, 0, NULL, 0); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(datalen, usrsocktest_daemon_get_send_bytes()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: NonBlockConnectSend + * + * Description: + * Open socket, connect in non-blocking mode and send + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void NonBlockConnectSend(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + const void *data; + struct sockaddr_in addr; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = false; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Do connect, should succeed (after connect block released). */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('W', 100)); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('E', 100)); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Send data to remote. */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = write(sd, data, datalen); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(datalen, usrsocktest_daemon_get_send_bytes()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: SendTimeout + * + * Description: + * Open socket, connect in blocking mode and send with SO_SNDTIMEO + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void SendTimeout(FAR struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + const void *data; + struct sockaddr_in addr; + struct timeval tv; + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Do connect, should succeed (after connect block released). */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('E', 100)); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Setup send timeout. */ + + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + ret = setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (FAR const void *)&tv, + sizeof(tv)); + TEST_ASSERT_EQUAL(0, ret); + + /* Try send data to remote. */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = sendto(sd, data, datalen, 0, NULL, 0); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: BlockSend test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(BlockSend) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: BlockSend test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(BlockSend) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(BlockSend, ConnectSend) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + ConnectSend(&usrsocktest_daemon_config); +} + +TEST(BlockSend, ConnectSendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + ConnectSend(&usrsocktest_daemon_config); +} + +TEST(BlockSend, NonBlockConnectSend) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + NonBlockConnectSend(&usrsocktest_daemon_config); +} + +TEST(BlockSend, NonBlockConnectSendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + NonBlockConnectSend(&usrsocktest_daemon_config); +} + +TEST(BlockSend, SendTimeout) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + SendTimeout(&usrsocktest_daemon_config); +} + +TEST(BlockSend, SendTimeoutDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + SendTimeout(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(BlockSend) +{ + RUN_TEST_CASE(BlockSend, ConnectSend); + RUN_TEST_CASE(BlockSend, ConnectSendDelay); + RUN_TEST_CASE(BlockSend, NonBlockConnectSend); + RUN_TEST_CASE(BlockSend, NonBlockConnectSendDelay); + RUN_TEST_CASE(BlockSend, SendTimeout); + RUN_TEST_CASE(BlockSend, SendTimeoutDelay); +} + diff --git a/examples/usrsocktest/usrsocktest_chardev.c b/examples/usrsocktest/usrsocktest_chardev.c new file mode 100644 index 000000000..745a54970 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_chardev.c @@ -0,0 +1,234 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_chardev.c + * Character device node tests + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int us_fd; +static int us_fd_two; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: CharDev test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(CharDev) +{ + us_fd = -1; + us_fd_two = -1; +} + +/**************************************************************************** + * Name: CharDev test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(CharDev) +{ + int ret; + + if (us_fd >= 0) + { + ret = close(us_fd); + assert(ret >= 0); + } + if (us_fd_two >= 0) + { + ret = close(us_fd_two); + assert(ret >= 0); + } +} + +/**************************************************************************** + * Name: OpenRw + * + * Description: + * Simple test for opening and closing usrsock node + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(CharDev, OpenRw) +{ + int ret; + + us_fd = open(USRSOCK_NODE, O_RDWR); + TEST_ASSERT_TRUE(us_fd >= 0); + + ret = close(us_fd); + TEST_ASSERT_TRUE(ret >= 0); + us_fd = -1; +} + +/**************************************************************************** + * Name: ReopenRw + * + * Description: + * Repeated simple test for opening and closing usrsock node, reopen should + * work + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(CharDev, ReopenRw) +{ + int ret; + + us_fd = open(USRSOCK_NODE, O_RDWR); + TEST_ASSERT_TRUE(us_fd >= 0); + + ret = close(us_fd); + TEST_ASSERT_TRUE(ret >= 0); + us_fd = -1; + + us_fd = open(USRSOCK_NODE, O_RDWR); + TEST_ASSERT_TRUE(us_fd >= 0); + + ret = close(us_fd); + TEST_ASSERT_TRUE(ret >= 0); + us_fd = -1; +} + +/**************************************************************************** + * Name: NoMultipleOpen + * + * Description: + * No permission for multiple access, + * first user should be only user. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(CharDev, NoMultipleOpen) +{ + us_fd = open(USRSOCK_NODE, O_RDWR); + TEST_ASSERT_TRUE(us_fd >= 0); + + us_fd_two = open(USRSOCK_NODE, O_RDWR); + TEST_ASSERT_FALSE(us_fd_two >= 0); + TEST_ASSERT_EQUAL(EPERM, errno); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(CharDev) +{ + RUN_TEST_CASE(CharDev, OpenRw); + RUN_TEST_CASE(CharDev, ReopenRw); + RUN_TEST_CASE(CharDev, NoMultipleOpen); +} + diff --git a/examples/usrsocktest/usrsocktest_daemon.c b/examples/usrsocktest/usrsocktest_daemon.c new file mode 100644 index 000000000..64f0ffae8 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_daemon.c @@ -0,0 +1,1948 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_daemon.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#ifndef dbg + #define dbg _warn +#endif + +#define usrsocktest_dbg(...) ((void)0) + +#define TEST_SOCKET_SOCKID_BASE 10000U +#define TEST_SOCKET_COUNT 8 + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#define noinline + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct test_socket_s +{ + bool opened:1; + bool connected:1; + bool blocked_connect:1; + bool block_send:1; + bool connect_refused:1; + bool disconnected:1; + int recv_avail_bytes; + FAR void *endp; + struct usrsock_message_req_ack_s pending_resp; +}; + +struct delayed_cmd_s +{ + sq_entry_t node; + pthread_t tid; + sem_t startsem; + int pipefd; + uint16_t delay_msec; + char cmd; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct daemon_priv_s +{ + FAR const struct usrsocktest_daemon_conf_s *conf; + pthread_t tid; + bool joined; + + int pipefd[2]; + sem_t wakewaitsem; + unsigned int sockets_active; + unsigned int sockets_connected; + unsigned int sockets_waiting_connect; + unsigned int sockets_recv_empty; + unsigned int sockets_not_connected_refused; + unsigned int sockets_remote_disconnected; + size_t total_send_bytes; + size_t total_recv_bytes; + bool do_not_poll_usrsock; + + struct test_socket_s test_sockets[TEST_SOCKET_COUNT]; + sq_queue_t delayed_cmd_threads; +} daemon = + { + .joined = true, + .conf = NULL, + }; + +static pthread_mutex_t daemon_mutex = PTHREAD_MUTEX_INITIALIZER; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +const struct usrsocktest_daemon_conf_s usrsocktest_daemon_defconf = + USRSOCKTEST_DAEMON_CONF_DEFAULTS; +struct usrsocktest_daemon_conf_s usrsocktest_daemon_config; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int test_socket_alloc(FAR struct daemon_priv_s *priv) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(priv->test_sockets); i++) + { + FAR struct test_socket_s *tsock = &priv->test_sockets[i]; + + if (!tsock->opened) + { + memset(tsock, 0, sizeof(*tsock)); + tsock->opened = true; + tsock->block_send = priv->conf->endpoint_block_send; + tsock->recv_avail_bytes = + priv->conf->endpoint_recv_avail_from_start ? + priv->conf->endpoint_recv_avail : 0; + priv->sockets_active++; + if (tsock->recv_avail_bytes == 0) + priv->sockets_recv_empty++; + return i + TEST_SOCKET_SOCKID_BASE; + } + } + + return -1; +} + +static FAR struct test_socket_s *test_socket_get(FAR struct daemon_priv_s *priv, + int sockid) +{ + if (sockid < TEST_SOCKET_SOCKID_BASE) + return NULL; + + sockid -= TEST_SOCKET_SOCKID_BASE; + if (sockid >= ARRAY_SIZE(priv->test_sockets)) + return NULL; + + return &priv->test_sockets[sockid]; +} + +static int test_socket_free(FAR struct daemon_priv_s *priv, int sockid) +{ + FAR struct test_socket_s *tsock = test_socket_get(priv, sockid); + + if (!tsock) + return -EBADFD; + + if (!tsock->opened) + return -EFAULT; + + if (tsock->connected) + { + priv->sockets_connected--; + tsock->connected = false; + } + if (tsock->blocked_connect) + { + priv->sockets_waiting_connect--; + tsock->blocked_connect = false; + } + if (tsock->endp) + { + free(tsock->endp); + usrsocktest_endp_malloc_cnt--; + tsock->endp = NULL; + } + if (tsock->recv_avail_bytes == 0) + { + priv->sockets_recv_empty--; + } + if (tsock->connect_refused) + { + priv->sockets_not_connected_refused--; + } + if (tsock->disconnected) + { + priv->sockets_remote_disconnected--; + } + + tsock->opened = false; + priv->sockets_active--; + + return 0; +} + +static int tsock_send_event(int fd, FAR struct daemon_priv_s *priv, + FAR struct test_socket_s *tsock, int events) +{ + FAR struct usrsock_message_socket_event_s event = {}; + ssize_t wlen; + int i; + + event.head.flags = USRSOCK_MESSAGE_FLAG_EVENT; + event.head.msgid = USRSOCK_MESSAGE_SOCKET_EVENT; + + for (i = 0; i < ARRAY_SIZE(priv->test_sockets); i++) + { + if (tsock == &priv->test_sockets[i]) + break; + } + + if (i == ARRAY_SIZE(priv->test_sockets)) + return -EINVAL; + + event.usockid = i + TEST_SOCKET_SOCKID_BASE; + event.events = events; + + wlen = write(fd, &event, sizeof(event)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(event)) + return -ENOSPC; + + return OK; +} + + +static FAR void *find_endpoint(FAR struct daemon_priv_s *priv, + in_addr_t ipaddr) +{ + FAR struct sockaddr_in *endpaddr; + int ok; + + endpaddr = malloc(sizeof(*endpaddr)); + usrsocktest_endp_malloc_cnt++; + assert(endpaddr); + + ok = inet_pton(AF_INET, priv->conf->endpoint_addr, + &endpaddr->sin_addr.s_addr); + endpaddr->sin_family = AF_INET; + endpaddr->sin_port = htons(priv->conf->endpoint_port); + assert(ok); + + if (endpaddr->sin_addr.s_addr == ipaddr) + return endpaddr; + + free(endpaddr); + usrsocktest_endp_malloc_cnt--; + return NULL; +} + +static bool endpoint_connect(FAR struct daemon_priv_s *priv, FAR void *endp, + uint16_t port) +{ + FAR struct sockaddr_in *endpaddr = endp; + + if (endpaddr->sin_port == port) + return true; + else + return false; +} + +static void get_endpoint_sockaddr(FAR void *endp, + FAR struct sockaddr_in *endpaddr) +{ + *endpaddr = *(FAR struct sockaddr_in *)endp; +} + +static ssize_t +read_req(int fd, FAR const struct usrsock_request_common_s *common_hdr, + FAR void *req, size_t reqsize) +{ + ssize_t rlen; + int err; + + rlen = read(fd, (uint8_t *)req + sizeof(*common_hdr), + reqsize - sizeof(*common_hdr)); + if (rlen < 0) + { + err = errno; + usrsocktest_dbg("Error reading %d bytes of request: ret=%d, errno=%d\n", + reqsize - sizeof(*common_hdr), (int)rlen, errno); + return -err; + } + if (rlen + sizeof(*common_hdr) != reqsize) + { + return -EMSGSIZE; + } + + return rlen; +} + +static int socket_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_socket_s *req = hdrbuf; + struct usrsock_message_req_ack_s resp = {}; + int socketid; + ssize_t wlen; + + /* Validate input. */ + + if (req->domain != priv->conf->supported_domain) + { + socketid = -EAFNOSUPPORT; + } + else if (req->type != priv->conf->supported_type || + req->protocol != priv->conf->supported_protocol) + { + socketid = -EPROTONOSUPPORT; + } + else if (priv->sockets_active >= priv->conf->max_sockets) + { + socketid = -EMFILE; + } + else + { + /* Allocate socket. */ + + socketid = test_socket_alloc(priv); + if (socketid < 0) + socketid = -ENFILE; + } + + /* Prepare response. */ + + resp.head.msgid = USRSOCK_MESSAGE_RESPONSE_ACK; + resp.head.flags = 0; + resp.xid = req->head.xid; + resp.result = socketid; + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + return OK; +} + +static int close_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_close_s *req = hdrbuf; + struct usrsock_message_req_ack_s resp = {}; + ssize_t wlen; + int ret; + + /* Check if this socket exists. */ + + ret = test_socket_free(priv, req->usockid); + + /* Prepare response. */ + + resp.head.msgid = USRSOCK_MESSAGE_RESPONSE_ACK; + resp.xid = req->head.xid; + if (priv->conf->delay_all_responses) + { + resp.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.result = -EAGAIN; + } + else + { + resp.head.flags = 0; + resp.result = ret; + } + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + if (priv->conf->delay_all_responses) + { + pthread_mutex_unlock(&daemon_mutex); + usleep(50 * 1000); + pthread_mutex_lock(&daemon_mutex); + + /* Previous write was acknowledgment to request, informing that request + * is still in progress. Now write actual completion response. */ + + resp.result = ret; + resp.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + } + + return OK; +} + +static int connect_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_connect_s *req = hdrbuf; + struct sockaddr_in addr; + FAR struct test_socket_s *tsock; + struct usrsock_message_req_ack_s resp = {}; + ssize_t wlen, rlen; + int ret = 0; + + DEBUGASSERT(priv); + DEBUGASSERT(req); + + /* Check if this socket exists. */ + + tsock = test_socket_get(priv, req->usockid); + if (!tsock) + { + ret = -EBADFD; + goto prepare; + } + + /* Check if this socket is already connected. */ + + if (tsock->connected) + { + ret = -EISCONN; + goto prepare; + } + + /* Check if address size ok. */ + + if (req->addrlen > sizeof(addr)) + { + ret = -EFAULT; + goto prepare; + } + + /* Read address. */ + + rlen = read(fd, &addr, sizeof(addr)); + if (rlen < 0 || rlen < req->addrlen) + { + ret = -EFAULT; + goto prepare; + } + + /* Check address family. */ + + if (addr.sin_family != priv->conf->supported_domain) + { + ret = -EAFNOSUPPORT; + goto prepare; + } + + /* Check if there is endpoint with target address */ + + tsock->endp = find_endpoint(priv, addr.sin_addr.s_addr); + if (!tsock->endp) + { + ret = -ENETUNREACH; + goto prepare; + } + + /* Check if there is port open at endpoint */ + + if (!endpoint_connect(priv, tsock->endp, addr.sin_port)) + { + free(tsock->endp); + usrsocktest_endp_malloc_cnt--; + tsock->endp = NULL; + ret = -ECONNREFUSED; + goto prepare; + } + + ret = OK; + +prepare: + /* Prepare response. */ + + resp.xid = req->head.xid; + resp.head.msgid = USRSOCK_MESSAGE_RESPONSE_ACK; + resp.head.flags = 0; + + if (priv->conf->endpoint_block_connect) + { + resp.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.result = ret; + + /* Mark connection as blocked */ + + priv->sockets_waiting_connect++; + tsock->blocked_connect = true; + tsock->pending_resp = resp; + } + else if (priv->conf->delay_all_responses) + { + resp.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.result = -EINPROGRESS; + tsock->blocked_connect = false; + } + else + { + if (ret == OK) + { + priv->sockets_connected++; + tsock->connected = true; + } + + resp.head.flags = 0; + resp.result = ret; + tsock->blocked_connect = false; + } + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + if (priv->conf->endpoint_block_connect) + { + tsock->pending_resp.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + } + else + { + int events; + + if (priv->conf->delay_all_responses) + { + pthread_mutex_unlock(&daemon_mutex); + usleep(50 * 1000); + pthread_mutex_lock(&daemon_mutex); + + /* Previous write was acknowledgment to request, informing that request + * is still in progress. Now write actual completion response. */ + + resp.result = ret; + + if (ret == OK) + { + priv->sockets_connected++; + tsock->connected = true; + } + + resp.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + } + + events = 0; + if (!tsock->block_send) + events |= USRSOCK_EVENT_SENDTO_READY; + if (tsock->recv_avail_bytes > 0) + events |= USRSOCK_EVENT_RECVFROM_AVAIL; + + if (events) + { + wlen = tsock_send_event(fd, priv, tsock, events); + if (wlen < 0) + return wlen; + } + } + + return OK; +} + +static int sendto_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_sendto_s *req = hdrbuf; + FAR struct test_socket_s *tsock; + struct usrsock_message_req_ack_s resp = {}; + ssize_t wlen, rlen; + int ret = 0; + uint8_t sendbuf[16]; + int sendbuflen = 0; + + DEBUGASSERT(priv); + DEBUGASSERT(req); + + /* Check if this socket exists. */ + + tsock = test_socket_get(priv, req->usockid); + if (!tsock) + { + ret = -EBADFD; + goto prepare; + } + + /* Check if this socket is connected. */ + + if (!tsock->connected) + { + ret = -ENOTCONN; + goto prepare; + } + + /* Check if address size non-zero. */ + + if (req->addrlen > 0) + { + ret = -EISCONN; /* connection-mode socket do not accept address */ + goto prepare; + } + + /* Can send? */ + + if (!tsock->block_send) + { + /* Check if request has data. */ + + if (req->buflen > 0) + { + sendbuflen = req->buflen; + if (sendbuflen > sizeof(sendbuf)) + sendbuflen = sizeof(sendbuf); + + /* Read data. */ + + rlen = read(fd, sendbuf, sendbuflen); + if (rlen < 0 || rlen < sendbuflen) + { + ret = -EFAULT; + goto prepare; + } + + /* Debug print */ + + usrsocktest_dbg("got %d bytes of data: '%.*s'\n", + sendbuflen, sendbuflen, sendbuf); + } + } + else + { + ret = -EAGAIN; /* blocked. */ + goto prepare; + } + + ret = sendbuflen; + +prepare: + /* Prepare response. */ + + resp.xid = req->head.xid; + resp.head.msgid = USRSOCK_MESSAGE_RESPONSE_ACK; + resp.head.flags = 0; + + if (priv->conf->delay_all_responses) + { + resp.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.result = -EINPROGRESS; + } + else + { + if (ret > 0) + { + priv->total_send_bytes += ret; + } + + resp.head.flags = 0; + resp.result = ret; + } + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + if (priv->conf->delay_all_responses) + { + pthread_mutex_unlock(&daemon_mutex); + usleep(50 * 1000); + pthread_mutex_lock(&daemon_mutex); + + /* Previous write was acknowledgment to request, informing that request + * is still in progress. Now write actual completion response. */ + + resp.result = ret; + + if (ret > 0) + { + priv->total_send_bytes += ret; + } + + resp.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + } + + if (!tsock->block_send) + { + /* Let kernel-side know that there is space for more send data. */ + + wlen = tsock_send_event(fd, priv, tsock, USRSOCK_EVENT_SENDTO_READY); + if (wlen < 0) + return wlen; + } + + return OK; +} + +static int recvfrom_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_recvfrom_s *req = hdrbuf; + FAR struct test_socket_s *tsock; + struct usrsock_message_datareq_ack_s resp = {}; + ssize_t wlen; + size_t i; + int ret = 0; + size_t outbuflen; + struct sockaddr_in endpointaddr; + + DEBUGASSERT(priv); + DEBUGASSERT(req); + + /* Check if this socket exists. */ + + tsock = test_socket_get(priv, req->usockid); + if (!tsock) + { + ret = -EBADFD; + goto prepare; + } + + /* Check if this socket is connected. */ + + if (!tsock->connected) + { + ret = -ENOTCONN; + goto prepare; + } + + get_endpoint_sockaddr(tsock->endp, &endpointaddr); + + /* Do we have recv data available? */ + + if (tsock->recv_avail_bytes > 0) + { + outbuflen = req->max_buflen; + + if (outbuflen > tsock->recv_avail_bytes) + { + outbuflen = tsock->recv_avail_bytes; + } + } + else + { + ret = -EAGAIN; /* blocked. */ + goto prepare; + } + + ret = outbuflen; + +prepare: + /* Prepare response. */ + + resp.reqack.xid = req->head.xid; + resp.reqack.head.msgid = USRSOCK_MESSAGE_RESPONSE_DATA_ACK; + resp.reqack.head.flags = 0; + + if (priv->conf->delay_all_responses) + { + resp.reqack.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.reqack.result = -EINPROGRESS; + resp.valuelen = 0; + resp.valuelen_nontrunc = 0; + + /* Send ack response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + pthread_mutex_unlock(&daemon_mutex); + usleep(50 * 1000); + pthread_mutex_lock(&daemon_mutex); + + /* Previous write was acknowledgment to request, informing that request + * is still in progress. Now write actual completion response. */ + + resp.reqack.head.msgid = USRSOCK_MESSAGE_RESPONSE_DATA_ACK; + resp.reqack.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + } + + resp.reqack.head.flags = 0; + resp.reqack.result = ret; + if (ret >= 0) + { + priv->total_recv_bytes += ret; + resp.valuelen_nontrunc = sizeof(endpointaddr); + resp.valuelen = resp.valuelen_nontrunc; + if (resp.valuelen > req->max_addrlen) + resp.valuelen = req->max_addrlen; + } + else + { + resp.valuelen = 0; + resp.valuelen_nontrunc = 0; + } + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + if (resp.valuelen > 0) + { + /* Send address (value) */ + + wlen = write(fd, &endpointaddr, resp.valuelen); + if (wlen < 0) + return -errno; + if (wlen != resp.valuelen) + return -ENOSPC; + } + + if (resp.reqack.result > 0) + { + /* Send buffer */ + + for (i = 0; i < resp.reqack.result; i++) + { + char tmp = 'a' + i; + + tsock->recv_avail_bytes--; + + wlen = write(fd, &tmp, 1); + if (wlen < 0) + return -errno; + if (wlen != 1) + return -ENOSPC; + } + + if (tsock->recv_avail_bytes == 0) + priv->sockets_recv_empty++; + } + + if (tsock->recv_avail_bytes > 0) + { + /* Let kernel-side know that there is more recv data. */ + + wlen = tsock_send_event(fd, priv, tsock, USRSOCK_EVENT_RECVFROM_AVAIL); + if (wlen < 0) + return wlen; + } + + return OK; +} + +static int setsockopt_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_setsockopt_s *req = hdrbuf; + FAR struct test_socket_s *tsock; + struct usrsock_message_req_ack_s resp = {}; + ssize_t wlen, rlen; + int ret = 0; + int value; + + DEBUGASSERT(priv); + DEBUGASSERT(req); + + /* Check if this socket exists. */ + + tsock = test_socket_get(priv, req->usockid); + if (!tsock) + { + ret = -EBADFD; + goto prepare; + } + + if (req->level != SOL_SOCKET) + { + usrsocktest_dbg("setsockopt: level=%d not supported\n", req->level); + ret = -ENOPROTOOPT; + goto prepare; + } + + if (req->option != SO_REUSEADDR) + { + usrsocktest_dbg("setsockopt: option=%d not supported\n", req->option); + ret = -ENOPROTOOPT; + goto prepare; + } + + if (req->valuelen < sizeof(value)) + { + ret = -EINVAL; + goto prepare; + } + + /* Read value. */ + + rlen = read(fd, &value, sizeof(value)); + if (rlen < 0 || rlen < sizeof(value)) + { + ret = -EFAULT; + goto prepare; + } + + /* Debug print */ + + usrsocktest_dbg("setsockopt: option=%d value=%d\n", req->option, value); + + ret = OK; + +prepare: + /* Prepare response. */ + + resp.xid = req->head.xid; + resp.head.msgid = USRSOCK_MESSAGE_RESPONSE_ACK; + resp.head.flags = 0; + + if (priv->conf->delay_all_responses) + { + resp.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.result = -EINPROGRESS; + } + else + { + resp.head.flags = 0; + resp.result = ret; + } + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + if (priv->conf->delay_all_responses) + { + pthread_mutex_unlock(&daemon_mutex); + usleep(50 * 1000); + pthread_mutex_lock(&daemon_mutex); + + /* Previous write was acknowledgment to request, informing that request + * is still in progress. Now write actual completion response. */ + + resp.result = ret; + resp.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + } + + return OK; +} + +static int getsockopt_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_getsockopt_s *req = hdrbuf; + FAR struct test_socket_s *tsock; + struct usrsock_message_datareq_ack_s resp = {}; + ssize_t wlen; + int ret = 0; + int value; + + DEBUGASSERT(priv); + DEBUGASSERT(req); + + /* Check if this socket exists. */ + + tsock = test_socket_get(priv, req->usockid); + if (!tsock) + { + ret = -EBADFD; + goto prepare; + } + + if (req->level != SOL_SOCKET) + { + usrsocktest_dbg("getsockopt: level=%d not supported\n", req->level); + ret = -ENOPROTOOPT; + goto prepare; + } + + if (req->option != SO_REUSEADDR) + { + usrsocktest_dbg("getsockopt: option=%d not supported\n", req->option); + ret = -ENOPROTOOPT; + goto prepare; + } + + if (req->max_valuelen < sizeof(value)) + { + ret = -EINVAL; + goto prepare; + } + + value = 0; + ret = OK; + +prepare: + /* Prepare response. */ + + resp.reqack.xid = req->head.xid; + resp.reqack.head.msgid = USRSOCK_MESSAGE_RESPONSE_DATA_ACK; + resp.reqack.head.flags = 0; + + if (priv->conf->delay_all_responses) + { + resp.reqack.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.reqack.result = -EINPROGRESS; + resp.valuelen = 0; + resp.valuelen_nontrunc = 0; + + /* Send ack response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + pthread_mutex_unlock(&daemon_mutex); + usleep(50 * 1000); + pthread_mutex_lock(&daemon_mutex); + + /* Previous write was acknowledgment to request, informing that request + * is still in progress. Now write actual completion response. */ + + resp.reqack.head.msgid = USRSOCK_MESSAGE_RESPONSE_DATA_ACK; + resp.reqack.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + } + + resp.reqack.head.flags = 0; + resp.reqack.result = ret; + if (ret >= 0) + { + resp.valuelen = sizeof(value); + } + else + { + resp.valuelen = 0; + } + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + if (resp.valuelen > 0) + { + /* Send address (value) */ + + wlen = write(fd, &value, resp.valuelen); + if (wlen < 0) + return -errno; + if (wlen != resp.valuelen) + return -ENOSPC; + } + + return OK; +} + +static int getsockname_request(int fd, FAR struct daemon_priv_s *priv, + FAR void *hdrbuf) +{ + FAR struct usrsock_request_getsockname_s *req = hdrbuf; + FAR struct test_socket_s *tsock; + struct usrsock_message_datareq_ack_s resp = {}; + ssize_t wlen; + int ret = 0; + struct sockaddr_in addr; + + DEBUGASSERT(priv); + DEBUGASSERT(req); + + /* Check if this socket exists. */ + + tsock = test_socket_get(priv, req->usockid); + if (!tsock) + { + ret = -EBADFD; + goto prepare; + } + + ret = inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(12345); + ret = ret == 1 ? 0 : -EINVAL; + +prepare: + /* Prepare response. */ + + resp.reqack.xid = req->head.xid; + resp.reqack.head.msgid = USRSOCK_MESSAGE_RESPONSE_DATA_ACK; + resp.reqack.head.flags = 0; + + if (priv->conf->delay_all_responses) + { + resp.reqack.head.flags = USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + resp.reqack.result = -EINPROGRESS; + resp.valuelen = 0; + resp.valuelen_nontrunc = 0; + + /* Send ack response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + pthread_mutex_unlock(&daemon_mutex); + usleep(50 * 1000); + pthread_mutex_lock(&daemon_mutex); + + /* Previous write was acknowledgment to request, informing that request + * is still in progress. Now write actual completion response. */ + + resp.reqack.head.msgid = USRSOCK_MESSAGE_RESPONSE_DATA_ACK; + resp.reqack.head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + } + + resp.reqack.head.flags = 0; + resp.reqack.result = ret; + if (ret >= 0) + { + resp.valuelen = sizeof(addr); + resp.valuelen_nontrunc = sizeof(addr); + if (resp.valuelen > req->max_addrlen) + resp.valuelen = req->max_addrlen; + } + else + { + resp.valuelen = 0; + resp.valuelen_nontrunc = 0; + } + + /* Send response. */ + + wlen = write(fd, &resp, sizeof(resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(resp)) + return -ENOSPC; + + if (resp.valuelen > 0) + { + /* Send address (value) */ + + wlen = write(fd, &addr, resp.valuelen); + if (wlen < 0) + return -errno; + if (wlen != resp.valuelen) + return -ENOSPC; + } + + return OK; +} + +static int handle_usrsock_request(int fd, FAR struct daemon_priv_s *priv) +{ + static const struct + { + unsigned int hdrlen; + int (CODE *fn)(int fd, FAR struct daemon_priv_s *priv, FAR void *req); + } handlers[USRSOCK_REQUEST__MAX] = + { + [USRSOCK_REQUEST_SOCKET] = + { + sizeof(struct usrsock_request_socket_s), + socket_request, + }, + [USRSOCK_REQUEST_CLOSE] = + { + sizeof(struct usrsock_request_close_s), + close_request, + }, + [USRSOCK_REQUEST_CONNECT] = + { + sizeof(struct usrsock_request_connect_s), + connect_request, + }, + [USRSOCK_REQUEST_SENDTO] = + { + sizeof(struct usrsock_request_sendto_s), + sendto_request, + }, + [USRSOCK_REQUEST_RECVFROM] = + { + sizeof(struct usrsock_request_recvfrom_s), + recvfrom_request, + }, + [USRSOCK_REQUEST_SETSOCKOPT] = + { + sizeof(struct usrsock_request_setsockopt_s), + setsockopt_request, + }, + [USRSOCK_REQUEST_GETSOCKOPT] = + { + sizeof(struct usrsock_request_getsockopt_s), + getsockopt_request, + }, + [USRSOCK_REQUEST_GETSOCKNAME] = + { + sizeof(struct usrsock_request_getsockname_s), + getsockname_request, + }, + }; + uint8_t hdrbuf[16]; + FAR struct usrsock_request_common_s *common_hdr = (FAR void *)hdrbuf; + ssize_t rlen; + + rlen = read(fd, common_hdr, sizeof(*common_hdr)); + if (rlen < 0) + return -errno; + if (rlen != sizeof(*common_hdr)) + return -EMSGSIZE; + + if (common_hdr->reqid >= USRSOCK_REQUEST__MAX || + !handlers[common_hdr->reqid].fn) + { + usrsocktest_dbg("Unknown request type: %d\n", common_hdr->reqid); + return -EIO; + } + + assert(handlers[common_hdr->reqid].hdrlen < sizeof(hdrbuf)); + + rlen = read_req(fd, common_hdr, hdrbuf, handlers[common_hdr->reqid].hdrlen); + if (rlen < 0) + return rlen; + + return handlers[common_hdr->reqid].fn(fd, priv, hdrbuf); +} + +static int unblock_sendto(int fd, FAR struct daemon_priv_s *priv, + FAR struct test_socket_s *tsock) +{ + if (tsock->block_send) + { + int ret; + + tsock->block_send = false; + + ret = tsock_send_event(fd, priv, tsock, USRSOCK_EVENT_SENDTO_READY); + if (ret < 0) + return ret; + } + + return OK; +} + +static int reset_recv_avail(int fd, FAR struct daemon_priv_s *priv, + FAR struct test_socket_s *tsock) +{ + if (tsock->recv_avail_bytes == 0) + { + int ret; + + priv->sockets_recv_empty--; + + tsock->recv_avail_bytes = priv->conf->endpoint_recv_avail; + + ret = tsock_send_event(fd, priv, tsock, + USRSOCK_EVENT_RECVFROM_AVAIL); + if (ret < 0) + return ret; + } + + return OK; +} + +static int disconnect_connection(int fd, FAR struct daemon_priv_s *priv, + FAR struct test_socket_s *tsock) +{ + if (tsock->connected) + { + int ret; + + tsock->disconnected = true; + tsock->connected = false; + + priv->sockets_connected--; + priv->sockets_remote_disconnected++; + + ret = tsock_send_event(fd, priv, tsock, USRSOCK_EVENT_REMOTE_CLOSED); + if (ret < 0) + return ret; + } + + return OK; +} + +static int establish_blocked_connection(int fd, FAR struct daemon_priv_s *priv, + FAR struct test_socket_s *tsock) +{ + if (tsock->blocked_connect) + { + FAR struct usrsock_message_req_ack_s *resp = &tsock->pending_resp; + ssize_t wlen; + int events; + + if (resp->result == OK) + { + priv->sockets_connected++; + tsock->connected = true; + } + + tsock->blocked_connect = false; + + priv->sockets_waiting_connect--; + resp->head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + + wlen = write(fd, resp, sizeof(*resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(*resp)) + return -ENOSPC; + + events = 0; + if (!tsock->block_send) + events |= USRSOCK_EVENT_SENDTO_READY; + if (tsock->recv_avail_bytes > 0) + events |= USRSOCK_EVENT_RECVFROM_AVAIL; + + if (events) + { + wlen = tsock_send_event(fd, priv, tsock, events); + if (wlen < 0) + return wlen; + } + } + + return OK; +} + +static int fail_blocked_connection(int fd, FAR struct daemon_priv_s *priv, + FAR struct test_socket_s *tsock) +{ + if (tsock->blocked_connect) + { + FAR struct usrsock_message_req_ack_s *resp = &tsock->pending_resp; + ssize_t wlen; + + resp->result = -ECONNREFUSED; + priv->sockets_not_connected_refused++; + tsock->connect_refused = true; + tsock->blocked_connect = false; + + priv->sockets_waiting_connect--; + resp->head.flags &= ~USRSOCK_MESSAGE_FLAG_REQ_IN_PROGRESS; + + wlen = write(fd, resp, sizeof(*resp)); + if (wlen < 0) + return -errno; + if (wlen != sizeof(*resp)) + return -ENOSPC; + } + + return OK; +} + +static int for_each_connection(int fd, FAR struct daemon_priv_s *priv, + int (CODE *iter_fn)( + int fd, + FAR struct daemon_priv_s *priv, + FAR struct test_socket_s *tsock)) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(priv->test_sockets); i++) + { + FAR struct test_socket_s *tsock = &priv->test_sockets[i]; + + if (tsock->opened) + { + int ret = iter_fn(fd, priv, tsock); + if (ret < 0) + return ret; + } + } + + return OK; +} + +static FAR void *usrsocktest_daemon(FAR void *param) +{ + FAR struct daemon_priv_s *priv = param; + bool stopped; + int ret; + int fd; + + usrsocktest_dbg("\n"); + + priv->sockets_active = 0; + + fd = open("/dev/usrsock", O_RDWR); + if (fd < 0) + { + ret = -errno; + goto errout; + } + + do + { + struct pollfd pfd[2] = {}; + int npfds = 0; + int usrsock_pfdpos = -1; + int pipe_pdfpos = -1; + + stopped = false; + + /* Wait for request from kernel side. */ + + pthread_mutex_lock(&daemon_mutex); + if (!priv->do_not_poll_usrsock && fd >= 0) + { + pfd[npfds].fd = fd; + pfd[npfds].events = POLLIN; + usrsock_pfdpos = npfds++; + } + pfd[npfds].fd = priv->pipefd[0]; + pfd[npfds].events = POLLIN; + pipe_pdfpos = npfds++; + pthread_mutex_unlock(&daemon_mutex); + + ret = poll(pfd, npfds, -1); + if (ret < 0) + { + /* Error? */ + ret = -errno; + goto errout; + } + + if (usrsock_pfdpos >= 0 && (pfd[usrsock_pfdpos].revents & POLLIN)) + { + pthread_mutex_lock(&daemon_mutex); + ret = handle_usrsock_request(fd, priv); + pthread_mutex_unlock(&daemon_mutex); + if (ret < 0) + goto errout; + } + + if (pipe_pdfpos >= 0 && (pfd[pipe_pdfpos].revents & POLLIN)) + { + char in; + + if (read(pfd[pipe_pdfpos].fd, &in, 1) == 1) + { + pthread_mutex_lock(&daemon_mutex); + + switch (in) + { + case 'S': + stopped = true; + ret = 0; + break; + case 's': + stopped = false; + ret = 0; + break; + case 'E': + ret = for_each_connection(fd, priv, + &establish_blocked_connection); + break; + case 'F': + ret = for_each_connection(fd, priv, + &fail_blocked_connection); + break; + case 'D': + ret = for_each_connection(fd, priv, + &disconnect_connection); + break; + case 'W': + ret = for_each_connection(fd, priv, + &unblock_sendto); + break; + case 'r': + ret = for_each_connection(fd, priv, + &reset_recv_avail); + break; + case 'K': + /* Kill usrsockdev */ + if (fd >= 0) + { + close(fd); + fd = -1; + } + break; + case '*': + sem_post(&priv->wakewaitsem); + break; /* woke thread. */ + } + + pthread_mutex_unlock(&daemon_mutex); + + if (ret < 0) + goto errout; + } + } + + usleep(1); + } + while (!stopped); + + ret = OK; +errout: + if (fd >= 0) + { + close(fd); + } + + usrsocktest_dbg("ret: %d\n", ret); + + return (FAR void *)(intptr_t)ret; +} + +static int get_daemon_value(FAR struct daemon_priv_s *priv, + FAR void *dst, FAR const void *src, size_t len) +{ + int ret = 0; + + if ((uintptr_t)src < (uintptr_t)priv || + (uintptr_t)src >= (uintptr_t)priv + sizeof(*priv) || len <= 0) + { + /* Not daemon value */ + + return -EINVAL; + } + + pthread_mutex_lock(&daemon_mutex); + + if (priv->conf == NULL) + { + /* Not running? */ + + ret = -ENODEV; + goto out; + } + + memmove(dst, src, len); + +out: + pthread_mutex_unlock(&daemon_mutex); + + return ret; +} + +static FAR void *delayed_cmd_thread(FAR void *priv) +{ + FAR struct delayed_cmd_s *cmd = priv; + + if (cmd->delay_msec) + sem_post(&cmd->startsem); + + usleep(cmd->delay_msec * 1000); + + (void)write(cmd->pipefd, &cmd->cmd, 1); + + if (!cmd->delay_msec) + sem_post(&cmd->startsem); + + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int usrsocktest_daemon_start(FAR const struct usrsocktest_daemon_conf_s *conf) +{ + FAR struct daemon_priv_s *priv = &daemon; + pthread_attr_t attr; + int ret; + + usrsocktest_dbg("\n"); + + pthread_mutex_lock(&daemon_mutex); + + if (priv->conf != NULL || !priv->joined) + { + /* Already running? */ + + ret = -EALREADY; + goto out; + } + + /* Clear daemon private data. */ + + memset(priv, 0, sizeof(*priv)); + + /* Allocate pipe for daemon commands. */ + + ret = pipe(priv->pipefd); + if (ret != OK) + { + ret = -errno; + goto out; + } + + ret = pthread_attr_init(&attr); + if (ret != OK) + { + ret = -ret; + goto errout_closepipe; + } + + sem_init(&priv->wakewaitsem, 0, 0); + + priv->joined = false; + priv->conf = conf; + + ret = pthread_create(&priv->tid, &attr, usrsocktest_daemon, priv); + if (ret != OK) + { + sem_destroy(&priv->wakewaitsem); + priv->joined = true; + priv->conf = NULL; + ret = -ret; + goto errout_closepipe; + } + +errout_closepipe: + if (ret != OK) + { + close(priv->pipefd[0]); + close(priv->pipefd[1]); + } +out: + pthread_mutex_unlock(&daemon_mutex); + usrsocktest_dbg("ret: %d\n", ret); + return ret; +} + +int usrsocktest_daemon_stop(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + FAR struct delayed_cmd_s *item, *next; + FAR pthread_addr_t retval; + char stopped; + int ret; + int i; + + usrsocktest_dbg("\n"); + + pthread_mutex_lock(&daemon_mutex); + + if (priv->conf == NULL) + { + /* Not running? */ + + ret = -ENODEV; + goto out; + } + + item = (void *)sq_peek(&priv->delayed_cmd_threads); + while (item) + { + next = (void *)sq_next(&item->node); + + pthread_mutex_unlock(&daemon_mutex); + (void)pthread_join(item->tid, &retval); + pthread_mutex_lock(&daemon_mutex); + sq_rem(&item->node, &priv->delayed_cmd_threads); + free(item); + usrsocktest_dcmd_malloc_cnt--; + + item = next; + } + + pthread_mutex_unlock(&daemon_mutex); + stopped = 'S'; + write(priv->pipefd[1], &stopped, 1); + + ret = pthread_join(priv->tid, &retval); + pthread_mutex_lock(&daemon_mutex); + if (ret != OK) + { + ret = -ret; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(priv->test_sockets); i++) + { + if (priv->test_sockets[i].opened && priv->test_sockets[i].endp != NULL) + { + free(priv->test_sockets[i].endp); + priv->test_sockets[i].endp = NULL; + usrsocktest_endp_malloc_cnt--; + } + } + + priv->conf = NULL; + close(priv->pipefd[0]); + close(priv->pipefd[1]); + sem_destroy(&priv->wakewaitsem); + + priv->joined = true; + ret = (intptr_t)retval; + +out: + pthread_mutex_unlock(&daemon_mutex); + usrsocktest_dbg("ret: %d\n", ret); + return ret; +} + +int usrsocktest_daemon_get_num_active_sockets(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + int ret, err; + + err = get_daemon_value(priv, &ret, &priv->sockets_active, sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +int usrsocktest_daemon_get_num_connected_sockets(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + int ret, err; + + err = get_daemon_value(priv, &ret, &priv->sockets_connected, sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +int usrsocktest_daemon_get_num_waiting_connect_sockets(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + int ret, err; + + err = get_daemon_value(priv, &ret, &priv->sockets_waiting_connect, sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +int usrsocktest_daemon_get_num_recv_empty_sockets(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + int ret, err; + + err = get_daemon_value(priv, &ret, &priv->sockets_recv_empty, sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +ssize_t usrsocktest_daemon_get_send_bytes(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + size_t ret; + int err; + + err = get_daemon_value(priv, &ret, &priv->total_send_bytes, sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +ssize_t usrsocktest_daemon_get_recv_bytes(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + size_t ret; + int err; + + err = get_daemon_value(priv, &ret, &priv->total_recv_bytes, sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +int usrsocktest_daemon_get_num_unreachable_sockets(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + int ret, err; + + err = get_daemon_value(priv, &ret, &priv->sockets_not_connected_refused, + sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +int usrsocktest_daemon_get_num_remote_disconnected_sockets(void) +{ + FAR struct daemon_priv_s *priv = &daemon; + int ret, err; + + err = get_daemon_value(priv, &ret, &priv->sockets_remote_disconnected, + sizeof(ret)); + if (err < 0) + return err; + + return ret; +} + +int usrsocktest_daemon_pause_usrsock_handling(bool pause) +{ + FAR struct daemon_priv_s *priv = &daemon; + int ret; + char cmd = '*'; + + pthread_mutex_lock(&daemon_mutex); + + if (priv->conf == NULL) + { + /* Not running? */ + + pthread_mutex_unlock(&daemon_mutex); + return -ENODEV; + } + + priv->do_not_poll_usrsock = pause; + + (void)write(priv->pipefd[1], &cmd, 1); + ret = OK; + + pthread_mutex_unlock(&daemon_mutex); + + sem_wait(&priv->wakewaitsem); + + return ret; +} + +bool usrsocktest_send_delayed_command(const char cmd, unsigned int delay_msec) +{ + FAR struct daemon_priv_s *priv = &daemon; + pthread_attr_t attr; + FAR struct delayed_cmd_s *delayed_cmd; + int ret; + + if (priv->conf == NULL) + { + /* Not running? */ + + return false; + } + + delayed_cmd = calloc(1, sizeof(*delayed_cmd)); + if (!delayed_cmd) + { + return false; + } + + usrsocktest_dcmd_malloc_cnt++; + + delayed_cmd->delay_msec = delay_msec; + delayed_cmd->cmd = cmd; + delayed_cmd->pipefd = priv->pipefd[1]; + (void)sem_init(&delayed_cmd->startsem, 0, 0); + + ret = pthread_attr_init(&attr); + if (ret != OK) + { + free(delayed_cmd); + usrsocktest_dcmd_malloc_cnt--; + return false; + } + + ret = pthread_create(&delayed_cmd->tid, &attr, delayed_cmd_thread, + delayed_cmd); + if (ret != OK) + { + free(delayed_cmd); + usrsocktest_dcmd_malloc_cnt--; + return false; + } + + while (sem_wait(&delayed_cmd->startsem) != OK); + + sq_addlast(&delayed_cmd->node, &priv->delayed_cmd_threads); + + return true; +} + +bool usrsocktest_daemon_establish_waiting_connections(void) +{ + return usrsocktest_send_delayed_command('E', 0); +} diff --git a/examples/usrsocktest/usrsocktest_main.c b/examples/usrsocktest/usrsocktest_main.c new file mode 100644 index 000000000..a59c4c5e5 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_main.c @@ -0,0 +1,284 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_main.c + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Author: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +#ifndef dbg + #define dbg _warn +#endif + +#define usrsocktest_dbg(...) ((void)0) + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct +{ + unsigned int ok; + unsigned int failed; + + unsigned int nchecks; +} overall; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +int usrsocktest_endp_malloc_cnt = 0; +int usrsocktest_dcmd_malloc_cnt = 0; +bool usrsocktest_test_failed = false; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void get_mallinfo(struct mallinfo *mem) +{ +#ifdef CONFIG_CAN_PASS_STRUCTS + *mem = mallinfo(); +#else + (void)mallinfo(mem); +#endif +} + +static void print_mallinfo(const struct mallinfo *mem, const char *title) +{ + if (title) + printf("%s:\n", title); + printf(" %11s%11s%11s%11s\n", "total", "used", "free", "largest"); + printf("Mem: %11d%11d%11d%11d\n", + mem->arena, mem->uordblks, mem->fordblks, mem->mxordblk); +} + +static void utest_assert_print_head(FAR const char *func, const int line, + FAR const char *check_str) +{ + printf("\t[TEST ASSERT FAILED!]\n" + "\t\tIn function \"%s\":\n" + "\t\tline %d: Assertion `%s' failed.\n", func, line, check_str); +} + +static void run_tests(FAR const char *name, void (CODE *test_fn)(void)) +{ + printf("Testing group \"%s\" =>\n", name); + fflush(stdout); + fflush(stderr); + + usrsocktest_test_failed = false; + test_fn(); + if (!usrsocktest_test_failed) + { + printf("\tGroup \"%s\": [OK]\n", name); + overall.ok++; + } + else + { + printf("\tGroup \"%s\": [FAILED]\n", name); + overall.failed++; + } + + fflush(stdout); + fflush(stderr); +} + +/**************************************************************************** + * Name: runAllTests + * + * Description: + * Sequentially runs all included test groups + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void runAllTests(void) +{ + RUN_TEST_GROUP(CharDev); + RUN_TEST_GROUP(NoDaemon); + RUN_TEST_GROUP(BasicDaemon); + RUN_TEST_GROUP(BasicConnect); + RUN_TEST_GROUP(BasicConnectDelay); + RUN_TEST_GROUP(NoBlockConnect); + RUN_TEST_GROUP(BasicSend); + RUN_TEST_GROUP(NoBlockSend); + RUN_TEST_GROUP(BlockSend); + RUN_TEST_GROUP(NoBlockRecv); + RUN_TEST_GROUP(BlockRecv); + RUN_TEST_GROUP(RemoteDisconnect); + RUN_TEST_GROUP(BasicSetSockOpt); + RUN_TEST_GROUP(BasicGetSockOpt); + RUN_TEST_GROUP(BasicGetSockName); + RUN_TEST_GROUP(WakeWithSignal); + RUN_TEST_GROUP(MultiThread); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +bool usrsocktest_assert_print_value(FAR const char *func, + const int line, + FAR const char *check_str, + long int test_value, + long int should_be) +{ + ++overall.nchecks; + + if (test_value == should_be) + { + int keep_errno = errno; + + usrsocktest_dbg("%d => OK.\n", line); + + errno = keep_errno; + return true; + } + + utest_assert_print_head(func, line, check_str); + + printf("\t\t\tgot value: %ld\n", test_value); + printf("\t\t\tshould be: %ld\n", should_be); + + fflush(stdout); + fflush(stderr); + + return false; +} + +bool usrsocktest_assert_print_buf(FAR const char *func, + const int line, + FAR const char *check_str, + FAR const void *test_buf, + FAR const void *expect_buf, + size_t buflen) +{ + ++overall.nchecks; + + if (memcmp(test_buf, expect_buf, buflen) == 0) + { + int keep_errno = errno; + + usrsocktest_dbg("%d => OK.\n", line); + + errno = keep_errno; + return true; + } + + utest_assert_print_head(func, line, check_str); + + fflush(stdout); + fflush(stderr); + + return false; +} + +/**************************************************************************** + * usrsocktest_main + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int usrsocktest_main(int argc, char *argv[]) +#endif +{ + struct mallinfo mem_before, mem_after; + + memset(&overall, 0, sizeof(overall)); + + printf("Starting unit-tests...\n"); + fflush(stdout); + fflush(stderr); + + get_mallinfo(&mem_before); + + runAllTests(); + + printf("Unit-test groups done... OK:%d, FAILED:%d, TOTAL:%d\n", + overall.ok, overall.failed, overall.ok + overall.failed); + printf(" -- number of checks made: %d\n", overall.nchecks); + fflush(stdout); + fflush(stderr); + + get_mallinfo(&mem_after); + + print_mallinfo(&mem_before, "HEAP BEFORE TESTS"); + print_mallinfo(&mem_after, "HEAP AFTER TESTS"); + + fflush(stdout); + fflush(stderr); + exit(0); + + return 0; +} + diff --git a/examples/usrsocktest/usrsocktest_multi_thread.c b/examples/usrsocktest/usrsocktest_multi_thread.c new file mode 100644 index 000000000..6563b0dd0 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_multi_thread.c @@ -0,0 +1,267 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_multi_thread.c + * Multi-threaded access to sockets + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static pthread_t tids[4]; +static int sds[4]; +static bool started; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void usrsock_socket_multitask_do_work(int *sd) +{ + struct sockaddr_in addr; + int ret; + int i; + + for (i = 0; i < 10; i++) + { + /* Simple test for opening socket with usrsock daemon running. */ + + *sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(*sd >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(*sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(*sd) >= 0); + *sd = -1; + } +} + +static FAR void *usrsock_socket_multitask_thread(FAR void *param) +{ + usrsock_socket_multitask_do_work((int*)param); + return NULL; +} + +/**************************************************************************** + * Name: MultiThread test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(MultiThread) +{ + int i; + for (i = 0; i < ARRAY_SIZE(sds); i++) + { + sds[i] = -1; + } + for (i = 0; i < ARRAY_SIZE(tids); i++) + { + tids[i] = -1; + } + started = false; +} + +/**************************************************************************** + * Name: MultiThread test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(MultiThread) +{ + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(tids); i++) + { + if (tids[i] != -1) + { + ret = pthread_cancel(tids[i]); + assert(ret == OK); + ret = pthread_join(tids[i], NULL); + assert(ret == OK); + } + } + for (i = 0; i < ARRAY_SIZE(sds); i++) + { + if (sds[i] != -1) + { + ret = close(sds[i]); + assert(ret >= 0); + } + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +/**************************************************************************** + * Name: OpenClose + * + * Description: + * Open and close socket with multiple threads + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(MultiThread, OpenClose) +{ + int ret; + int i; + + /* Start test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + usrsocktest_daemon_config.endpoint_block_send = false; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Launch worker threads. */ + + for (i = 0; i < ARRAY_SIZE(tids); i++) + { + ret = pthread_create(&tids[i], NULL, usrsock_socket_multitask_thread, + sds + i); + TEST_ASSERT_EQUAL(OK, ret); + } + + /* Wait threads to complete work. */ + + while (--i > -1) + { + pthread_addr_t tparam; + + ret = pthread_join(tids[i], &tparam); + TEST_ASSERT_EQUAL(OK, ret); + tids[i] = -1; + + /* This flag is set whenever a test fails, otherwise it is not touched + * No need for synchronization. Here we bail from main test thread on + * first failure in any thread. + */ + + TEST_ASSERT_FALSE(usrsocktest_test_failed); + } + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(MultiThread) +{ + RUN_TEST_CASE(MultiThread, OpenClose); +} + diff --git a/examples/usrsocktest/usrsocktest_noblock_connect.c b/examples/usrsocktest/usrsocktest_noblock_connect.c new file mode 100644 index 000000000..3c0a26eaa --- /dev/null +++ b/examples/usrsocktest/usrsocktest_noblock_connect.c @@ -0,0 +1,816 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_noblock_connect.c + * Socket connect tests in non-blocking mode + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd, sd2; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +TEST_SETUP(NoBlockConnect) +{ + sd = -1; + sd2 = -1; + started = false; +} + +TEST_TEAR_DOWN(NoBlockConnect) +{ + int ret; + + if (sd >= 0) + { + ret = close(sd); + assert(ret == 0); + } + if (sd2 >= 0) + { + ret = close(sd2); + assert(ret == 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(NoBlockConnect, InstantConnect) +{ + int flags; + int ret; + struct sockaddr_in addr; + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Do connect, should succeed instantly. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +TEST(NoBlockConnect, DelayedConnect) +{ + int flags, ret, count; + struct sockaddr_in addr; + + /* Start test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + usrsocktest_daemon_config.delay_all_responses = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempt, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Another connect attempt results EALREADY. */ + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EALREADY, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Release delayed connect. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Another connect attempt results EISCONN. */ + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + + /* Close socket. */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +TEST(NoBlockConnect, CloseNotConnected) +{ + int flags, ret; + struct sockaddr_in addr; + + /* Start test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + usrsocktest_daemon_config.delay_all_responses = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempt, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Close socket. */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +TEST(NoBlockConnect, EarlyDrop) +{ + int flags, ret; + struct sockaddr_in addr; + + /* Start test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + usrsocktest_daemon_config.delay_all_responses = false; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempt, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + + /* Close socket. */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_waiting_connect_sockets()); +} + +TEST(NoBlockConnect, Multiple) +{ + int flags, ret, count; + struct sockaddr_in addr; + + /* Start test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + usrsocktest_daemon_config.delay_all_responses = false; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open sockets */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + sd2 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + flags = fcntl(sd2, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd2, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd2, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempts, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd2, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Release delayed connections. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd2, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Release delayed connections. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; + count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Close sockets. */ + + TEST_ASSERT_TRUE(close(sd2) >= 0); + sd2 = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Open sockets */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + sd2 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + flags = fcntl(sd2, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd2, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd2, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempts, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + ret = connect(sd2, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Release delayed connections. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Close sockets. */ + + TEST_ASSERT_TRUE(close(sd2) >= 0); + sd2 = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Open sockets */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + sd2 = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + flags = fcntl(sd2, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd2, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd2, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempt, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Release delayed connections. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Launch another connect attempt, daemon delays actual connection until triggered. */ + + ret = connect(sd2, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(2, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Close sockets. */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_TRUE(close(sd2) >= 0); + sd2 = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +TEST(NoBlockConnect, Dup2) +{ + int flags, ret, count; + struct sockaddr_in addr; + + /* Start test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + usrsocktest_daemon_config.delay_all_responses = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Duplicate socket */ + + sd2 = dup(sd); + TEST_ASSERT_TRUE(sd2 >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempt, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Another connect attempt results EALREADY. */ + + ret = connect(sd2, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EALREADY, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Release delayed connect. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Another connect attempt results EISCONN. */ + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + + /* Close sockets. */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + TEST_ASSERT_TRUE(close(sd2) >= 0); + sd2 = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(NoBlockConnect) +{ + RUN_TEST_CASE(NoBlockConnect, InstantConnect); + RUN_TEST_CASE(NoBlockConnect, DelayedConnect); + RUN_TEST_CASE(NoBlockConnect, CloseNotConnected); + RUN_TEST_CASE(NoBlockConnect, EarlyDrop); + RUN_TEST_CASE(NoBlockConnect, Multiple); + RUN_TEST_CASE(NoBlockConnect, Dup2); +} diff --git a/examples/usrsocktest/usrsocktest_noblock_recv.c b/examples/usrsocktest/usrsocktest_noblock_recv.c new file mode 100644 index 000000000..2e511796e --- /dev/null +++ b/examples/usrsocktest/usrsocktest_noblock_recv.c @@ -0,0 +1,521 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_noblock_recv.c + * Receive from the socket in non-blocking mode + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: Receive + * + * Description: + * Non-blocking & instant connect+recv + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Receive(struct usrsocktest_daemon_conf_s *dconf) +{ + int flags; + int count; + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[4]; + struct sockaddr_in remoteaddr; + socklen_t addrlen; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_recv_avail_from_start = true; + dconf->endpoint_recv_avail = 7; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Do connect, should succeed instantly. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + if (!dconf->delay_all_responses) + { + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, + usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + } + else + { + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, + usrsocktest_daemon_get_num_waiting_connect_sockets()); + + for (count = 0; usrsocktest_daemon_get_num_connected_sockets() != 1; + count++) + { + TEST_ASSERT_TRUE(count <= 3); + usleep(25 * 1000); + } + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + } + + /* Receive data from remote, daemon returns 4 bytes. */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, NULL); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(4, datalen); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abcd", data, 4); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(4, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote with address, daemon returns 3 bytes. */ + + addrlen = sizeof(remoteaddr); + ret = recvfrom(sd, data, datalen, 0, (FAR struct sockaddr *)&remoteaddr, + &addrlen); + TEST_ASSERT_EQUAL(3, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abc", data, 3); + TEST_ASSERT_EQUAL(addrlen, sizeof(remoteaddr)); + TEST_ASSERT_EQUAL_UINT8_ARRAY(&remoteaddr, &addr, addrlen); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(datalen + ret, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote, daemon has 0 bytes buffered => -EAGAIN */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, NULL); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(7, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote, daemon has 0 bytes buffered => -EAGAIN */ + + data = databuf; + datalen = sizeof(databuf); + ret = read(sd, data, datalen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(7, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Reset recv buffer for open sockets */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('r', 0)); + for (count = 0; usrsocktest_daemon_get_num_recv_empty_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(5 * 1000); + } + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Receive data from remote, daemon returns 4 bytes. */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, NULL); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(4, datalen); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abcd", data, 4); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(7 + 4, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(7 + 4, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: DelayedConnect + * + * Description: + * Non-blocking & delayed connect + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void DelayedConnect(struct usrsocktest_daemon_conf_s *dconf) +{ + int flags; + int count; + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[4]; + struct sockaddr_in remoteaddr; + socklen_t addrlen; + + /* Start test daemon. */ + + dconf->endpoint_block_connect = true; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 4; + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempt, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Try receive data, not connected yet. */ + + data = databuf; + datalen = sizeof(databuf); + ret = read(sd, data, datalen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + + /* Release delayed connect. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; + count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Try receive data, not received data yet. */ + + data = databuf; + datalen = sizeof(databuf); + addrlen = sizeof(remoteaddr); + ret = recvfrom(sd, data, datalen, 0, (FAR struct sockaddr *)&remoteaddr, + &addrlen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(0, addrlen); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Reset recv buffer for open sockets */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('r', 0)); + for (count = 0; usrsocktest_daemon_get_num_recv_empty_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(5 * 1000); + } + + /* Receive data from remote, daemon returns 4 bytes. */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, NULL); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(4, datalen); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abcd", data, 4); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(4, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Close socket. */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, + usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_recv_empty_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: NoBlockRecv test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(NoBlockRecv) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: NoBlockRecv test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(NoBlockRecv) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(NoBlockRecv, Receive) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Receive(&usrsocktest_daemon_config); +} + +TEST(NoBlockRecv, ReceiveDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Receive(&usrsocktest_daemon_config); +} + +TEST(NoBlockRecv, DelayedConnect) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + DelayedConnect(&usrsocktest_daemon_config); +} + +TEST(NoBlockRecv, DelayedConnectDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + DelayedConnect(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(NoBlockRecv) +{ + RUN_TEST_CASE(NoBlockRecv, Receive); + RUN_TEST_CASE(NoBlockRecv, ReceiveDelay); + RUN_TEST_CASE(NoBlockRecv, DelayedConnect); + RUN_TEST_CASE(NoBlockRecv, DelayedConnectDelay); +} diff --git a/examples/usrsocktest/usrsocktest_noblock_send.c b/examples/usrsocktest/usrsocktest_noblock_send.c new file mode 100644 index 000000000..c6d47a7ca --- /dev/null +++ b/examples/usrsocktest/usrsocktest_noblock_send.c @@ -0,0 +1,414 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_noblock_send.c + * Send through the socket in non-blocking mode + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: Send + * + * Description: + * Open socket, connect instantly and send + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Send(struct usrsocktest_daemon_conf_s *dconf) +{ + int flags; + int count; + ssize_t ret; + size_t datalen; + const void *data; + struct sockaddr_in addr; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Do connect, should succeed instantly. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + if (!dconf->delay_all_responses) + { + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + } + else + { + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + for (count = 0; usrsocktest_daemon_get_num_connected_sockets() != 1; + count++) + { + TEST_ASSERT_TRUE(count <= 3); + usleep(25 * 1000); + } + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + } + + /* Send data to remote */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = sendto(sd, data, datalen, 0, NULL, 0); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(datalen, usrsocktest_daemon_get_send_bytes()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); +} + +/**************************************************************************** + * Name: ConnectSend + * + * Description: + * Open socket, connect is delayed and send + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void ConnectSend(struct usrsocktest_daemon_conf_s *dconf) +{ + int flags; + int count; + ssize_t ret; + size_t datalen; + const void *data; + struct sockaddr_in addr; + + /* Start test daemon. */ + + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = true; + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Launch connect attempt, daemon delays actual connection until triggered. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Send data to remote, not connected yet. */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = write(sd, data, datalen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + + /* Release delayed connect. */ + + TEST_ASSERT_TRUE(usrsocktest_daemon_establish_waiting_connections()); + for (count = 0; usrsocktest_daemon_get_num_waiting_connect_sockets() > 0; + count++) + { + TEST_ASSERT_TRUE(count <= 5); + usleep(10 * 1000); + } + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Send data to remote */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = write(sd, data, datalen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EAGAIN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + + /* Close socket. */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, + usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: NoBlockSend test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(NoBlockSend) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: NoBlockSend test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(NoBlockSend) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(NoBlockSend, Send) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Send(&usrsocktest_daemon_config); +} + +TEST(NoBlockSend, SendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Send(&usrsocktest_daemon_config); +} + +TEST(NoBlockSend, ConnectSend) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + ConnectSend(&usrsocktest_daemon_config); +} + +TEST(NoBlockSend, ConnectSendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + ConnectSend(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(NoBlockSend) +{ + RUN_TEST_CASE(NoBlockSend, Send); + RUN_TEST_CASE(NoBlockSend, SendDelay); + RUN_TEST_CASE(NoBlockSend, ConnectSend); + RUN_TEST_CASE(NoBlockSend, ConnectSendDelay); +} diff --git a/examples/usrsocktest/usrsocktest_nodaemon.c b/examples/usrsocktest/usrsocktest_nodaemon.c new file mode 100644 index 000000000..ceccb91a1 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_nodaemon.c @@ -0,0 +1,154 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_nodaemon.c + * Tests without user socket daemon + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: NoDaemon test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(NoDaemon) +{ + sd = -1; +} + +/**************************************************************************** + * Name: NoDaemon test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(NoDaemon) +{ + int ret; + + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } +} + +/**************************************************************************** + * Name: NoSocket + * + * Description: + * Simple test for opening socket without usrsock daemon running + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(NoDaemon, NoSocket) +{ + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_EQUAL(-1, sd); + TEST_ASSERT_TRUE(errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT || errno == ENETDOWN); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(NoDaemon) +{ + RUN_TEST_CASE(NoDaemon, NoSocket); +} diff --git a/examples/usrsocktest/usrsocktest_poll.c b/examples/usrsocktest/usrsocktest_poll.c new file mode 100644 index 000000000..bfd6454c6 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_poll.c @@ -0,0 +1,646 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_poll.c + * User socket polling tests + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ConnectReceive + * + * Description: + * Non-blocking connect and receive + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void ConnectReceive(struct usrsocktest_daemon_conf_s *dconf) +{ + int flags; + int count; + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[4]; + struct pollfd pfd; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 3; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Poll for input (instant timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 0); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(POLLIN, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + + /* Do connect, should succeed instantly. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + if (!dconf->delay_all_responses) + { + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + } + else + { + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + for (count = 0; usrsocktest_daemon_get_num_connected_sockets() != 1; count++) + { + TEST_ASSERT_TRUE(count <= 3); + usleep(25 * 1000); + } + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + } + + /* Poll for input (instant timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 0); + TEST_ASSERT_EQUAL(0, ret); + + /* Poll for input (with timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 10); + TEST_ASSERT_EQUAL(0, ret); + + /* Poll for input (no timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('r', 100)); + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLIN, pfd.revents); + + /* Receive data from remote, daemon returns 3 bytes. */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, NULL); + TEST_ASSERT_EQUAL(3, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abc", data, 3); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(3, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Poll for input (instant timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 0); + TEST_ASSERT_EQUAL(0, ret); + + /* Make more data avail */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('r', 0)); + for (count = 0; usrsocktest_daemon_get_num_recv_empty_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 3); + usleep(5 * 1000); + } + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Poll for input (no timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLIN, pfd.revents); + + /* Receive data from remote, daemon returns 3 bytes. */ + + data = databuf; + datalen = sizeof(databuf); + ret = recvfrom(sd, data, datalen, 0, NULL, NULL); + TEST_ASSERT_EQUAL(3, ret); + TEST_ASSERT_EQUAL_UINT8_ARRAY("abc", data, 3); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(6, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(6, usrsocktest_daemon_get_recv_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: ConnectSend + * + * Description: + * Non-blocking connect and receive + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void ConnectSend(struct usrsocktest_daemon_conf_s *dconf) +{ + int flags; + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + struct pollfd pfd; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_recv_avail_from_start = true; + dconf->endpoint_recv_avail = 3; + dconf->endpoint_block_send = false; + dconf->endpoint_block_connect = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Poll for input (instant timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 0); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(POLLIN, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + + /* Start non-blocking connect. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Poll for input (no timeout). As send is ready after established connection, + * poll will exit with POLLOUT. */ + + memset(&pfd, 0, sizeof(pfd)); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('E', 100)); + pfd.fd = sd; + pfd.events = POLLOUT; + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLOUT, pfd.revents); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Send data to remote. */ + + data = "abcdeFG"; + datalen = strlen(data); + ret = write(sd, data, datalen); + TEST_ASSERT_EQUAL(datalen, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(datalen, usrsocktest_daemon_get_send_bytes()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(datalen, usrsocktest_daemon_get_send_bytes()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: DaemonAbort + * + * Description: + * Poll with daemon abort + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void DaemonAbort(struct usrsocktest_daemon_conf_s *dconf) +{ + int flags; + ssize_t ret; + struct sockaddr_in addr; + struct pollfd pfd; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 3; + dconf->endpoint_block_send = false; + dconf->endpoint_block_connect = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Poll for input (instant timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 0); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(POLLIN, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + + /* Start non-blocking connect. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Poll for input (no timeout). Stop daemon forcefully. */ + + memset(&pfd, 0, sizeof(pfd)); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('S', 100)); + pfd.fd = sd; + pfd.events = POLLOUT; + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLERR, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + + /* Poll for input (no timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLOUT; + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLERR, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_send_bytes()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_recv_empty_sockets()); +} + +/**************************************************************************** + * Name: Poll test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(Poll) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: Poll test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(Poll) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(Poll, ConnectReceive) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + ConnectReceive(&usrsocktest_daemon_config); +} + +TEST(Poll, ConnectReceiveDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + ConnectReceive(&usrsocktest_daemon_config); +} + +TEST(Poll, ConnectSend) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + ConnectSend(&usrsocktest_daemon_config); +} + +TEST(Poll, ConnectSendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + ConnectSend(&usrsocktest_daemon_config); +} + +TEST(Poll, DaemonAbort) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + DaemonAbort(&usrsocktest_daemon_config); +} + +TEST(Poll, DaemonAbortDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + DaemonAbort(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(Poll) +{ + RUN_TEST_CASE(Poll, ConnectReceive); + RUN_TEST_CASE(Poll, ConnectReceiveDelay); + RUN_TEST_CASE(Poll, ConnectSend); + RUN_TEST_CASE(Poll, ConnectSendDelay); + RUN_TEST_CASE(Poll, DaemonAbort); + RUN_TEST_CASE(Poll, DaemonAbortDelay); +} diff --git a/examples/usrsocktest/usrsocktest_remote_disconnect.c b/examples/usrsocktest/usrsocktest_remote_disconnect.c new file mode 100644 index 000000000..aa58b465a --- /dev/null +++ b/examples/usrsocktest/usrsocktest_remote_disconnect.c @@ -0,0 +1,1082 @@ +/**************************************************************************** + * usrsocktest/usrsocktest_remote_disconnect.c + * Remote end disconnects + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Roman Saveljev + * Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint8_t tevents[] = { POLLIN, POLLOUT, POLLOUT|POLLIN, 0}; +static bool started; +static int sd; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ConnectReceive + * + * Description: + * Remote end is unreachable + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Unreachable(struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + struct sockaddr_in addr; + int flags; + struct pollfd pfd; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = true; + dconf->endpoint_block_send = true; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 7; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Try connect, connection not accepted. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('F', 100)); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(ECONNREFUSED, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Try connect, connection in progress. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Disconnect pending connections. */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('F', 100)); + + /* Poll for input (no timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLERR, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + +} + +/**************************************************************************** + * Name: Send + * + * Description: + * Send and disconnect + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Send(struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + int count; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = false; + dconf->endpoint_block_send = false; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 7; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Try connect, connection in progress. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + + /* Disconnect connections. */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('D', 0)); + for (count = 0; usrsocktest_daemon_get_num_connected_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 3); + usleep(5 * 1000); + } + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + + for (count = 0; count < 2; count++) + { + /* Send data to remote */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = write(sd, data, datalen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EPIPE, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + } + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Send2 + * + * Description: + * Send and disconnect + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Send2(struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + int count; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = false; + dconf->endpoint_block_send = true; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 7; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + + /* Try connect. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + + /* Disconnect connections with delay. */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('D', 100)); + + for (count = 0; count < 2; count++) + { + /* Send data to remote */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = write(sd, data, datalen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EPIPE, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + } + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Receive + * + * Description: + * Receive and disconnect + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Receive(struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[5]; + int count; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = false; + dconf->endpoint_block_send = false; + dconf->endpoint_recv_avail_from_start = true; + dconf->endpoint_recv_avail = 1; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Try connect. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + /* Disconnect connections. */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('D', 0)); + for (count = 0; usrsocktest_daemon_get_num_connected_sockets() > 0; count++) + { + TEST_ASSERT_TRUE(count <= 3); + usleep(5 * 1000); + } + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_recv_empty_sockets()); + + for (count = 0; count < 2; count++) + { + /* Read from closed remote (EOF) */ + + data = databuf; + datalen = sizeof(databuf); + ret = read(sd, data, datalen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_recv_bytes()); + } + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Receive2 + * + * Description: + * Receive and disconnect + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Receive2(struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[5]; + int count; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_block_connect = false; + dconf->endpoint_block_send = true; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 7; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + + /* Try connect. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_unreachable_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + + /* Disconnect connections with delay. */ + + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('D', 100)); + + for (count = 0; count < 2; count++) + { + /* Read from closed remote (EOF) */ + + data = databuf; + datalen = sizeof(databuf); + ret = read(sd, data, datalen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_recv_bytes()); + } + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Poll + * + * Description: + * Poll and disconnect + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Poll(struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + struct sockaddr_in addr; + int flags; + struct pollfd pfd; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 3; + dconf->endpoint_block_send = false; + dconf->endpoint_block_connect = true; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Poll for input (instant timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = POLLIN; + ret = poll(&pfd, 1, 0); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(POLLIN, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + + /* Start non-blocking connect. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Poll for input/output (no timeout). Fail connection. */ + + memset(&pfd, 0, sizeof(pfd)); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('F', 100)); + pfd.fd = sd; + pfd.events = POLLOUT | POLLIN; + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLERR, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_unreachable_sockets()); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: Poll2 + * + * Description: + * Poll and disconnect + * + * Input Parameters: + * dconf - socket daemon configuration + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +static void Poll2(struct usrsocktest_daemon_conf_s *dconf) +{ + ssize_t ret; + size_t datalen; + void *data; + struct sockaddr_in addr; + char databuf[5]; + int flags; + struct pollfd pfd; + int count; + const uint8_t *events = tevents; + + /* Start test daemon. */ + + dconf->endpoint_addr = "127.0.0.1"; + dconf->endpoint_port = 255; + dconf->endpoint_recv_avail_from_start = false; + dconf->endpoint_recv_avail = 3; + dconf->endpoint_block_send = true; + dconf->endpoint_block_connect = false; + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(dconf)); + started = true; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + do + { + TEST_ASSERT_TRUE(*events == POLLIN || *events == POLLOUT || *events == (POLLOUT|POLLIN)); + + /* Open socket */ + + sd = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(sd >= 0); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + + /* Make socket non-blocking */ + + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(0, flags & O_NONBLOCK); + ret = fcntl(sd, F_SETFL, flags | O_NONBLOCK); + TEST_ASSERT_EQUAL(0, ret); + flags = fcntl(sd, F_GETFL, 0); + TEST_ASSERT_TRUE(flags >= 0); + TEST_ASSERT_EQUAL(O_RDWR, flags & O_RDWR); + TEST_ASSERT_EQUAL(O_NONBLOCK, flags & O_NONBLOCK); + + /* Poll for input (instant timeout). */ + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = sd; + pfd.events = *events; + ret = poll(&pfd, 1, 0); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(*events & POLLIN, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + + /* Start non-blocking connect. */ + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + if (!dconf->delay_all_responses) + { + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_waiting_connect_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + } + else + { + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EINPROGRESS, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + for (count = 0; usrsocktest_daemon_get_num_connected_sockets() != 1; count++) + { + TEST_ASSERT_TRUE(count <= 3); + usleep(25 * 1000); + } + + ret = connect(sd, (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EISCONN, errno); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_recv_empty_sockets()); + } + + /* Poll for output (no timeout). Close connection. */ + + memset(&pfd, 0, sizeof(pfd)); + TEST_ASSERT_TRUE(usrsocktest_send_delayed_command('D', 100)); + pfd.fd = sd; + pfd.events = *events; + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLERR); + TEST_ASSERT_EQUAL(POLLHUP, pfd.revents & POLLHUP); + TEST_ASSERT_EQUAL(*events & POLLIN, pfd.revents & POLLIN); + TEST_ASSERT_EQUAL(0, pfd.revents & POLLOUT); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + + for (count = 0; count < 2; count++) + { + /* Read from closed remote (EOF) */ + + data = databuf; + datalen = sizeof(databuf); + ret = read(sd, data, datalen); + TEST_ASSERT_EQUAL(0, ret); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_recv_bytes()); + } + + for (count = 0; count < 2; count++) + { + /* Send data to remote */ + + data = "abcde"; + datalen = strlen("abcde"); + ret = write(sd, data, datalen); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EPIPE, errno); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(1, usrsocktest_daemon_get_num_remote_disconnected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_send_bytes()); + } + + /* Close socket */ + + TEST_ASSERT_TRUE(close(sd) >= 0); + sd = -1; + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_connected_sockets()); + + events++; + } + while (*events); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + started = false; + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_active_sockets()); + TEST_ASSERT_EQUAL(-ENODEV, usrsocktest_daemon_get_num_connected_sockets()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); +} + +/**************************************************************************** + * Name: RemoteDisconnect test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(RemoteDisconnect) +{ + sd = -1; + started = false; +} + +/**************************************************************************** + * Name: RemoteDisconnect test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(RemoteDisconnect) +{ + int ret; + if (sd >= 0) + { + ret = close(sd); + assert(ret >= 0); + } + if (started) + { + ret = usrsocktest_daemon_stop(); + assert(ret == OK); + } +} + +TEST(RemoteDisconnect, Unreachable) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Unreachable(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, UnreachableDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Unreachable(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Send) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Send(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, SendDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Send(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Send2) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Send2(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Send2Delay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Send2(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Receive) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Receive(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, ReceiveDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Receive(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Receive2) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Receive2(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Receive2Delay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Receive2(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Poll) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Poll(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, PollDelay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Poll(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Poll2) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + Poll2(&usrsocktest_daemon_config); +} + +TEST(RemoteDisconnect, Poll2Delay) +{ + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = true; + Poll2(&usrsocktest_daemon_config); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(RemoteDisconnect) +{ + RUN_TEST_CASE(RemoteDisconnect, Unreachable); + RUN_TEST_CASE(RemoteDisconnect, UnreachableDelay); + RUN_TEST_CASE(RemoteDisconnect, Send); + RUN_TEST_CASE(RemoteDisconnect, SendDelay); + RUN_TEST_CASE(RemoteDisconnect, Send2); + RUN_TEST_CASE(RemoteDisconnect, Send2Delay); + RUN_TEST_CASE(RemoteDisconnect, Receive); + RUN_TEST_CASE(RemoteDisconnect, ReceiveDelay); + RUN_TEST_CASE(RemoteDisconnect, Receive2); + RUN_TEST_CASE(RemoteDisconnect, Receive2Delay); + RUN_TEST_CASE(RemoteDisconnect, Poll); + RUN_TEST_CASE(RemoteDisconnect, PollDelay); + RUN_TEST_CASE(RemoteDisconnect, Poll2); + RUN_TEST_CASE(RemoteDisconnect, Poll2Delay); +} + diff --git a/examples/usrsocktest/usrsocktest_wake_with_signal.c b/examples/usrsocktest/usrsocktest_wake_with_signal.c new file mode 100644 index 000000000..a6e213d09 --- /dev/null +++ b/examples/usrsocktest/usrsocktest_wake_with_signal.c @@ -0,0 +1,1658 @@ +/**************************************************************************** + * examples/usrsocktest/usrsocktest_wake_with_signal.c + * Wake blocked IO with signal or daemon abort + * + * Copyright (C) 2015, 2017 Haltian Ltd. All rights reserved. + * Authors: Jussi Kivilinna + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "defines.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#define TEST_FLAG_PAUSE_USRSOCK_HANDLING (1 << 0) +#define TEST_FLAG_DAEMON_ABORT (1 << 1) +#define TEST_FLAG_MULTI_THREAD (1 << 2) + +#define MAX_THREADS 4 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +enum e_test_type +{ + TEST_TYPE_SOCKET = 0, + TEST_TYPE_CLOSE, + TEST_TYPE_CONNECT, + TEST_TYPE_SETSOCKOPT, + TEST_TYPE_GETSOCKOPT, + TEST_TYPE_SEND, + TEST_TYPE_RECV, + TEST_TYPE_POLL, + __TEST_TYPE_MAX, +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ +static pthread_t tid[MAX_THREADS]; +static sem_t tid_startsem; +static sem_t tid_releasesem; +static int test_sd[MAX_THREADS]; +static enum e_test_type test_type; +static int test_flags; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void do_usrsock_blocking_socket_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + TEST_ASSERT_TRUE(test_hang); + TEST_ASSERT_TRUE(test_abort); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt hanging open socket. */ + + sem_post(&tid_startsem); + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_EQUAL(-1, test_sd[tidx]); + TEST_ASSERT_EQUAL(ENETDOWN, errno); +} + +static FAR void * usrsock_blocking_socket_thread(FAR void *param) +{ + do_usrsock_blocking_socket_thread(param); + return NULL; +} + +static void do_usrsock_blocking_close_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + struct sockaddr_in addr; + int ret; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + TEST_ASSERT_TRUE(test_hang); + TEST_ASSERT_TRUE(test_abort); + + /* Open socket. */ + + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(test_sd[tidx] >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt hanging close socket. */ + + sem_post(&tid_startsem); + ret = close(test_sd[tidx]); + TEST_ASSERT_EQUAL(0, ret); + test_sd[tidx] = -1; +} + +static FAR void * usrsock_blocking_close_thread(FAR void *param) +{ + do_usrsock_blocking_close_thread(param); + return NULL; +} + +static void do_usrsock_blocking_connect_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + struct sockaddr_in addr; + int ret; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + TEST_ASSERT_TRUE(test_hang || !test_hang); + + /* Open socket. */ + + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(test_sd[tidx] >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt blocking connect. */ + + sem_post(&tid_startsem); + ret = connect(test_sd[tidx], (FAR const struct sockaddr *)&addr, + sizeof(addr)); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(test_abort ? ECONNABORTED : EINTR, errno); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(test_sd[tidx]) >= 0); + test_sd[tidx] = -1; +} + +static FAR void * usrsock_blocking_connect_thread(FAR void *param) +{ + do_usrsock_blocking_connect_thread(param); + return NULL; +} + +static void do_usrsock_blocking_setsockopt_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + struct sockaddr_in addr; + int ret; + int value; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + /* Open socket. */ + + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(test_sd[tidx] >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + + TEST_ASSERT_TRUE(test_hang); + TEST_ASSERT_TRUE(test_abort); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt hanging setsockopt. */ + + sem_post(&tid_startsem); + value = 1; + ret = setsockopt(test_sd[tidx], SOL_SOCKET, SO_REUSEADDR, &value, + sizeof(value)); + TEST_ASSERT_EQUAL(-1, ret); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(test_sd[tidx]) >= 0); + test_sd[tidx] = -1; +} + +static FAR void * usrsock_blocking_setsockopt_thread(FAR void *param) +{ + do_usrsock_blocking_setsockopt_thread(param); + return NULL; +} + +static void do_usrsock_blocking_getsockopt_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + struct sockaddr_in addr; + int ret; + int value; + socklen_t valuelen; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + /* Open socket. */ + + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(test_sd[tidx] >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + + TEST_ASSERT_TRUE(test_hang); + TEST_ASSERT_TRUE(test_abort); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt hanging getsockopt. */ + + sem_post(&tid_startsem); + value = -1; + valuelen = sizeof(value); + ret = getsockopt(test_sd[tidx], SOL_SOCKET, SO_REUSEADDR, &value, &valuelen); + TEST_ASSERT_EQUAL(-1, ret); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(test_sd[tidx]) >= 0); + test_sd[tidx] = -1; +} + +static FAR void * usrsock_blocking_getsockopt_thread(FAR void *param) +{ + do_usrsock_blocking_getsockopt_thread(param); + return NULL; +} + +static void do_usrsock_blocking_send_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + struct sockaddr_in addr; + int ret; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + TEST_ASSERT_TRUE(test_hang || !test_hang); + + /* Open socket. */ + + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(test_sd[tidx] >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + + /* Connect socket. */ + + ret = connect(test_sd[tidx], (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt blocking send. */ + + sem_post(&tid_startsem); + ret = send(test_sd[tidx], &addr, sizeof(addr), 0); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(test_abort ? EPIPE : EINTR, errno); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(test_sd[tidx]) >= 0); + test_sd[tidx] = -1; +} + +static FAR void * usrsock_blocking_send_thread(FAR void *param) +{ + do_usrsock_blocking_send_thread(param); + return NULL; +} + +static void do_usrsock_blocking_recv_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + struct sockaddr_in addr; + int ret; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + TEST_ASSERT_TRUE(test_hang || !test_hang); + + /* Open socket. */ + + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(test_sd[tidx] >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + + /* Connect socket. */ + + ret = connect(test_sd[tidx], (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt blocking recv. */ + + sem_post(&tid_startsem); + ret = recv(test_sd[tidx], &addr, sizeof(addr), 0); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(test_abort ? EPIPE : EINTR, errno); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(test_sd[tidx]) >= 0); + test_sd[tidx] = -1; +} + +static FAR void * usrsock_blocking_recv_thread(FAR void *param) +{ + do_usrsock_blocking_recv_thread(param); + return NULL; +} + +static void do_usrsock_blocking_poll_thread(FAR void *param) +{ + intptr_t tidx = (intptr_t)param; + struct sockaddr_in addr; + int ret; + struct pollfd pfd = {}; + bool test_abort = !!(test_flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(test_flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + TEST_ASSERT_TRUE(test_abort); + TEST_ASSERT_TRUE(test_hang || !test_hang); + + /* Open socket. */ + + test_sd[tidx] = socket(AF_INET, SOCK_STREAM, 0); + TEST_ASSERT_TRUE(test_sd[tidx] >= 0); + + inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr); + addr.sin_family = AF_INET; + addr.sin_port = htons(255); + + /* Connect socket. */ + + ret = connect(test_sd[tidx], (FAR const struct sockaddr *)&addr, sizeof(addr)); + TEST_ASSERT_EQUAL(0, ret); + + /* Allow main thread to hang usrsock daemon at this point. */ + + sem_post(&tid_startsem); + sem_wait(&tid_releasesem); + + /* Attempt poll. */ + + pfd.fd = test_sd[tidx]; + pfd.events = POLLIN; + + sem_post(&tid_startsem); + ret = poll(&pfd, 1, -1); + TEST_ASSERT_EQUAL(1, ret); + TEST_ASSERT_EQUAL(POLLERR | POLLHUP, pfd.revents); + + /* Attempt read from aborted socket */ + + ret = recv(test_sd[tidx], &addr, sizeof(addr), 0); + TEST_ASSERT_EQUAL(-1, ret); + TEST_ASSERT_EQUAL(EPIPE, errno); + + /* Close socket */ + + TEST_ASSERT_TRUE(close(test_sd[tidx]) >= 0); + test_sd[tidx] = -1; +} + +static FAR void * usrsock_blocking_poll_thread(FAR void *param) +{ + do_usrsock_blocking_poll_thread(param); + return NULL; +} + +static void do_wake_test(enum e_test_type type, int flags) +{ + static const struct + { + pthread_startroutine_t fn; + bool stop_only_on_hang; + } thread_funcs[__TEST_TYPE_MAX] = + { + [TEST_TYPE_SOCKET] = { usrsock_blocking_socket_thread, false }, + [TEST_TYPE_CLOSE] = { usrsock_blocking_close_thread, false }, + [TEST_TYPE_CONNECT] = { usrsock_blocking_connect_thread, true }, + [TEST_TYPE_SETSOCKOPT] = { usrsock_blocking_setsockopt_thread, false }, + [TEST_TYPE_GETSOCKOPT] = { usrsock_blocking_getsockopt_thread, false }, + [TEST_TYPE_RECV] = { usrsock_blocking_recv_thread, true }, + [TEST_TYPE_SEND] = { usrsock_blocking_send_thread, true }, + [TEST_TYPE_POLL] = { usrsock_blocking_poll_thread, true }, + }; + int ret; + int nthreads = (flags & TEST_FLAG_MULTI_THREAD) ? MAX_THREADS : 1; + int tidx; + bool test_abort = !!(flags & TEST_FLAG_DAEMON_ABORT); + bool test_hang = !!(flags & TEST_FLAG_PAUSE_USRSOCK_HANDLING); + + /* Start test daemon. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_start(&usrsocktest_daemon_config)); + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_get_num_active_sockets()); + + /* Launch worker threads. */ + + test_type = type; + test_flags = flags; + for (tidx = 0; tidx < nthreads; tidx++) + { + ret = pthread_create(&tid[tidx], NULL, thread_funcs[type].fn, + (pthread_addr_t)(intptr_t)tidx); + TEST_ASSERT_EQUAL(OK, ret); + } + +/* Let workers to start. */ + + for (tidx = 0; tidx < nthreads; tidx++) + { + sem_wait(&tid_startsem); + } + + if (test_hang || !thread_funcs[type].stop_only_on_hang) + { + TEST_ASSERT_EQUAL(0, usrsocktest_daemon_pause_usrsock_handling(true)); + } + + for (tidx = 0; tidx < nthreads; tidx++) + { + sem_post(&tid_releasesem); + } + for (tidx = 0; tidx < nthreads; tidx++) + { + sem_wait(&tid_startsem); + } + + usleep(100 * USEC_PER_MSEC); /* Let worker thread proceed to blocking + * function. */ + + if (!test_abort) + { + /* Wake waiting thread with signal. */ + + /* Send signal to task to break out from blocking send. */ + + for (tidx = 0; tidx < nthreads; tidx++) + { + pthread_kill(tid[tidx], 1); + + /* Wait threads to complete work. */ + + ret = pthread_join(tid[tidx], NULL); + TEST_ASSERT_EQUAL(OK, ret); + tid[tidx] = -1; + } + TEST_ASSERT_FALSE(usrsocktest_test_failed); + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + } + else + { + /* Wake waiting thread with daemon abort. */ + + /* Stopping daemon should succeed. */ + + TEST_ASSERT_EQUAL(OK, usrsocktest_daemon_stop()); + TEST_ASSERT_EQUAL(0, usrsocktest_endp_malloc_cnt); + TEST_ASSERT_EQUAL(0, usrsocktest_dcmd_malloc_cnt); + + /* Wait threads to complete work. */ + + for (tidx = 0; tidx < nthreads; tidx++) + { + ret = pthread_join(tid[tidx], NULL); + TEST_ASSERT_EQUAL(OK, ret); + tid[tidx] = -1; + } + TEST_ASSERT_FALSE(usrsocktest_test_failed); + } +} + +/**************************************************************************** + * Name: WakeWithSignal test group setup + * + * Description: + * Setup function executed before each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_SETUP(WakeWithSignal) +{ + int i; + + for (i = 0; i < MAX_THREADS; i++) + { + tid[i] = -1; + test_sd[i] = -1; + } + sem_init(&tid_startsem, 0, 0); + sem_init(&tid_releasesem, 0, 0); +} + +/**************************************************************************** + * Name: WakeWithSignal test group teardown + * + * Description: + * Setup function executed after each testcase in this test group + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST_TEAR_DOWN(WakeWithSignal) +{ + int ret; + int i; + + for (i = 0; i < MAX_THREADS; i++) + { + if (tid[i] != -1) + { + ret = pthread_cancel(tid[i]); + assert(ret == OK); + ret = pthread_join(tid[i], NULL); + assert(ret == OK); + } + if (test_sd[i] != -1) + { + close(test_sd[i]); + test_sd[i] = -1; + } + } + sem_destroy(&tid_startsem); + sem_destroy(&tid_releasesem); +} + +/**************************************************************************** + * Name: WakeBlockingConnect + * + * Description: + * Wake blocking connect with signal + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, WakeBlockingConnect) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CONNECT, 0); +} + +/**************************************************************************** + * Name: WakeBlockingConnectMultiThread + * + * Description: + * Wake multiple blocking connect with signal + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, WakeBlockingConnectMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CONNECT, TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: WakeBlockingSend + * + * Description: + * Wake blocking send with signal + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, WakeBlockingSend) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SEND, 0); +} + +/**************************************************************************** + * Name: WakeBlockingSendMultiThread + * + * Description: + * Wake multiple blocking send with signal + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, WakeBlockingSendMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SEND, TEST_FLAG_MULTI_THREAD); +} +/**************************************************************************** + * Name: WakeBlockingRecv + * + * Description: + * Wake blocking recv with signal + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, WakeBlockingRecv) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = false; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_RECV, 0); +} + +/**************************************************************************** + * Name: WakeBlockingRecvMultiThread + * + * Description: + * Wake multiple blocking recv with signal + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, WakeBlockingRecvMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = false; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_RECV, TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: AbortBlockingConnect + * + * Description: + * Wake blocking connect with daemon abort + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, AbortBlockingConnect) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CONNECT, TEST_FLAG_DAEMON_ABORT); +} + +/**************************************************************************** + * Name: AbortBlockingConnectMultiThread + * + * Description: + * Wake multiple blocking connect with daemon abort + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, AbortBlockingConnectMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CONNECT, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: AbortBlockingSend + * + * Description: + * Wake blocking send with daemon abort + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, AbortBlockingSend) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SEND, TEST_FLAG_DAEMON_ABORT); +} + +/**************************************************************************** + * Name: AbortBlockingSendMultiThread + * + * Description: + * Wake multiple blocking send with daemon abort + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, AbortBlockingSendMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SEND, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: AbortBlockingRecv + * + * Description: + * Wake blocking recv with daemon abort + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, AbortBlockingRecv) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = false; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_RECV, TEST_FLAG_DAEMON_ABORT); +} + +/**************************************************************************** + * Name: AbortBlockingRecvMultiThread + * + * Description: + * Wake multiple blocking recv with daemon abort + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, AbortBlockingRecvMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = false; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_RECV, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingConnect + * + * Description: + * Wake blocking connect with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingConnect) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CONNECT, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingConnectMultiThread + * + * Description: + * Wake multiple blocking connect with daemon abort (and daemon not handling + * pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingConnectMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CONNECT, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingSend + * + * Description: + * Wake blocking send with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingSend) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SEND, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingSendMultiThread + * + * Description: + * Wake multiple blocking send with daemon abort (and daemon not handling + * pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingSendMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SEND, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingRecv + * + * Description: + * Wake blocking recv with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingRecv) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = false; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_RECV, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingRecvMultiThread + * + * Description: + * Wake multiple blocking recv with daemon abort (and daemon not handling + * pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingRecvMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = false; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_RECV, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingOpen + * + * Description: + * Wake blocking open with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingOpen) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SOCKET, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingOpenMultiThread + * + * Description: + * Wake multiple blocking open with daemon abort (and daemon not handling + * pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingOpenMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SOCKET, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingClose + * + * Description: + * Wake blocking close with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ +TEST(WakeWithSignal, PendingRequestBlockingClose) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CLOSE, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingCloseMultiThread + * + * Description: + * Wake multiple blocking close with daemon abort (and daemon not handling + * pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingCloseMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_CLOSE, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingPoll + * + * Description: + * Wake blocking poll with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingPoll) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_POLL, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingPollMultiThread + * + * Description: + * Wake multiple blocking poll with daemon abort (and daemon not handling + * pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingPollMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = false; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_POLL, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingSetSockOpt + * + * Description: + * Wake blocking setsockopt with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingSetSockOpt) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SETSOCKOPT, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingSetSockOptMultiThread + * + * Description: + * Wake multiple blocking setsockopt with daemon abort (and daemon not + * handling pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingSetSockOptMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_SETSOCKOPT, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Name: PendingRequestBlockingGetSockOpt + * + * Description: + * Wake blocking getsockopt with daemon abort (and daemon not handling pending + * request before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingGetSockOpt) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_GETSOCKOPT, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING); +} + +/**************************************************************************** + * Name: PendingRequestBlockingGetSockOptMultiThread + * + * Description: + * Wake multiple blocking getsockopt with daemon abort (and daemon not + * handling pending requests before abort) + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + * Assumptions/Limitations: + * None + * + ****************************************************************************/ + +TEST(WakeWithSignal, PendingRequestBlockingGetSockOptMultiThread) +{ + /* Configure test daemon. */ + + usrsocktest_daemon_config = usrsocktest_daemon_defconf; + usrsocktest_daemon_config.delay_all_responses = false; + usrsocktest_daemon_config.endpoint_block_send = true; + usrsocktest_daemon_config.endpoint_block_connect = true; + usrsocktest_daemon_config.endpoint_recv_avail = 0; + usrsocktest_daemon_config.endpoint_addr = "127.0.0.1"; + usrsocktest_daemon_config.endpoint_port = 255; + + /* Run test. */ + + do_wake_test(TEST_TYPE_GETSOCKOPT, + TEST_FLAG_DAEMON_ABORT | TEST_FLAG_PAUSE_USRSOCK_HANDLING | + TEST_FLAG_MULTI_THREAD); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +TEST_GROUP(WakeWithSignal) +{ + RUN_TEST_CASE(WakeWithSignal, WakeBlockingConnect); + RUN_TEST_CASE(WakeWithSignal, WakeBlockingConnectMultiThread); + RUN_TEST_CASE(WakeWithSignal, WakeBlockingSend); + RUN_TEST_CASE(WakeWithSignal, WakeBlockingSendMultiThread); + RUN_TEST_CASE(WakeWithSignal, WakeBlockingRecv); + RUN_TEST_CASE(WakeWithSignal, WakeBlockingRecvMultiThread); + RUN_TEST_CASE(WakeWithSignal, AbortBlockingConnect); + RUN_TEST_CASE(WakeWithSignal, AbortBlockingConnectMultiThread); + RUN_TEST_CASE(WakeWithSignal, AbortBlockingSend); + RUN_TEST_CASE(WakeWithSignal, AbortBlockingSendMultiThread); + RUN_TEST_CASE(WakeWithSignal, AbortBlockingRecv); + RUN_TEST_CASE(WakeWithSignal, AbortBlockingRecvMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingConnect); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingConnectMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingSend); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingSendMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingRecv); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingRecvMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingOpen); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingOpenMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingClose); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingCloseMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingPoll); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingPollMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingSetSockOpt); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingSetSockOptMultiThread); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingGetSockOpt); + RUN_TEST_CASE(WakeWithSignal, PendingRequestBlockingGetSockOptMultiThread); +} + diff --git a/netutils/tftpc/tftpc_internal.h b/netutils/tftpc/tftpc_internal.h index 8516aef37..47a7bd0cd 100644 --- a/netutils/tftpc/tftpc_internal.h +++ b/netutils/tftpc/tftpc_internal.h @@ -48,6 +48,8 @@ #include #include +#include +#include /**************************************************************************** * Pre-processor Definitions diff --git a/nshlib/README.txt b/nshlib/README.txt index 05ba319e9..8edf053b7 100644 --- a/nshlib/README.txt +++ b/nshlib/README.txt @@ -1017,10 +1017,10 @@ o rmmod NAME INIT UNINIT ARG TEXT SIZE DATA SIZE nsh> -o set +o set [{+|-}{e|x|xe|ex}] [ ] - Set the environment variable to the sting . - For example, + Set the environment variable to the sting and or set NSH + parser control options. For example, nsh> echo $foobar @@ -1029,6 +1029,38 @@ o set foovalue nsh> + Set the 'exit on error control' and/or 'print a trace' of commands when parsing + scripts in NSH. The settinngs are in effect from the point of exection, until + they are changed again, or in the case of the init script, the settings are + returned to the default settings when it exits. Included child scripts will run + with the parents settings and changes made in the child script will effect the + parent on return. + + Use 'set -e' to enable and 'set +e' to disable (ignore) the exit condition on commands. + The default is -e. Errors cause script to exit. + + Use 'set -x' to enable and 'set +x' to disable (silence) printing a trace of the script + commands as they are ececuted. + The default is +x. No printing of a trace of script commands as they are executed. + + Example 1 - no exit on command not found + set +e + notacommand + + Example 2 - will exit on command not found + set -e + notacommand + + Example 3 - will exit on command not found, and print a trace of the script commmands + set -ex + + Example 4 - will exit on command not found, and print a trace of the script commmands + and set foobar to foovalue. + set -ex foobar foovalue + nsh> echo $foobar + foovalue + + o sh Execute the sequence of NSH commands in the file referred diff --git a/nshlib/nsh.h b/nshlib/nsh.h index 25ef9ce46..94e7e55d8 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -690,6 +690,15 @@ # undef NSH_HAVE_TRIMSPACES #endif +#ifndef CONFIG_NSH_DISABLESCRIPT +# define NSH_NP_SET_OPTIONS "ex" /* Maintain order see nsh_npflags_e */ +# define NSH_NP_SET_OPTIONS_INIT (NSH_PFLAG_SILENT) +#endif + +#if defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_NSH_DISABLESCRIPT) +# define CONFIG_NSH_DISABLE_SET +#endif + /**************************************************************************** * Public Types ****************************************************************************/ @@ -741,6 +750,22 @@ struct nsh_loop_s }; #endif +#ifndef CONFIG_NSH_DISABLESCRIPT +/* Define the bits that correspond to the option defined in + * NSH_NP_SET_OPTIONS. The bit value is 1 shifted left the offset + * of the char in NSH_NP_SET_OPTIONS string. + */ + +enum nsh_npflags_e +{ + NSH_PFLAG_IGNORE = 1, /* set for +e no exit on errors, + * cleared -e exit on error */ + NSH_PFLAG_SILENT = 2, /* cleared -x print a trace of commands + * when parsing. + * set +x no print a trace of commands */ +}; +#endif + /* These structure provides the overall state of the parser */ struct nsh_parser_s @@ -752,6 +777,9 @@ struct nsh_parser_s bool np_redirect; /* true: Output from the last command was re-directed */ #endif bool np_fail; /* true: The last command failed */ +#ifndef CONFIG_NSH_DISABLESCRIPT + uint8_t np_flags; /* See nsh_npflags_e above */ +#endif #ifndef CONFIG_NSH_DISABLEBG int np_nice; /* "nice" value applied to last background cmd */ #endif @@ -1175,10 +1203,10 @@ int cmd_lsmod(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); int cmd_uname(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); #endif -#ifndef CONFIG_DISABLE_ENVIRON -# ifndef CONFIG_NSH_DISABLE_SET +#ifndef CONFIG_NSH_DISABLE_SET int cmd_set(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); -# endif +#endif +#ifndef CONFIG_DISABLE_ENVIRON # ifndef CONFIG_NSH_DISABLE_UNSET int cmd_unset(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv); # endif diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c index ad68fd7c9..2d8b731d2 100644 --- a/nshlib/nsh_command.c +++ b/nshlib/nsh_command.c @@ -436,10 +436,14 @@ static const struct cmdmap_s g_cmdmap[] = { "rmmod", cmd_rmmod, 2, 2, "" }, #endif -#ifndef CONFIG_DISABLE_ENVIRON -# ifndef CONFIG_NSH_DISABLE_SET +#ifndef CONFIG_NSH_DISABLE_SET +# if !defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_NSH_DISABLESCRIPT) + { "set", cmd_set, 2, 4, "[{+|-}{e|x|xe|ex}] [ ]" }, +# elif !defined(CONFIG_DISABLE_ENVIRON) && defined(CONFIG_NSH_DISABLESCRIPT) { "set", cmd_set, 3, 3, " " }, -# endif +# elif defined(CONFIG_DISABLE_ENVIRON) && !defined(CONFIG_NSH_DISABLESCRIPT) + { "set", cmd_set, 2, 2, "{+|-}{e|x|xe|ex}" }, +# endif #endif #if CONFIG_NFILE_DESCRIPTORS > 0 && CONFIG_NFILE_STREAMS > 0 && !defined(CONFIG_NSH_DISABLESCRIPT) diff --git a/nshlib/nsh_console.c b/nshlib/nsh_console.c index 6d68e3acb..1a22f0a66 100644 --- a/nshlib/nsh_console.c +++ b/nshlib/nsh_console.c @@ -480,5 +480,11 @@ FAR struct console_stdio_s *nsh_newconsole(void) #endif } +#ifndef CONFIG_NSH_DISABLESCRIPT + /* Set the initial option flags */ + + pstate->cn_vtbl.np.np_flags = NSH_NP_SET_OPTIONS_INIT; +#endif + return pstate; } diff --git a/nshlib/nsh_consolemain.c b/nshlib/nsh_consolemain.c index f89944ff6..b61cf15af 100644 --- a/nshlib/nsh_consolemain.c +++ b/nshlib/nsh_consolemain.c @@ -85,6 +85,12 @@ int nsh_consolemain(int argc, char *argv[]) /* Execute the start-up script */ (void)nsh_initscript(&pstate->cn_vtbl); + +#ifndef CONFIG_NSH_DISABLESCRIPT + /* Reset the option flags */ + + pstate->cn_vtbl.np.np_flags = NSH_NP_SET_OPTIONS_INIT; +#endif #endif #ifdef CONFIG_NSH_USBDEV_TRACE diff --git a/nshlib/nsh_envcmds.c b/nshlib/nsh_envcmds.c index cbda78ba1..b323d9538 100644 --- a/nshlib/nsh_envcmds.c +++ b/nshlib/nsh_envcmds.c @@ -311,29 +311,86 @@ int cmd_pwd(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) * Name: cmd_set ****************************************************************************/ -#ifndef CONFIG_DISABLE_ENVIRON #ifndef CONFIG_NSH_DISABLE_SET int cmd_set(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) { FAR char *value; - int ret; + int ret = OK; + int ndx = 1; +#ifndef CONFIG_NSH_DISABLESCRIPT + FAR char *popt; + const char opts[] = NSH_NP_SET_OPTIONS; + int op; - /* Trim whitespace from the value */ + /* Support set [{+|-}{e|x|xe|ex}] [ ] */ - value = nsh_trimspaces(argv[2]); - - /* Set the environment variable */ - - ret = setenv(argv[1], value, TRUE); - if (ret < 0) + if (argc == 2 || argc == 4) { - nsh_output(vtbl, g_fmtcmdfailed, argv[0], "setenv", NSH_ERRNO); - } + if (strlen(argv[1]) < 2) + { + ret = -EINVAL; + nsh_output(vtbl, g_fmtargrequired, argv[0], "set", NSH_ERRNO); + } + else + { + op = argv[1][0]; + if (op != '-' && op != '+') + { + ret = -EINVAL; + nsh_output(vtbl, g_fmtarginvalid, argv[0], "set", NSH_ERRNO); + } + else + { + value = &argv[1][1]; + while(*value && *value != ' ') + { + popt = strchr(opts, *value++); + if (popt == NULL) + { + nsh_output(vtbl, g_fmtarginvalid, argv[0], "set", NSH_ERRNO); + ret = -EINVAL; + break; + } + if (op == '+') + { + vtbl->np.np_flags |= 1 << (popt-opts); + } + else + { + vtbl->np.np_flags &= ~(1 << (popt-opts)); + } + } + + if (ret == OK) + { + ndx = 2; + } + } + } + } +# ifndef CONFIG_DISABLE_ENVIRON + if (ret == OK && (argc == 3 || argc == 4)) +# endif +#endif +#ifndef CONFIG_DISABLE_ENVIRON + { + /* Trim whitespace from the value */ + + value = nsh_trimspaces(argv[ndx+1]); + + /* Set the environment variable */ + + ret = setenv(argv[ndx], value, TRUE); + if (ret < 0) + { + nsh_output(vtbl, g_fmtcmdfailed, argv[0], "setenv", NSH_ERRNO); + } + } +#endif return ret; } #endif -#endif /**************************************************************************** * Name: cmd_unset diff --git a/nshlib/nsh_netcmds.c b/nshlib/nsh_netcmds.c index 7d167825e..592f02b3f 100644 --- a/nshlib/nsh_netcmds.c +++ b/nshlib/nsh_netcmds.c @@ -575,7 +575,7 @@ static int nsh_gethostip(FAR char *hostname, FAR union ip_addr_u *ipaddr, static void wget_callback(FAR char **buffer, int offset, int datend, FAR int *buflen, FAR void *arg) { - (void)write((int)arg, &((*buffer)[offset]), datend - offset); + (void)write((int)((intptr_t)arg), &((*buffer)[offset]), datend - offset); } #endif #endif @@ -1668,7 +1668,7 @@ int cmd_wget(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv) /* And perform the wget */ - ret = wget(url, buffer, 512, wget_callback, (FAR void *)fd); + ret = wget(url, buffer, 512, wget_callback, (FAR void *)((intptr_t)fd)); if (ret < 0) { nsh_output(vtbl, g_fmtcmdfailed, argv[0], "wget", NSH_ERRNO); diff --git a/nshlib/nsh_netinit.c b/nshlib/nsh_netinit.c index f49a45120..8ee0958ef 100644 --- a/nshlib/nsh_netinit.c +++ b/nshlib/nsh_netinit.c @@ -101,13 +101,17 @@ /* Select the single network device name supported this this network * initialization logci. If multiple interfaces are present with different - * link types, the the orider of definition in the following conditional - * logic will select the one interface that will be used. + * link types, the the order of definition in the following conditional + * logic will select the one interface that will be used (which might + * not be the one that you want). */ #if defined(CONFIG_NET_ETHERNET) # define NET_DEVNAME "eth0" # define NSH_HAVE_NETDEV +#elif defined(CONFIG_NET_6LOWPAN) +# define NET_DEVNAME "wpan0" +# define NSH_HAVE_NETDEV #elif defined(CONFIG_NET_SLIP) # define NET_DEVNAME "sl0" # ifndef CONFIG_NSH_NOMAC @@ -120,6 +124,8 @@ #elif defined(CONFIG_NET_LOCAL) # define NET_DEVNAME "lo" # define NSH_HAVE_NETDEV +#elif defined(CONFIG_NET_USRSOCK) +# undef NSH_HAVE_NETDEV #elif !defined(CONFIG_NET_LOOPBACK) # error ERROR: No link layer protocol defined #endif @@ -200,7 +206,7 @@ static const uint16_t g_ipv6_netmask[8] = HTONS(CONFIG_NSH_IPv6NETMASK_7), HTONS(CONFIG_NSH_IPv6NETMASK_8), }; -#endif /* CONFIG_NET_IPv6 && !CONFIG_NET_ICMPv6_AUTOCONF*/ +#endif /* CONFIG_NET_IPv6 && !CONFIG_NET_ICMPv6_AUTOCONF */ /**************************************************************************** * Private Functions diff --git a/nshlib/nsh_parse.c b/nshlib/nsh_parse.c index 231dc2526..ecea05791 100644 --- a/nshlib/nsh_parse.c +++ b/nshlib/nsh_parse.c @@ -1169,6 +1169,11 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline, envstr = nsh_envexpand(vtbl, ptr); + if ((vtbl->np.np_flags & NSH_PFLAG_SILENT) == 0) + { + nsh_output(vtbl," %s=%s\n", ptr, envstr ? envstr :"(null)"); + } + /* Concatenate the result of the operation with the accumulated * string. On failures to allocation memory, nsh_strcat will * just return value value of argument diff --git a/nshlib/nsh_script.c b/nshlib/nsh_script.c index 2c4a369bd..37486c252 100644 --- a/nshlib/nsh_script.c +++ b/nshlib/nsh_script.c @@ -136,10 +136,15 @@ int nsh_script(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, * considerable amount of stack may be used. */ + if ((vtbl->np.np_flags & NSH_PFLAG_SILENT) == 0) + { + nsh_output(vtbl,"%s", buffer); + } + ret = nsh_parse(vtbl, buffer); } } - while (pret && ret == OK); + while (pret && (ret == OK || (vtbl->np.np_flags & NSH_PFLAG_IGNORE))); /* Close the script file */