diff options
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 |
