From d1218d7309475193b53911667911c4f59a1a7752 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sat, 21 Nov 2020 22:11:21 -0800 Subject: New NUTS test framework (NNG Unit Test Support). This is based on testutil/acutest, but is cleaner and fixes some short-comings. We will be adding more support for additional common paradigms to better facilitate transport tests. While here we added some more test cases, and fixed a possible symbol collision in the the stats framework (due to Linux use of a macro definition of "si_value" in a standard OS header). Test coverage may regress slightly as we are no longer using some of the legacy APIs. --- src/testing/nuts.h | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 src/testing/nuts.h (limited to 'src/testing/nuts.h') diff --git a/src/testing/nuts.h b/src/testing/nuts.h new file mode 100644 index 00000000..2ed8744c --- /dev/null +++ b/src/testing/nuts.h @@ -0,0 +1,206 @@ +// +// Copyright 2020 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. +// + +// NUTS - NNG Unit Test Support +// +// This is the NNG testing support library. It is used in the NNG +// project to support the various unit tests. It should not be used +// in other projects, and no guarantees are made about interface +// stability, etc. + +#ifndef NNG_TESTING_NUTS_H +#define NNG_TESTING_NUTS_H + +#include "acutest.h" + +#include +#include +#include + +// The following headers are provided for test code convenience. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// nuts_clock returns the current time in milliseconds. +// The reference clock may be any point in the past (typically since +// the program started running.) +extern uint64_t nuts_clock(void); + +// nuts_poll_fd tests if the given file descriptor polls as readable. +extern bool nuts_poll_fd(int); + +// nuts_be16 converts native and big-endian words. +extern uint16_t nuts_be16(uint16_t); + +// nuts_be32 converts native and big-endian double-words. +extern uint32_t nuts_be32(uint32_t); + +// nuts_sleep sleeps the specified number of milliseconds. +extern void nuts_sleep(int); + +// nuts_next_port returns a new port number (presumably unique) +extern uint16_t nuts_next_port(void); + +// nuts_scratch_addr makes a scratch address for the given scheme. +// The address buffer must be supplied, and the size should be at least +// 64 bytes to ensure no truncation occurs. +extern void nuts_scratch_addr(const char *, size_t, char *); + +// nuts_marry connects two sockets using inproc. It uses socket +// pipe hooks to ensure that it does not return before both sockets +// are fully connected. +extern int nuts_marry(nng_socket, nng_socket); + +// nuts_marry_ex is like nuts_marry, but returns the pipes that +// were connected, and includes an optional URL. The pipe pointers and the +// URL may be NULL if not needed. If a port number is part of the URL +// and is zero (i.e. if the URL contains :0) then listen is done first, +// and the actual bound port will be used for the client. +extern int nuts_marry_ex( + nng_socket, nng_socket, const char *, nng_pipe *, nng_pipe *); + +// nuts_stream_send_start and nuts_stream_recv_start are used +// to initiate transfers asynchronously. They return a token which can +// be used with nuts_stream_wait, which will return the result of +// the operation (0 on success, an NNG error number otherwise.) +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 *); + +// 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. +extern const char *nuts_server_key; +extern const char *nuts_server_crt; +extern const char *nuts_client_key; +extern const char *nuts_client_crt; + +// NUTS_SUCCESS tests for NNG success. It reports the failure if it +// did not. +#define NUTS_PASS(cond) \ + do { \ + int result_ = (cond); \ + TEST_CHECK_(result_ == 0, "%s succeeds", #cond); \ + TEST_MSG("%s: expected success, got %s (%d)", #cond, \ + nng_strerror(result_), result_); \ + } while (0) + +// NUTS_ERROR tests for a specific NNG error code. +#define NUTS_FAIL(cond, expect) \ + do { \ + int result_ = (cond); \ + TEST_CHECK_(result_ == (expect), "%s fails with %s", #cond, \ + nng_strerror(expect)); \ + TEST_MSG("%s: expected %s (%d), got %s (%d)", #cond, \ + nng_strerror(expect), expect, nng_strerror(result_), \ + result_); \ + } while (0) + +#define NUTS_SEND(sock, string) \ + NUTS_PASS(nng_send(sock, string, strlen(string) + 1, 0)) + +#define NUTS_RECV(sock, string) \ + do { \ + char buf_[64]; \ + size_t sz_ = sizeof(buf_); \ + int rv_ = nng_recv(sock, &buf_, &sz_, 0); \ + TEST_CHECK_( \ + rv_ == 0, "nng_recv (%d %s)", rv_, nng_strerror(rv_)); \ + TEST_CHECK_(sz_ == strlen(string) + 1, "length %d want %d", \ + sz_, strlen(string) + 1); \ + buf_[sizeof(buf_) - 1] = '\0'; \ + TEST_CHECK_( \ + strcmp(string, buf_) == 0, "%s == %s", string, buf_); \ + } while (0) + +#define NUTS_MATCH(s1, s2) \ + do { \ + TEST_CHECK_(strcmp(s1, s2) == 0, "%s == %s", s1, s2); \ + } while (0) + +#define NUTS_NULL(x) \ + do { \ + TEST_CHECK_((x) == NULL, "%p == NULL", x); \ + } while (0) + +#define NUTS_ADDR(var, scheme) \ + do { \ + static char nuts_addr_[64]; \ + nuts_scratch_addr(scheme, sizeof(nuts_addr_), nuts_addr_); \ + (var) = nuts_addr_; \ + } while (0) + +#define NUTS_OPEN(sock) NUTS_PASS(nng_pair1_open(&(sock))) + +#define NUTS_CLOSE(sock) NUTS_PASS(nng_close(sock)) + +#define NUTS_SLEEP(ms) nuts_sleep(ms) + +#define NUTS_CLOCK(var) \ + do { \ + (var) = nuts_clock(); \ + } while (0) + +#define NUTS_BEFORE(when) \ + do { \ + uint64_t nuts_t0_ = (when); \ + uint64_t nuts_t1_ = nuts_clock(); \ + TEST_CHECK_(nuts_t1_ < nuts_t0_, \ + "time before, deadline %lld, current %lld, delta %lld", \ + (long long) nuts_t0_, (long long) nuts_t1_, \ + (long long) nuts_t0_ - (long long) nuts_t1_); \ + } while (0) + +#define NUTS_AFTER(when) \ + do { \ + uint64_t nuts_t0_ = (when); \ + uint64_t nuts_t1_ = nuts_clock(); \ + TEST_CHECK_(nuts_t1_ >= nuts_t0_, \ + "time after, deadline %lld, current %lld, delta %lld", \ + (long long) nuts_t0_, (long long) nuts_t1_, \ + (long long) nuts_t0_ - (long long) nuts_t1_); \ + } while (0) + +#define NUTS_MARRY(s1, s2) NUTS_PASS(nuts_marry(s1, s2)) +#define NUTS_MARRY_EX(s1, s2, url, p1, p2) \ + NUTS_PASS(nuts_marry_ex(s1, s2, url, p1, p2)) + +// Redefine some macros from acutest.h for consistency. +#define NUTS_TRUE TEST_CHECK +#define NUTS_ASSERT TEST_ASSERT +#define NUTS_CASE TEST_CASE +#define NUTS_MSG TEST_MSG + +#define NUTS_TESTS TEST_LIST + +#define NUTS_PROTO(x, y) (((x) << 4u) | (y)) + +#ifdef __cplusplus +}; +#endif + +#endif // NNG_TEST_NUTS_H -- cgit v1.2.3-70-g09d2