tests: sockets: Add TCP test cases

- Test ipv4/ipv6 send() and recv().
- Test ipv4/ipv6 sendto() and recvfrom().
- Test ipv4/ipv6 sendto() and recvfrom() with NULL dest address.

Signed-off-by: Aska Wu <aska.wu@linaro.org>
diff --git a/tests/net/socket/tcp/Makefile b/tests/net/socket/tcp/Makefile
new file mode 100644
index 0000000..295b8c2
--- /dev/null
+++ b/tests/net/socket/tcp/Makefile
@@ -0,0 +1,11 @@
+#
+# Copyright (c) 2017 Linaro Limited
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+BOARD ?= qemu_x86
+CONF_FILE ?= prj.conf
+
+include $(ZEPHYR_BASE)/Makefile.inc
+include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack
diff --git a/tests/net/socket/tcp/prj.conf b/tests/net/socket/tcp/prj.conf
new file mode 100644
index 0000000..7ff6fb3
--- /dev/null
+++ b/tests/net/socket/tcp/prj.conf
@@ -0,0 +1,30 @@
+# General config
+CONFIG_NEWLIB_LIBC=y
+
+# Networking config
+CONFIG_NETWORKING=y
+CONFIG_NET_IPV4=y
+CONFIG_NET_IPV6=y
+CONFIG_NET_TCP=y
+CONFIG_NET_SOCKETS=y
+CONFIG_NET_SOCKETS_POSIX_NAMES=y
+
+# Network driver config
+CONFIG_TEST_RANDOM_GENERATOR=y
+
+# Network address config
+CONFIG_NET_APP_SETTINGS=y
+CONFIG_NET_APP_NEED_IPV4=y
+CONFIG_NET_APP_NEED_IPV6=y
+CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1"
+CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1"
+
+CONFIG_MAIN_STACK_SIZE=2048
+
+# Required:
+# Net pkt will be reused since src and dst address are the same.
+# It takes at least 3 tx pkt to establish TCP connection (syn/syn-ack/ack)
+CONFIG_NET_PKT_TX_COUNT=6
+
+CONFIG_ZTEST=y
+CONFIG_ZTEST_STACKSIZE=2048
diff --git a/tests/net/socket/tcp/src/Makefile b/tests/net/socket/tcp/src/Makefile
new file mode 100644
index 0000000..513cbee
--- /dev/null
+++ b/tests/net/socket/tcp/src/Makefile
@@ -0,0 +1,3 @@
+obj-y += main.o
+ccflags-y += -I${ZEPHYR_BASE}/tests/include
+ccflags-y += -I${ZEPHYR_BASE}/tests/ztest/include
diff --git a/tests/net/socket/tcp/src/main.c b/tests/net/socket/tcp/src/main.c
new file mode 100644
index 0000000..13732bb
--- /dev/null
+++ b/tests/net/socket/tcp/src/main.c
@@ -0,0 +1,369 @@
+#include <ztest_assert.h>
+#include <net/socket.h>
+
+#define TEST_STR_SMALL "test"
+
+#define ANY_PORT 0
+#define SERVER_PORT 4242
+
+#define MAX_CONNS 5
+
+#define TCP_TEARDOWN_TIMEOUT K_SECONDS(1)
+
+static void prepare_sock_v4(const char *addr,
+			    u16_t port,
+			    int *sock,
+			    struct sockaddr_in *sockaddr)
+{
+	int rv;
+
+	zassert_not_null(addr, "null addr");
+	zassert_not_null(sock, "null sock");
+	zassert_not_null(sockaddr, "null sockaddr");
+
+	*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	zassert_true(*sock >= 0, "socket open failed");
+
+	sockaddr->sin_family = AF_INET;
+	sockaddr->sin_port = htons(port);
+	rv = inet_pton(AF_INET, addr, &sockaddr->sin_addr);
+	zassert_equal(rv, 1, "inet_pton failed");
+}
+
+static void prepare_sock_v6(const char *addr,
+			    u16_t port,
+			    int *sock,
+			    struct sockaddr_in6 *sockaddr)
+{
+	int rv;
+
+	zassert_not_null(addr, "null addr");
+	zassert_not_null(sock, "null sock");
+	zassert_not_null(sockaddr, "null sockaddr");
+
+	*sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+	zassert_true(*sock >= 0, "socket open failed");
+
+	sockaddr->sin6_family = AF_INET6;
+	sockaddr->sin6_port = htons(port);
+	rv = inet_pton(AF_INET6, addr, &sockaddr->sin6_addr);
+	zassert_equal(rv, 1, "inet_pton failed");
+}
+
+static void test_bind(int sock, struct sockaddr *addr, socklen_t addrlen)
+{
+	zassert_equal(bind(sock, addr, addrlen),
+		      0,
+		      "bind failed");
+}
+
+static void test_listen(int sock)
+{
+	zassert_equal(listen(sock, MAX_CONNS),
+		      0,
+		      "listen failed");
+}
+
+static void test_connect(int sock, struct sockaddr *addr, socklen_t addrlen)
+{
+	zassert_equal(connect(sock, addr, addrlen),
+		      0,
+		      "connect failed");
+}
+
+static void test_send(int sock, const void *buf, size_t len, int flags)
+{
+	zassert_equal(send(sock, buf, len, flags),
+		      len,
+		      "send failed");
+}
+
+static void test_sendto(int sock, const void *buf, size_t len, int flags,
+			const struct sockaddr *addr, socklen_t addrlen)
+{
+	zassert_equal(sendto(sock, buf, len, flags, addr, addrlen),
+		      len,
+		      "send failed");
+}
+
+static int test_accept(int sock)
+{
+	int new_sock;
+	struct sockaddr addr;
+	socklen_t addrlen = sizeof(addr);
+
+	new_sock = accept(sock, &addr, &addrlen);
+	zassert_true(new_sock >= 0, "accept failed");
+	return new_sock;
+}
+
+static void test_recv(int sock)
+{
+	ssize_t recved = 0;
+	char rx_buf[30] = {0};
+
+	recved = recv(sock, rx_buf, sizeof(rx_buf), 0);
+	zassert_equal(recved,
+		      strlen(TEST_STR_SMALL),
+		      "unexpected received bytes");
+	zassert_equal(strncmp(rx_buf, TEST_STR_SMALL, strlen(TEST_STR_SMALL)),
+		      0,
+		      "unexpected data");
+}
+
+static void test_recvfrom(int sock,
+			  struct sockaddr *addr,
+			  socklen_t *addrlen)
+{
+	ssize_t recved = 0;
+	char rx_buf[30] = {0};
+
+	recved = recvfrom(sock,
+			  rx_buf,
+			  sizeof(rx_buf),
+			  0,
+			  addr,
+			  addrlen);
+	zassert_equal(recved,
+		      strlen(TEST_STR_SMALL),
+		      "unexpected received bytes");
+	zassert_equal(strncmp(rx_buf, TEST_STR_SMALL, strlen(TEST_STR_SMALL)),
+		      0,
+		      "unexpected data");
+}
+
+static void test_close(int sock)
+{
+	zassert_equal(close(sock),
+		      0,
+		      "close failed");
+}
+
+void test_v4_send_recv(void)
+{
+	/* Test if send() and recv() work on a ipv4 stream socket. */
+	int c_sock;
+	int s_sock;
+	int new_sock;
+	struct sockaddr_in c_saddr;
+	struct sockaddr_in s_saddr;
+
+	prepare_sock_v4(CONFIG_NET_APP_MY_IPV4_ADDR,
+			ANY_PORT,
+			&c_sock,
+			&c_saddr);
+
+	prepare_sock_v4(CONFIG_NET_APP_MY_IPV4_ADDR,
+			SERVER_PORT,
+			&s_sock,
+			&s_saddr);
+
+	test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_listen(s_sock);
+
+	test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_send(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0);
+
+	new_sock = test_accept(s_sock);
+	test_recv(new_sock);
+
+	test_close(new_sock);
+	test_close(c_sock);
+	test_close(s_sock);
+
+	k_sleep(TCP_TEARDOWN_TIMEOUT);
+}
+
+void test_v6_send_recv(void)
+{
+	/* Test if send() and recv() work on a ipv6 stream socket. */
+	int c_sock;
+	int s_sock;
+	int new_sock;
+	struct sockaddr_in6 c_saddr;
+	struct sockaddr_in6 s_saddr;
+
+	prepare_sock_v6(CONFIG_NET_APP_MY_IPV6_ADDR,
+			ANY_PORT,
+			&c_sock,
+			&c_saddr);
+
+	prepare_sock_v6(CONFIG_NET_APP_MY_IPV6_ADDR,
+			SERVER_PORT,
+			&s_sock,
+			&s_saddr);
+
+	test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_listen(s_sock);
+
+	test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_send(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0);
+
+	new_sock = test_accept(s_sock);
+	test_recv(new_sock);
+
+	test_close(new_sock);
+	test_close(s_sock);
+	test_close(c_sock);
+
+	k_sleep(TCP_TEARDOWN_TIMEOUT);
+}
+
+void test_v4_sendto_recvfrom(void)
+{
+	int c_sock;
+	int s_sock;
+	int new_sock;
+	struct sockaddr_in c_saddr;
+	struct sockaddr_in s_saddr;
+	struct sockaddr_in addr;
+	socklen_t addrlen = sizeof(addr);
+
+	prepare_sock_v4(CONFIG_NET_APP_MY_IPV4_ADDR,
+			ANY_PORT,
+			&c_sock,
+			&c_saddr);
+
+	prepare_sock_v4(CONFIG_NET_APP_MY_IPV4_ADDR,
+			SERVER_PORT,
+			&s_sock,
+			&s_saddr);
+
+	test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_listen(s_sock);
+
+	test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
+		    (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+
+	new_sock = test_accept(s_sock);
+	test_recvfrom(new_sock, (struct sockaddr *)&addr, &addrlen);
+
+	test_close(new_sock);
+	test_close(s_sock);
+	test_close(c_sock);
+
+	k_sleep(TCP_TEARDOWN_TIMEOUT);
+}
+
+void test_v6_sendto_recvfrom(void)
+{
+	int c_sock;
+	int s_sock;
+	int new_sock;
+	struct sockaddr_in6 c_saddr;
+	struct sockaddr_in6 s_saddr;
+	struct sockaddr_in6 addr;
+	socklen_t addrlen = sizeof(addr);
+
+	prepare_sock_v6(CONFIG_NET_APP_MY_IPV6_ADDR,
+			ANY_PORT,
+			&c_sock,
+			&c_saddr);
+
+	prepare_sock_v6(CONFIG_NET_APP_MY_IPV6_ADDR,
+			SERVER_PORT,
+			&s_sock,
+			&s_saddr);
+
+	test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_listen(s_sock);
+
+	test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
+		    (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+
+	new_sock = test_accept(s_sock);
+	test_recvfrom(new_sock, (struct sockaddr *)&addr, &addrlen);
+
+	test_close(new_sock);
+	test_close(s_sock);
+	test_close(c_sock);
+
+	k_sleep(TCP_TEARDOWN_TIMEOUT);
+}
+
+void test_v4_sendto_recvfrom_null_dest(void)
+{
+	/* For a stream socket, sendto() should ignore NULL dest address */
+	int c_sock;
+	int s_sock;
+	int new_sock;
+	struct sockaddr_in c_saddr;
+	struct sockaddr_in s_saddr;
+
+	prepare_sock_v4(CONFIG_NET_APP_MY_IPV4_ADDR,
+			ANY_PORT,
+			&c_sock,
+			&c_saddr);
+
+	prepare_sock_v4(CONFIG_NET_APP_MY_IPV4_ADDR,
+			SERVER_PORT,
+			&s_sock,
+			&s_saddr);
+
+	test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_listen(s_sock);
+
+	test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
+		    (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+
+	new_sock = test_accept(s_sock);
+	test_recvfrom(new_sock, NULL, NULL);
+
+	test_close(new_sock);
+	test_close(s_sock);
+	test_close(c_sock);
+
+	k_sleep(TCP_TEARDOWN_TIMEOUT);
+}
+
+void test_v6_sendto_recvfrom_null_dest(void)
+{
+	/* For a stream socket, sendto() should ignore NULL dest address */
+	int c_sock;
+	int s_sock;
+	int new_sock;
+	struct sockaddr_in6 c_saddr;
+	struct sockaddr_in6 s_saddr;
+
+	prepare_sock_v6(CONFIG_NET_APP_MY_IPV6_ADDR,
+			ANY_PORT,
+			&c_sock,
+			&c_saddr);
+
+	prepare_sock_v6(CONFIG_NET_APP_MY_IPV6_ADDR,
+			SERVER_PORT,
+			&s_sock,
+			&s_saddr);
+
+	test_bind(s_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_listen(s_sock);
+
+	test_connect(c_sock, (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+	test_sendto(c_sock, TEST_STR_SMALL, strlen(TEST_STR_SMALL), 0,
+		    (struct sockaddr *)&s_saddr, sizeof(s_saddr));
+
+	new_sock = test_accept(s_sock);
+	test_recvfrom(new_sock, NULL, NULL);
+
+	test_close(new_sock);
+	test_close(s_sock);
+	test_close(c_sock);
+
+	k_sleep(TCP_TEARDOWN_TIMEOUT);
+}
+
+void test_main(void)
+{
+	ztest_test_suite(socket_tcp,
+			 ztest_unit_test(test_v4_send_recv),
+			 ztest_unit_test(test_v6_send_recv),
+			 ztest_unit_test(test_v4_sendto_recvfrom),
+			 ztest_unit_test(test_v6_sendto_recvfrom),
+			 ztest_unit_test(test_v4_sendto_recvfrom_null_dest),
+			 ztest_unit_test(test_v6_sendto_recvfrom_null_dest));
+
+	ztest_run_test_suite(socket_tcp);
+}
diff --git a/tests/net/socket/tcp/testcase.yaml b/tests/net/socket/tcp/testcase.yaml
new file mode 100644
index 0000000..e8b360d
--- /dev/null
+++ b/tests/net/socket/tcp/testcase.yaml
@@ -0,0 +1,5 @@
+tests:
+  - test:
+      build_only: true
+      min_ram: 32
+      tags: net