diff options
| author | Garrett D'Amore <garrett@damore.org> | 2019-12-21 10:20:55 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2019-12-24 00:34:29 -0800 |
| commit | 3f7561417bec08226bcfeb107d94be0dbf71b09e (patch) | |
| tree | 409901d7929df5eeb7295ab971b34c2e1040f507 /tests/testutil.c | |
| parent | 9e7a4aff25139703bbc375b6dda263d6d42341a8 (diff) | |
| download | nng-3f7561417bec08226bcfeb107d94be0dbf71b09e.tar.gz nng-3f7561417bec08226bcfeb107d94be0dbf71b09e.tar.bz2 nng-3f7561417bec08226bcfeb107d94be0dbf71b09e.zip | |
fixes #1032 Figure out Darwin bustedness
fixes #1035 Convey is awkward -- consider acutest.h
This represents a rather large effort towards cleaning up our
testing and optional configuration infrastructure.
A separate test library is built by default, which is static, and
includes some useful utilities design to make it easier to write
shorter and more robust (not timing dependent) tests. This also means
that we can cover pretty nearly all the tests (protocols etc.) in
every case, even if the shipped image will be minimized.
Subsystems which are optional can now use a few new macros to configure
what they need see nng_sources_if, nng_headers_if, and nng_defines_if.
This goes a long way to making the distributed CMakefiles a lot simpler.
Additionally, tests for different parts of the tree can now be located
outside of the tests/ tree, so that they can be placed next to the code
that they are testing.
Beyond the enabling work, the work has only begun, but these changes
have resolved the most often failing tests for Darwin in the cloud.
Diffstat (limited to 'tests/testutil.c')
| -rw-r--r-- | tests/testutil.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/tests/testutil.c b/tests/testutil.c new file mode 100644 index 00000000..77b985a3 --- /dev/null +++ b/tests/testutil.c @@ -0,0 +1,310 @@ +// +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2018 Capitar IT Group BV <info@capitar.com> +// +// 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. +// + +#ifdef _WIN32 + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include <windows.h> +#include <winsock2.h> +// order counts +#include <mswsock.h> +#define poll WSAPoll +#include <io.h> +#else +#include <fcntl.h> +#include <poll.h> +#include <stdint.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#if !defined(_WIN32) && !defined(CLOCK_MONOTONIC) +#include <poll.h> +#endif + +#include "testutil.h" + +#include <nng/supplemental/util/platform.h> + +uint64_t +testutil_clock(void) +{ +#ifdef _WIN32 + return (GetTickCount64()); +#elif defined(CLOCK_MONTONIC) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint64_t val; + + val = ts.tv_sec; + val *= 1000; + val += ts.tv_nsec / 1000000; + return (val); +#else + static time_t epoch; + struct timeval tv; + + if (epoch == 0) { + epoch = time(NULL); + } + gettimeofday(&tv, NULL); + + if (tv.tv_sec < epoch) { + // Broken clock. + // This will force all other timing tests to fail + return (0); + } + tv.tv_sec -= epoch; + return ( + ((uint64_t)(tv.tv_sec) * 1000) + (uint64_t)(tv.tv_usec / 1000)); +#endif + +#ifdef _WIN32 +#else +#include <fcntl.h> +#include <unistd.h> +#endif +} + +bool +testutil_pollfd(int fd) +{ +#ifdef _WIN32 + struct pollfd pfd; + pfd.fd = (SOCKET) fd; + pfd.events = POLLRDNORM; + pfd.revents = 0; + + switch (WSAPoll(&pfd, 1, 0)) { + case 0: + return (false); + case 1: + return (true); + } +#else + struct pollfd pfd; + + pfd.fd = fd; + pfd.events = POLLRDNORM; + pfd.revents = 0; + + switch (poll(&pfd, 1, 0)) { + case 0: + return (false); + case 1: + return (true); + } +#endif + return (false); +} + +uint16_t +testutil_htons(uint16_t in) +{ +#ifdef NNG_LITTLE_ENDIAN + in = ((in >> 8u) & 0xffu) | ((in & 0xffu) << 8u); +#endif + return (in); +} + +// testutil_next_port returns a "next" allocation port. +// Ports are chosen by starting from a random point within a +// range (normally 38000-40000, but other good places to choose +// might be 36000-37000, 42000-43000, 45000-47000, 48000-49000. +// These are non-ephemeral ports. Successive calls to this function +// will return the next port in the range (wrapping). This works even +// across process boundaries, as the range is tracked in a file named +// by $TEST_PORT_FILE. The range of ports can be configured by using +// $TEST_PORT_RANGE (the range is specified as "lo:hi" where the actual +// port will be in the range [lo,hi). +uint16_t +testutil_next_port(void) +{ + char * pfile; + FILE * f; + uint16_t port; + uint16_t base; + uint16_t end; + char * str; +#ifdef _WIN32 + OVERLAPPED olp; + HANDLE h; +#endif + + pfile = getenv("TEST_PORT_FILE"); + if (pfile == NULL) { + pfile = ".nng_ports"; + } + if (((str = getenv("TEST_PORT_RANGE")) == NULL) || + ((sscanf(str, "%hu:%hu", &base, &end)) != 1) || + ((int) end - (int) base) < 1) { + base = 38000; + end = 40000; + } + + if (((f = fopen(pfile, "r+")) == NULL) && + ((f = fopen(pfile, "w+")) == NULL)) { + return (0); + } + (void) fseek(f, 0, SEEK_SET); + +#ifdef _WIN32 + h = (HANDLE) _get_osfhandle(_fileno(f)); + + // This contains the offset information for LockFileEx. + ZeroMemory(&olp, sizeof(olp)); + + if (LockFileEx(h, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, + &olp) == FALSE) { + fclose(f); + return (0); + } +#else + if (lockf(fileno(f), 0, F_LOCK) != 0) { + (void) fclose(f); + return (0); + } +#endif + if (fscanf(f, "%hu", &port) != 1) { + unsigned seed = (unsigned) time(NULL); + +#ifdef _WIN32 + port = base + rand_s(&seed) % (end - base); +#else + port = base + rand_r(&seed) % (end - base); +#endif + } + port++; + if ((port < base) || (port >= (base + end))) { + port = base; + } + +#ifdef _WIN32 + fseek(f, 0, SEEK_SET); + SetEndOfFile(h); + (void) fprintf(f, "%u", port); + ZeroMemory(&olp, sizeof(olp)); + (void) UnlockFileEx(h, 0, MAXDWORD, MAXDWORD, &olp); +#else + fseek(f, 0, SEEK_SET); + ftruncate(fileno(f), 0); + + (void) fprintf(f, "%u", port); + (void) lockf(fileno(f), 0, F_ULOCK); + +#endif + (void) fclose(f); + return (port); +} + +void +testutil_sleep(int msec) +{ +#ifdef _WIN32 + Sleep(msec); +#elif defined(CLOCK_MONOTONIC) + struct timespec ts; + + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + + // Do this in a loop, so that interrupts don't actually wake us. + while (ts.tv_sec || ts.tv_nsec) { + if (nanosleep(&ts, &ts) == 0) { + break; + } + } +#else + poll(NULL, 0, msec); +#endif +} + +struct marriage_notice { + nng_mtx *mx; + nng_cv * cv; + int s1; + int s2; + int cnt1; + int cnt2; +}; + +static void +married(nng_pipe p, nng_pipe_ev ev, void *arg) +{ + struct marriage_notice *notice = arg; + (void) p; + (void) ev; + + nng_mtx_lock(notice->mx); + if (nng_socket_id(nng_pipe_socket(p)) == notice->s1) { + notice->cnt1++; + } else if (nng_socket_id(nng_pipe_socket(p)) == notice->s2) { + notice->cnt2++; + } + nng_cv_wake(notice->cv); + nng_mtx_unlock(notice->mx); +} + +int +testutil_marry(nng_socket s1, nng_socket s2) +{ + struct marriage_notice note; + nng_time timeout; + int rv; + char addr[32]; + + (void) snprintf(addr, sizeof(addr), "inproc://marry%04x%04x%04x%04x", + nng_random(), nng_random(), nng_random(), nng_random()); + + note.cnt1 = 0; + note.cnt2 = 0; + note.s1 = nng_socket_id(s1); + note.s2 = nng_socket_id(s2); + timeout = nng_clock() + 1000; // 1 second + + if (((rv = nng_mtx_alloc(¬e.mx)) != 0) || + ((rv = nng_cv_alloc(¬e.cv, note.mx)) != 0) || + ((rv = nng_pipe_notify( + s1, NNG_PIPE_EV_ADD_POST, married, ¬e)) != 0) || + ((rv = nng_pipe_notify( + s2, NNG_PIPE_EV_ADD_POST, married, ¬e)) != 0) || + ((rv = nng_listen(s1, addr, NULL, 0)) != 0) || + ((rv = nng_dial(s2, addr, NULL, 0)) != 0)) { + goto done; + } + + nng_mtx_lock(note.mx); + while ((note.cnt1 == 0) || (note.cnt2 == 0)) { + if ((rv = nng_cv_until(note.cv, timeout)) != 0) { + break; + } + } + nng_mtx_unlock(note.mx); + +done: + nng_pipe_notify(s1, NNG_PIPE_EV_ADD_POST, NULL, NULL); + nng_pipe_notify(s2, NNG_PIPE_EV_ADD_POST, NULL, NULL); + if (note.cv != NULL) { + nng_cv_free(note.cv); + } + if (note.mx != NULL) { + nng_mtx_free(note.mx); + } + return (rv); +}
\ No newline at end of file |
