tests: Add ztest tests for coap client
Add tests for coap client and stubs for isolating the tests.
Signed-off-by: Jarno Lämsä <jarno.lamsa@nordicsemi.no>
diff --git a/tests/net/lib/coap_client/CMakeLists.txt b/tests/net/lib/coap_client/CMakeLists.txt
new file mode 100644
index 0000000..60506c2
--- /dev/null
+++ b/tests/net/lib/coap_client/CMakeLists.txt
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: Apache-2.0
+
+cmake_minimum_required(VERSION 3.20.0)
+find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
+project(coap_client_test)
+
+set(APP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
+
+# Add test sources
+target_sources(app PRIVATE ${APP_SRC_DIR}/main.c)
+target_sources(app PRIVATE ${APP_SRC_DIR}/stubs.c)
+target_sources(app PRIVATE ${ZEPHYR_BASE}/subsys/net/lib/coap/coap_client.c)
+target_sources(app PRIVATE ${ZEPHYR_BASE}/subsys/net/lib/coap/coap.c)
+
+# Add includes directories
+target_include_directories(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
+target_include_directories(app PRIVATE ${ZEPHYR_BASE}/include/)
+
+add_compile_definitions(CONFIG_NET_SOCKETS_POLL_MAX=3)
+add_compile_definitions(CONFIG_COAP_CLIENT=y)
+add_compile_definitions(CONFIG_COAP_CLIENT_BLOCK_SIZE=256)
+add_compile_definitions(CONFIG_COAP_CLIENT_MESSAGE_SIZE=256)
+add_compile_definitions(CONFIG_COAP_CLIENT_MESSAGE_HEADER_SIZE=48)
+add_compile_definitions(CONFIG_COAP_CLIENT_STACK_SIZE=1024)
+add_compile_definitions(CONFIG_COAP_CLIENT_THREAD_PRIORITY=10)
+add_compile_definitions(CONFIG_COAP_LOG_LEVEL=4)
+add_compile_definitions(CONFIG_NET_SOCKETS_POSIX_NAMES=y)
+add_compile_definitions(CONFIG_COAP_INIT_ACK_TIMEOUT_MS=2000)
diff --git a/tests/net/lib/coap_client/prj.conf b/tests/net/lib/coap_client/prj.conf
new file mode 100644
index 0000000..d185b3e
--- /dev/null
+++ b/tests/net/lib/coap_client/prj.conf
@@ -0,0 +1,4 @@
+#Testing
+CONFIG_ZTEST=y
+CONFIG_ZTEST_NEW_API=y
+CONFIG_ZTEST_STACK_SIZE=4096
diff --git a/tests/net/lib/coap_client/src/main.c b/tests/net/lib/coap_client/src/main.c
new file mode 100644
index 0000000..8cfc70a
--- /dev/null
+++ b/tests/net/lib/coap_client/src/main.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2023 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/fff.h>
+#include <zephyr/logging/log.h>
+#include <zephyr/ztest.h>
+
+#include "stubs.h"
+
+LOG_MODULE_REGISTER(coap_client_test);
+
+DEFINE_FFF_GLOBALS;
+#define FFF_FAKES_LIST(FAKE)
+
+static uint8_t last_response_code;
+static const char *test_path = "test";
+static uint16_t last_message_id;
+
+static struct coap_client client;
+
+static char *short_payload = "testing";
+static char *long_payload = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
+ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
+ "enim ad minim veniam, quis nostrud exercitation ullamco laboris "
+ "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor "
+ "in reprehenderit in voluptate velit esse cillum dolore eu fugiat"
+ " nulla pariatur. Excepteur sint occaecat cupidatat non proident,"
+ " sunt in culpa qui officia deserunt mollit anim id est laborum.";
+
+static ssize_t z_impl_zsock_recvfrom_custom_fake(int sock, void *buf, size_t max_len, int flags,
+ struct sockaddr *src_addr, socklen_t *addrlen)
+{
+ LOG_INF("Recvfrom");
+ static uint8_t ack_data[] = {
+ 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ ack_data[2] = (uint8_t) last_message_id >> 8;
+ ack_data[3] = (uint8_t) last_message_id;
+
+ memcpy(buf, ack_data, sizeof(ack_data));
+
+ return sizeof(ack_data);
+}
+
+static ssize_t z_impl_zsock_sendto_custom_fake(int sock, void *buf, size_t len,
+ int flags, const struct sockaddr *dest_addr,
+ socklen_t addrlen)
+{
+ LOG_INF("Sendto");
+ last_message_id = 0;
+ last_message_id |= ((uint8_t *) buf)[2] << 8;
+ last_message_id |= ((uint8_t *) buf)[3];
+
+ last_response_code = ((uint8_t *) buf)[1];
+
+ LOG_INF("Latest message ID: %d", last_message_id);
+ return 1;
+}
+
+static ssize_t z_impl_zsock_recvfrom_custom_fake_response(int sock, void *buf, size_t max_len,
+ int flags, struct sockaddr *src_addr,
+ socklen_t *addrlen)
+{
+ static uint8_t ack_data[] = {
+ 0x48, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ ack_data[2] = (uint8_t) last_message_id >> 8;
+ ack_data[3] = (uint8_t) last_message_id;
+
+ memcpy(buf, ack_data, sizeof(ack_data));
+
+ return sizeof(ack_data);
+}
+
+static ssize_t z_impl_zsock_recvfrom_custom_fake_empty_ack(int sock, void *buf, size_t max_len,
+ int flags, struct sockaddr *src_addr,
+ socklen_t *addrlen)
+{
+ static uint8_t ack_data[] = {
+ 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ ack_data[2] = (uint8_t) last_message_id >> 8;
+ ack_data[3] = (uint8_t) last_message_id;
+
+ memcpy(buf, ack_data, sizeof(ack_data));
+
+ z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_response;
+
+ return sizeof(ack_data);
+}
+
+static ssize_t z_impl_zsock_recvfrom_custom_fake_unmatching(int sock, void *buf, size_t max_len,
+ int flags, struct sockaddr *src_addr,
+ socklen_t *addrlen)
+{
+ static uint8_t ack_data[] = {
+ 0x68, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+ };
+
+ ack_data[2] = (uint8_t) last_message_id >> 8;
+ ack_data[3] = (uint8_t) last_message_id;
+
+ memcpy(buf, ack_data, sizeof(ack_data));
+
+ return sizeof(ack_data);
+}
+
+static void *suite_setup(void)
+{
+ coap_client_init(&client, NULL);
+
+ return NULL;
+}
+
+static void test_setup(void *data)
+{
+ /* Register resets */
+ DO_FOREACH_FAKE(RESET_FAKE);
+ /* reset common FFF internal structures */
+ FFF_RESET_HISTORY();
+
+ z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake;
+ z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_custom_fake;
+}
+
+void coap_callback(int16_t code, size_t offset, const uint8_t *payload, size_t len, bool last_block,
+ void *user_data)
+{
+ LOG_INF("CoAP response callback, %d", code);
+ last_response_code = code;
+}
+
+ZTEST_SUITE(coap_client, NULL, suite_setup, test_setup, NULL, NULL);
+
+ZTEST(coap_client, test_get_request)
+{
+ int ret = 0;
+ struct sockaddr address;
+ struct coap_client_request client_request = {
+ .method = COAP_METHOD_GET,
+ .confirmable = true,
+ .path = test_path,
+ .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
+ .cb = coap_callback,
+ .payload = NULL,
+ .len = 0
+ };
+
+ client_request.payload = short_payload;
+ client_request.len = strlen(short_payload);
+
+ k_sleep(K_MSEC(1));
+
+ LOG_INF("Send request");
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+ zassert_true(ret >= 0, "Sending request failed, %d", ret);
+ set_socket_events(ZSOCK_POLLIN);
+
+ k_sleep(K_MSEC(5));
+ k_sleep(K_MSEC(1000));
+ zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
+}
+
+ZTEST(coap_client, test_get_no_path)
+{
+ int ret = 0;
+ struct sockaddr address;
+ struct coap_client_request client_request = {
+ .method = COAP_METHOD_GET,
+ .confirmable = true,
+ .path = NULL,
+ .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
+ .cb = coap_callback,
+ .payload = NULL,
+ .len = 0
+ };
+
+ client_request.payload = short_payload;
+ client_request.len = strlen(short_payload);
+
+ k_sleep(K_MSEC(1));
+
+ LOG_INF("Send request");
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+
+ zassert_equal(ret, -EINVAL, "Get request without path");
+}
+
+ZTEST(coap_client, test_send_large_data)
+{
+ int ret = 0;
+ struct sockaddr address;
+ struct coap_client_request client_request = {
+ .method = COAP_METHOD_GET,
+ .confirmable = true,
+ .path = test_path,
+ .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
+ .cb = coap_callback,
+ .payload = NULL,
+ .len = 0
+ };
+
+ client_request.payload = long_payload;
+ client_request.len = strlen(long_payload);
+
+ k_sleep(K_MSEC(1));
+
+ LOG_INF("Send request");
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+ zassert_true(ret >= 0, "Sending request failed, %d", ret);
+ set_socket_events(ZSOCK_POLLIN);
+
+ k_sleep(K_MSEC(5));
+ k_sleep(K_MSEC(1000));
+ zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
+}
+
+ZTEST(coap_client, test_no_response)
+{
+ int ret = 0;
+ struct sockaddr address;
+ struct coap_client_request client_request = {
+ .method = COAP_METHOD_GET,
+ .confirmable = true,
+ .path = test_path,
+ .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
+ .cb = coap_callback,
+ .payload = NULL,
+ .len = 0
+ };
+
+ client_request.payload = short_payload;
+ client_request.len = strlen(short_payload);
+
+ k_sleep(K_MSEC(1));
+
+ LOG_INF("Send request");
+ clear_socket_events();
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+
+ zassert_true(ret >= 0, "Sending request failed, %d", ret);
+ k_sleep(K_MSEC(1000));
+}
+
+ZTEST(coap_client, test_separate_response)
+{
+ int ret = 0;
+ struct sockaddr address;
+ struct coap_client_request client_request = {
+ .method = COAP_METHOD_GET,
+ .confirmable = true,
+ .path = test_path,
+ .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
+ .cb = coap_callback,
+ .payload = NULL,
+ .len = 0
+ };
+
+ client_request.payload = short_payload;
+ client_request.len = strlen(short_payload);
+
+ z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_empty_ack;
+
+ k_sleep(K_MSEC(1));
+
+ LOG_INF("Send request");
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+ zassert_true(ret >= 0, "Sending request failed, %d", ret);
+ set_socket_events(ZSOCK_POLLIN);
+
+ k_sleep(K_MSEC(5));
+ k_sleep(K_MSEC(1000));
+
+ k_sleep(K_MSEC(1000));
+
+ zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
+}
+
+ZTEST(coap_client, test_multiple_requests)
+{
+ int ret = 0;
+ struct sockaddr address;
+ struct coap_client_request client_request = {
+ .method = COAP_METHOD_GET,
+ .confirmable = true,
+ .path = test_path,
+ .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
+ .cb = coap_callback,
+ .payload = NULL,
+ .len = 0
+ };
+
+ client_request.payload = short_payload;
+ client_request.len = strlen(short_payload);
+
+ k_sleep(K_MSEC(1));
+ set_socket_events(ZSOCK_POLLIN);
+
+ LOG_INF("Send request");
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+ zassert_true(ret >= 0, "Sending request failed, %d", ret);
+
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+ zassert_equal(ret, -EAGAIN, "Shouldn't be able to send 2 requests at same time");
+
+ k_sleep(K_MSEC(5));
+ k_sleep(K_MSEC(1000));
+ zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
+
+ ret = coap_client_req(&client, 0, &address, &client_request, -1);
+ zassert_true(ret >= 0, "Sending request failed, %d", ret);
+
+ k_sleep(K_MSEC(5));
+ k_sleep(K_MSEC(1000));
+ zassert_equal(last_response_code, COAP_RESPONSE_CODE_OK, "Unexpected response");
+}
+
+ZTEST(coap_client, test_unmatching_tokens)
+{
+ int ret = 0;
+ struct sockaddr address;
+ struct coap_client_request client_request = {
+ .method = COAP_METHOD_GET,
+ .confirmable = true,
+ .path = test_path,
+ .fmt = COAP_CONTENT_FORMAT_TEXT_PLAIN,
+ .cb = coap_callback,
+ .payload = NULL,
+ .len = 0
+ };
+
+ client_request.payload = short_payload;
+ client_request.len = strlen(short_payload);
+
+ z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_custom_fake_unmatching;
+
+ LOG_INF("Send request");
+ ret = coap_client_req(&client, 0, &address, &client_request, 0);
+ zassert_true(ret >= 0, "Sending request failed, %d", ret);
+ set_socket_events(ZSOCK_POLLIN);
+
+ k_sleep(K_MSEC(1));
+ k_sleep(K_MSEC(1));
+ clear_socket_events();
+ zassert_equal(last_response_code, COAP_RESPONSE_CODE_NOT_FOUND, "Unexpected response %d",
+ last_response_code);
+ k_sleep(K_MSEC(1));
+}
diff --git a/tests/net/lib/coap_client/src/stubs.c b/tests/net/lib/coap_client/src/stubs.c
new file mode 100644
index 0000000..13effd8
--- /dev/null
+++ b/tests/net/lib/coap_client/src/stubs.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <zephyr/logging/log.h>
+#include <stubs.h>
+
+LOG_MODULE_DECLARE(coap_client_test);
+
+DEFINE_FAKE_VALUE_FUNC(uint32_t, z_impl_sys_rand32_get);
+DEFINE_FAKE_VOID_FUNC(z_impl_sys_rand_get, void *, size_t);
+DEFINE_FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_recvfrom, int, void *, size_t, int, struct sockaddr *,
+ socklen_t *);
+DEFINE_FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_sendto, int, void*, size_t, int,
+ const struct sockaddr *, socklen_t);
+
+struct zsock_pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+static short my_events;
+
+void set_socket_events(short events)
+{
+ my_events |= events;
+}
+
+void clear_socket_events(void)
+{
+ my_events = 0;
+}
+
+int z_impl_zsock_socket(int family, int type, int proto)
+{
+ return 0;
+}
+
+int z_impl_zsock_poll(struct zsock_pollfd *fds, int nfds, int poll_timeout)
+{
+ LOG_INF("Polling, events %d", my_events);
+ k_sleep(K_MSEC(10));
+ fds->revents = my_events;
+ if (my_events) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
diff --git a/tests/net/lib/coap_client/src/stubs.h b/tests/net/lib/coap_client/src/stubs.h
new file mode 100644
index 0000000..eb340d9
--- /dev/null
+++ b/tests/net/lib/coap_client/src/stubs.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2023 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef STUBS_H
+#define STUBS_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <zephyr/fff.h>
+#include <zephyr/ztest.h>
+
+#include <zephyr/net/coap_client.h>
+
+#define ZSOCK_POLLIN 1
+#define ZSOCK_POLLOUT 4
+
+void set_socket_events(short events);
+void clear_socket_events(void);
+
+DECLARE_FAKE_VALUE_FUNC(uint32_t, z_impl_sys_rand32_get);
+DECLARE_FAKE_VOID_FUNC(z_impl_sys_rand_get, void *, size_t);
+DECLARE_FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_recvfrom, int, void *, size_t, int, struct sockaddr *,
+ socklen_t *);
+DECLARE_FAKE_VALUE_FUNC(ssize_t, z_impl_zsock_sendto, int, void*, size_t, int,
+ const struct sockaddr *, socklen_t);
+
+#define DO_FOREACH_FAKE(FUNC) \
+ do { \
+ FUNC(z_impl_sys_rand32_get) \
+ FUNC(z_impl_sys_rand_get) \
+ FUNC(z_impl_zsock_recvfrom) \
+ FUNC(z_impl_zsock_sendto) \
+ } while (0)
+
+#endif /* STUBS_H */
diff --git a/tests/net/lib/coap_client/testcase.yaml b/tests/net/lib/coap_client/testcase.yaml
new file mode 100644
index 0000000..923e138
--- /dev/null
+++ b/tests/net/lib/coap_client/testcase.yaml
@@ -0,0 +1,6 @@
+common:
+ depends_on: netif
+tests:
+ net.coap.client:
+ platform_allow: native_posix
+ tags: coap net