diff --git a/src/sp/transport/inproc/CMakeLists.txt b/src/sp/transport/inproc/CMakeLists.txt index b84ab01e3..2132e8d7e 100644 --- a/src/sp/transport/inproc/CMakeLists.txt +++ b/src/sp/transport/inproc/CMakeLists.txt @@ -13,3 +13,4 @@ nng_directory(inproc) nng_sources_if(NNG_TRANSPORT_INPROC inproc.c) nng_defines_if(NNG_TRANSPORT_INPROC NNG_TRANSPORT_INPROC) +nng_test_if(NNG_TRANSPORT_INPROC inproc_test) diff --git a/src/sp/transport/inproc/inproc_test.c b/src/sp/transport/inproc/inproc_test.c new file mode 100644 index 000000000..2683b1e40 --- /dev/null +++ b/src/sp/transport/inproc/inproc_test.c @@ -0,0 +1,17 @@ +// +// Copyright 2024 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include + +NUTS_DECLARE_TRAN_TESTS(inproc) + +NUTS_TESTS = { + NUTS_INSERT_TRAN_TESTS(inproc), + { NULL, NULL }, +}; diff --git a/src/testing/nuts.h b/src/testing/nuts.h index a61968b8d..a2e850a3e 100644 --- a/src/testing/nuts.h +++ b/src/testing/nuts.h @@ -106,6 +106,62 @@ extern void *nuts_stream_send_start(nng_stream *, void *, size_t); extern void *nuts_stream_recv_start(nng_stream *, void *, size_t); extern int nuts_stream_wait(void *); +// nuts_tran_ functions are implementation of tesets +// for transports, to improve code reuse. +extern void nuts_tran_conn_refused(const char *scheme); +extern void nuts_tran_duplicate_listen(const char *scheme); +extern void nuts_tran_listen_accept(const char *scheme); +extern void nuts_tran_exchange(const char *scheme); +extern void nuts_tran_pipe_id(const char *scheme); +extern void nuts_tran_huge_msg(const char *scheme, size_t size); +extern void nuts_tran_msg_props(const char *scheme, void (*check)(nng_msg *)); +extern void nuts_tran_perf(const char *scheme); + +#ifndef NUTS_TRAN_HUGE_MSG_SIZE +#define NUTS_TRAN_HUGE_MSG_SIZE (1U << 20) +#endif + +#define NUTS_DECLARE_TRAN_TESTS(scheme) \ + void test_##scheme##_conn_refused(void) \ + { \ + nuts_tran_conn_refused(#scheme); \ + } \ + void test_##scheme##_duplicate_listen(void) \ + { \ + nuts_tran_duplicate_listen(#scheme); \ + } \ + void test_##scheme##_listen_accept(void) \ + { \ + nuts_tran_listen_accept(#scheme); \ + } \ + void test_##scheme##_exchange(void) \ + { \ + nuts_tran_exchange(#scheme); \ + } \ + void test_##scheme##_pipe_id(void) \ + { \ + nuts_tran_pipe_id(#scheme); \ + } \ + void test_##scheme##_huge_msg(void) \ + { \ + nuts_tran_huge_msg(#scheme, NUTS_TRAN_HUGE_MSG_SIZE); \ + } \ + void test_##scheme##_perf(void) \ + { \ + nuts_tran_perf(#scheme); \ + } + +// clang-format off +#define NUTS_INSERT_TRAN_TESTS(scheme) \ + { #scheme " conn refused", test_##scheme##_conn_refused }, \ + { #scheme " duplicate listen", test_##scheme##_duplicate_listen }, \ + { #scheme " listen accept", test_##scheme##_listen_accept }, \ + { #scheme " exchange", test_##scheme##_exchange }, \ + { #scheme " pipe id", test_##scheme##_pipe_id }, \ + { #scheme " huge msg", test_##scheme##_huge_msg }, \ + { #scheme " perf", test_##scheme##_perf } +// clang-format on + // These are TLS certificates. The client and server are signed with the // root. The server uses CN 127.0.0.1. Other details are bogus, but // designed to prevent accidental use elsewhere. diff --git a/src/testing/util.c b/src/testing/util.c index 497598edf..4b487e2d8 100644 --- a/src/testing/util.c +++ b/src/testing/util.c @@ -214,3 +214,270 @@ nuts_logger(nng_log_level level, nng_log_facility fac, const char *msgid, } acutest_message_color_(color, "%s: %s: %s", lstr, msgid, msg); } + +void +nuts_tran_conn_refused(const char *scheme) +{ + nng_socket s = NNG_SOCKET_INITIALIZER; + nng_dialer d = NNG_DIALER_INITIALIZER; + const char *addr; + + NUTS_ADDR(addr, scheme); + NUTS_OPEN(s); + NUTS_FAIL(nng_dial(s, addr, &d, 0), NNG_ECONNREFUSED); + NUTS_TRUE(nng_dialer_id(d) < 0); + NUTS_CLOSE(s); +} + +void +nuts_tran_duplicate_listen(const char *scheme) +{ + nng_socket s = NNG_SOCKET_INITIALIZER; + nng_listener l1 = NNG_LISTENER_INITIALIZER; + nng_listener l2 = NNG_LISTENER_INITIALIZER; + const char *addr; + + NUTS_ADDR(addr, scheme); + NUTS_OPEN(s); + NUTS_PASS(nng_listen(s, addr, &l1, 0)); + NUTS_FAIL(nng_listen(s, addr, &l2, 0), NNG_EADDRINUSE); + NUTS_TRUE(nng_listener_id(l1) > 0); + NUTS_TRUE(nng_listener_id(l2) < 0); + NUTS_CLOSE(s); +} + +void +nuts_tran_listen_accept(const char *scheme) +{ + nng_socket s1 = NNG_SOCKET_INITIALIZER; + nng_socket s2 = NNG_SOCKET_INITIALIZER; + nng_listener l1 = NNG_LISTENER_INITIALIZER; + nng_dialer d1 = NNG_LISTENER_INITIALIZER; + nng_dialer d2 = NNG_LISTENER_INITIALIZER; + const char *addr; + + NUTS_ADDR(addr, scheme); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_listen(s1, addr, &l1, 0)); + NUTS_PASS(nng_dial(s2, addr, &d1, 0)); + NUTS_PASS(nng_dial(s2, addr, &d2, 0)); + NUTS_TRUE(nng_listener_id(l1) > 0); + NUTS_TRUE(nng_dialer_id(d1) > 0); + NUTS_TRUE(nng_dialer_id(d2) > 0); + NUTS_TRUE(nng_dialer_id(d1) != nng_dialer_id(d2)); + NUTS_CLOSE(s1); + NUTS_CLOSE(s2); +} + +void +nuts_tran_exchange(const char *scheme) +{ + nng_socket s1 = NNG_SOCKET_INITIALIZER; + nng_socket s2 = NNG_SOCKET_INITIALIZER; + nng_listener l1 = NNG_LISTENER_INITIALIZER; + nng_dialer d1 = NNG_LISTENER_INITIALIZER; + const char *addr; + + NUTS_ADDR(addr, scheme); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_listen(s1, addr, &l1, 0)); + NUTS_PASS(nng_dial(s2, addr, &d1, 0)); + NUTS_TRUE(nng_listener_id(l1) > 0); + NUTS_TRUE(nng_dialer_id(d1) > 0); + for (int i = 0; i < 5; i++) { + NUTS_SEND(s1, "ping"); + NUTS_RECV(s2, "ping"); + NUTS_SEND(s2, "acknowledge"); + NUTS_RECV(s1, "acknowledge"); + } + NUTS_CLOSE(s1); + NUTS_CLOSE(s2); +} + +void +nuts_tran_pipe_id(const char *scheme) +{ + nng_socket s1 = NNG_SOCKET_INITIALIZER; + nng_socket s2 = NNG_SOCKET_INITIALIZER; + nng_listener l1 = NNG_LISTENER_INITIALIZER; + nng_dialer d1 = NNG_LISTENER_INITIALIZER; + const char *addr; + nng_msg *msg; + nng_pipe p1; + nng_pipe p2; + + NUTS_ADDR(addr, scheme); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_listen(s1, addr, &l1, 0)); + NUTS_PASS(nng_dial(s2, addr, &d1, 0)); + NUTS_TRUE(nng_listener_id(l1) > 0); + NUTS_TRUE(nng_dialer_id(d1) > 0); + NUTS_SEND(s1, "ping"); + NUTS_PASS(nng_recvmsg(s2, &msg, 0)); + NUTS_MATCH(nng_msg_body(msg), "ping"); + p1 = nng_msg_get_pipe(msg); + nng_msg_free(msg); + NUTS_TRUE(nng_pipe_id(p1) > 0); + NUTS_SEND(s2, "acknowledge"); + NUTS_PASS(nng_recvmsg(s1, &msg, 0)); + NUTS_MATCH(nng_msg_body(msg), "acknowledge"); + p2 = nng_msg_get_pipe(msg); + NUTS_TRUE(nng_pipe_id(p2) > 0); + nng_msg_free(msg); + NUTS_TRUE(nng_pipe_id(p1) != nng_pipe_id(p2)); + NUTS_CLOSE(s1); + NUTS_CLOSE(s2); +} + +void +nuts_tran_huge_msg(const char *scheme, size_t size) +{ + nng_socket s1 = NNG_SOCKET_INITIALIZER; + nng_socket s2 = NNG_SOCKET_INITIALIZER; + nng_listener l1 = NNG_LISTENER_INITIALIZER; + nng_dialer d1 = NNG_LISTENER_INITIALIZER; + const char *addr; + char *buf; + nng_msg *msg; + + buf = nng_alloc(size); + + NUTS_ADDR(addr, scheme); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_listen(s1, addr, &l1, 0)); + NUTS_PASS(nng_dial(s2, addr, &d1, 0)); + NUTS_TRUE(nng_listener_id(l1) > 0); + NUTS_TRUE(nng_dialer_id(d1) > 0); + for (int i = 0; i < 5; i++) { + for (size_t j = 0; j < size; j++) { + buf[j] = nng_random() & 0xff; + } + NUTS_PASS(nng_send(s1, buf, size, 0)); + NUTS_PASS(nng_recvmsg(s2, &msg, 0)); + NUTS_TRUE(nng_msg_len(msg) == size); + NUTS_TRUE(memcmp(nng_msg_body(msg), buf, size) == 0); + nng_msg_free(msg); + NUTS_PASS(nng_send(s2, buf, size, 0)); + NUTS_PASS(nng_recvmsg(s1, &msg, 0)); + NUTS_TRUE(nng_msg_len(msg) == size); + NUTS_TRUE(memcmp(nng_msg_body(msg), buf, size) == 0); + nng_msg_free(msg); + } + NUTS_CLOSE(s1); + NUTS_CLOSE(s2); + nng_free(buf, size); +} + +void +nuts_tran_msg_props(const char *scheme, void (*check)(nng_msg *)) +{ + nng_socket s1 = NNG_SOCKET_INITIALIZER; + nng_socket s2 = NNG_SOCKET_INITIALIZER; + nng_listener l1 = NNG_LISTENER_INITIALIZER; + nng_dialer d1 = NNG_LISTENER_INITIALIZER; + const char *addr; + nng_msg *msg; + + NUTS_ADDR(addr, scheme); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_listen(s1, addr, &l1, 0)); + NUTS_PASS(nng_dial(s2, addr, &d1, 0)); + NUTS_TRUE(nng_listener_id(l1) > 0); + NUTS_TRUE(nng_dialer_id(d1) > 0); + NUTS_SEND(s1, "ping"); + NUTS_PASS(nng_recvmsg(s2, &msg, 0)); + NUTS_MATCH(nng_msg_body(msg), "ping"); + check(msg); + nng_msg_free(msg); + NUTS_CLOSE(s1); + NUTS_CLOSE(s2); +} + +void +nuts_tran_perf(const char *scheme) +{ + nng_socket s1; + nng_socket s2; + const char *addr; + nng_msg *msg; + + nuts_set_logger(NNG_LOG_NOTICE); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_ADDR(addr, scheme); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 100)); + NUTS_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, 100)); + NUTS_MARRY_EX(s1, s2, addr, NULL, NULL); + NUTS_PASS(nng_msg_alloc(&msg, 64)); + nng_log_notice(scheme, "Exchanging 64 byte messages for 10 seconds"); + nng_time now = nng_clock(); + nng_time end = now + 10000; // ten seconds + nng_duration delta; + int rv; + int num = 0; + + // count round trips for 10 seconds + while (nng_clock() < end) { + if ((rv = nng_sendmsg(s1, msg, 0)) != 0) { + NUTS_PASS(rv); + NUTS_MSG("nng_sendmsg failed"); + break; + } + if ((rv = nng_recvmsg(s2, &msg, 0)) != 0) { + NUTS_PASS(rv); + NUTS_MSG("nng_recvmsg failed"); + break; + } + num++; + } + delta = (nng_clock() - now); + nng_msg_free(msg); + + // now count the cost of the time collection + now = nng_clock(); + for (int i = 0; i < num; i++) { + end = nng_clock(); + if (end < now) { + NUTS_ASSERT(end >= now); + } + } + NUTS_ASSERT(end - now > 0); + NUTS_ASSERT(end - now < 10000); + // remove the cost of timing + delta -= (end - now); + nng_log_notice(scheme, + "Did %u roundtrips in %0.2f seconds (%0.3f msg/sec)", num, + delta / 1000.0, (float) num / (delta / 1000.0)); + nng_log_notice(scheme, "RTT %0.3f ms", (float) delta / (float) num); + nng_log_notice( + scheme, "Timing overhead %0.3f ms", (end - now) / (float) num); + NUTS_CLOSE(s1); + NUTS_CLOSE(s2); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9d976dd90..f6008c415 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -127,7 +127,6 @@ endif () add_nng_test(files 5) add_nng_test1(httpclient 60 NNG_SUPP_HTTP) add_nng_test1(httpserver 30 NNG_SUPP_HTTP) -add_nng_test(inproc 5) add_nng_test(ipcsupp 10) add_nng_test(nonblock 60) add_nng_test(scalability 20 ON) diff --git a/tests/inproc.c b/tests/inproc.c deleted file mode 100644 index 11a6f118d..000000000 --- a/tests/inproc.c +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright 2021 Staysail Systems, Inc. -// Copyright 2017 Capitar IT Group BV -// -// This software is supplied under the terms of the MIT License, a -// copy of which should be located in the distribution where this -// file was obtained (LICENSE.txt). A copy of the license may also be -// found online at https://opensource.org/licenses/MIT. -// - -#include "convey.h" -#include "trantest.h" - -#include "core/nng_impl.h" - -// Inproc tests. - -TestMain("Inproc Transport", { - trantest_test_all("inproc://TEST_%u"); -})