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 | |
| 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.
| -rw-r--r-- | .codacy.yml | 5 | ||||
| -rw-r--r-- | .github/workflows/windows.yml | 4 | ||||
| -rw-r--r-- | CMakeLists.txt | 471 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 26 | ||||
| -rw-r--r-- | src/protocol/pair1/CMakeLists.txt | 13 | ||||
| -rw-r--r-- | src/protocol/pair1/pair.c | 1 | ||||
| -rw-r--r-- | src/protocol/pair1/pair1_test.c | 607 | ||||
| -rw-r--r-- | src/protocol/reqrep0/CMakeLists.txt | 32 | ||||
| -rw-r--r-- | src/protocol/reqrep0/reqrep_test.c | 300 | ||||
| -rw-r--r-- | src/supplemental/base64/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/supplemental/base64/base64_test.c | 106 | ||||
| -rw-r--r-- | src/supplemental/sha1/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | src/supplemental/sha1/sha1_test.c (renamed from tests/sha1.c) | 61 | ||||
| -rw-r--r-- | src/transport/inproc/CMakeLists.txt | 12 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 143 | ||||
| -rw-r--r-- | tests/acutest.h | 1650 | ||||
| -rw-r--r-- | tests/aio.c | 377 | ||||
| -rw-r--r-- | tests/base64.c | 84 | ||||
| -rw-r--r-- | tests/bufsz.c | 190 | ||||
| -rw-r--r-- | tests/pair1.c | 541 | ||||
| -rw-r--r-- | tests/platform.c | 201 | ||||
| -rw-r--r-- | tests/reqrep.c | 289 | ||||
| -rw-r--r-- | tests/sock.c | 1139 | ||||
| -rw-r--r-- | tests/stubs.h | 18 | ||||
| -rw-r--r-- | tests/testutil.c | 310 | ||||
| -rw-r--r-- | tests/testutil.h | 49 |
26 files changed, 4506 insertions, 2134 deletions
diff --git a/.codacy.yml b/.codacy.yml new file mode 100644 index 00000000..cce28d36 --- /dev/null +++ b/.codacy.yml @@ -0,0 +1,5 @@ +exclude_paths: + - tests/acutest.h + - tests/convey.c + - tests/convey.h + - .circleci/**
\ No newline at end of file diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 12ff1282..03054037 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -16,5 +16,7 @@ jobs: run: cmake --build build - name: Test - run: ctest -C Debug --output-on-failure + run: | + cd build + ctest -C Debug --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c8132b8..31e2efcb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ # +# Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> # Copyright (c) 2012 Martin Sustrik All rights reserved. # Copyright (c) 2013 GoPivotal, Inc. All rights reserved. # Copyright (c) 2015-2016 Jack R. Dunaway. All rights reserved. # Copyright 2016 Franklin "Snaipe" Mathieu <franklinmathieu@gmail.com> -# Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> # Copyright 2018 Capitar IT Group BV <info@capitar.com> # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,73 +25,73 @@ # IN THE SOFTWARE. # -cmake_minimum_required (VERSION 3.1) +cmake_minimum_required(VERSION 3.1) -project (nng C) -include (CheckFunctionExists) -include (CheckSymbolExists) -include (CheckStructHasMember) -include (CheckLibraryExists) -include (CheckCSourceCompiles) -include (CheckCCompilerFlag) -include (CMakeDependentOption) -include (GNUInstallDirs) -include (TestBigEndian) -include (FindUnixCommands) +project(nng C) +include(CheckFunctionExists) +include(CheckSymbolExists) +include(CheckStructHasMember) +include(CheckLibraryExists) +include(CheckCSourceCompiles) +include(CheckCCompilerFlag) +include(CMakeDependentOption) +include(GNUInstallDirs) +include(TestBigEndian) +include(FindUnixCommands) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) if (POLICY CMP0042) # Newer cmake on MacOS should use @rpath - cmake_policy (SET CMP0042 NEW) + cmake_policy(SET CMP0042 NEW) endif () if (POLICY CMP0028) # Double colon targets are only alias or imports. - cmake_policy (SET CMP0028 NEW) + cmake_policy(SET CMP0028 NEW) endif () set(CMAKE_C_STANDARD 99) -set (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -list (FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir) if ("${isSystemDir}" STREQUAL "-1") - set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") endif ("${isSystemDir}" STREQUAL "-1") -set (NNG_DESCRIPTION "High-Performance Scalability Protocols NextGen") -set (ISSUE_REPORT_MSG "Please consider opening an issue at https://github.com/nanomsg/nng") +set(NNG_DESCRIPTION "High-Performance Scalability Protocols NextGen") +set(ISSUE_REPORT_MSG "Please consider opening an issue at https://github.com/nanomsg/nng") # Determine library versions. -set (NNG_ABI_SOVERSION 1) -set (NNG_ABI_VERSION "1.1.0") +set(NNG_ABI_SOVERSION 1) +set(NNG_ABI_VERSION "1.1.0") # Determine package version. -find_package (Git QUIET) +find_package(Git QUIET) if (GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") # Working off a git repo, using git versioning # Get version from last tag - execute_process ( - COMMAND "${GIT_EXECUTABLE}" describe --always# | sed -e "s:v::" - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" - OUTPUT_VARIABLE NNG_PACKAGE_VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --always# | sed -e "s:v::" + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + OUTPUT_VARIABLE NNG_PACKAGE_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) # If the sources have been changed locally, add -dirty to the version. - execute_process ( - COMMAND "${GIT_EXECUTABLE}" diff --quiet - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" - RESULT_VARIABLE res) + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff --quiet + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE res) if (res EQUAL 1) - set (NNG_PACKAGE_VERSION "${NNG_PACKAGE_VERSION}-dirty") - endif() + set(NNG_PACKAGE_VERSION "${NNG_PACKAGE_VERSION}-dirty") + endif () else () - set (NNG_PACKAGE_VERSION "Unknown") -endif() + set(NNG_PACKAGE_VERSION "Unknown") +endif () if ("${NNG_PACKAGE_VERSION}" MATCHES "v[0-9]") string(SUBSTRING "${NNG_PACKAGE_VERSION}" 1 -1 NNG_PACKAGE_VERSION) -endif() +endif () string(REGEX REPLACE "([0-9]+).[0-9]+.+" "\\1" NNG_VERSION_MAJOR "${NNG_PACKAGE_VERSION}") string(REGEX REPLACE "[0-9]+.([0-9]+).[0-9].+" "\\1" NNG_VERSION_MINOR "${NNG_PACKAGE_VERSION}") @@ -102,20 +102,20 @@ string(REGEX REPLACE "[0-9]+.[0-9]+.([0-9]+).*" "\\1" NNG_VERSION_PATCH "${NNG_P option(BUILD_SHARED_LIBS "Build shared library" ${BUILD_SHARED_LIBS}) if (CMAKE_CROSSCOMPILING) - set (NNG_NATIVE_BUILD OFF) -else() - set (NNG_NATIVE_BUILD ON) -endif() + set(NNG_NATIVE_BUILD OFF) +else () + set(NNG_NATIVE_BUILD ON) +endif () # We only build command line tools and tests if we are not in a # cross-compile situation. Cross-compiling users who still want to # build these must enable them explicitly. -option (NNG_TESTS "Build and run tests" ${NNG_NATIVE_BUILD}) -option (NNG_TOOLS "Build extra tools" ${NNG_NATIVE_BUILD}) -option (NNG_ENABLE_NNGCAT "Enable building nngcat utility." ${NNG_TOOLS}) -option (NNG_ENABLE_COVERAGE "Enable coverage reporting." OFF) +option(NNG_TESTS "Build and run tests" ${NNG_NATIVE_BUILD}) +option(NNG_TOOLS "Build extra tools" ${NNG_NATIVE_BUILD}) +option(NNG_ENABLE_NNGCAT "Enable building nngcat utility." ${NNG_TOOLS}) +option(NNG_ENABLE_COVERAGE "Enable coverage reporting." OFF) # Enable access to private APIs for our own use. -add_definitions (-DNNG_PRIVATE) +add_definitions(-DNNG_PRIVATE) # We can use rlimit to configure the stack size for systems # that have too small defaults. This is not used for Windows, @@ -123,288 +123,379 @@ add_definitions (-DNNG_PRIVATE) # by with a smallish stack, but application callbacks might require # larger values if using aio completion callbacks.) if (NOT WIN32) - option (NNG_SETSTACKSIZE "Use rlimit for thread stack size" OFF) + option(NNG_SETSTACKSIZE "Use rlimit for thread stack size" OFF) if (NNG_SETSTACKSIZE) add_definitions(-DNNG_SETSTACKSIZE) - endif() + endif () mark_as_advanced(NNG_SETSTACKSIZE) -endif() +endif () -option (NNG_ENABLE_TLS "Enable TLS protocol (requires mbedTLS)" OFF) +option(NNG_ENABLE_TLS "Enable TLS protocol (requires mbedTLS)" OFF) if (NNG_ENABLE_TLS) add_definitions(-DNNG_SUPP_TLS) set(NNG_SUPP_TLS ON) -endif() +endif () -option (NNG_ENABLE_STATS "Enable statistics" ON) +option(NNG_ENABLE_STATS "Enable statistics" ON) if (NNG_ENABLE_STATS) add_definitions(-DNNG_ENABLE_STATS) -endif() +endif () mark_as_advanced(NNG_ENABLE_STATS) if (NNG_RESOLV_CONCURRENCY) add_definitions(-DNNG_RESOLV_CONCURRENCY=${NNG_RESOLV_CONCURRENCY}) -endif() +endif () mark_as_advanced(NNG_RESOLV_CONCURRENCY) if (NNG_NUM_TASKQ_THREADS) add_definitions(-DNNG_NUM_TASKQ_THREADS=${NNG_NUM_TASKQ_THREADS}) -endif() +endif () mark_as_advanced(NNG_NUM_TASKQ_THREADS) # Platform checks. if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - set(NNG_WARN_FLAGS "-Wall -Wextra -fno-omit-frame-pointer") + set(NNG_WARN_FLAGS "-Wall -Wextra -fno-omit-frame-pointer") elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") set(NNG_WARN_FLAGS "-Wall -Wextra -fno-omit-frame-pointer") elseif (CMAKE_C_COMPILER_ID STREQUAL "AppleClang") set(NNG_WARN_FLAGS "-Wall -Wextra -fno-omit-frame-pointer") -endif() +endif () -include (CheckSanitizer) +include(CheckSanitizer) CheckSanitizer() if (NOT NNG_SANITIZER STREQUAL "none") - set (NNG_SANITIZER_FLAGS "-fsanitize=${NNG_SANITIZER}") -endif() + set(NNG_SANITIZER_FLAGS "-fsanitize=${NNG_SANITIZER}") +endif () if (NNG_ENABLE_COVERAGE) # NB: This only works for GCC and Clang 3.0 and newer. If your stuff # is older than that, you will need to find something newer. For # correct reporting, we always turn off all optimizations. if (CMAKE_C_COMPILER_ID STREQUAL "GNU") - set(NNG_COVERAGE_FLAGS "-g -O0 --coverage") + set(NNG_COVERAGE_FLAGS "-g -O0 --coverage") elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") - set(NNG_COVERAGE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") + set(NNG_COVERAGE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") elseif (CMAKE_C_COMPILER_ID STREQUAL "AppleClang") - set(NNG_COVERAGE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") - else() + set(NNG_COVERAGE_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") + else () message(FATAL_ERROR "Unable to enable coverage for your compiler.") - endif() -endif() + endif () +endif () set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NNG_WARN_FLAGS} ${NNG_COVERAGE_FLAGS} ${NNG_SANITIZER_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NNG_WARN_FLAGS} ${NNG_COVERAGE_FLAGS} ${NNG_SANITIZER_FLAGS}") TEST_BIG_ENDIAN(NNG_BIG_ENDIAN) if (NNG_BIG_ENDIAN) - add_definitions (-DNNG_BIG_ENDIAN) -else() - add_definitions (-DNNG_LITTLE_ENDIAN) -endif() + add_definitions(-DNNG_BIG_ENDIAN) +else () + add_definitions(-DNNG_LITTLE_ENDIAN) +endif () # If the compiler is not on Windows, does it support hiding the # symbols by default? For shared libraries we would like to do this. if (NOT WIN32 AND NOT CYGWIN) check_c_compiler_flag(-fvisibility=hidden NNG_HIDDEN_VISIBILITY) if (NNG_HIDDEN_VISIBILITY) - add_definitions (-DNNG_HIDDEN_VISIBILITY) - endif() -endif() + add_definitions(-DNNG_HIDDEN_VISIBILITY) + endif () +endif () if (CMAKE_SYSTEM_NAME MATCHES "Linux") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-DNNG_PLATFORM_LINUX) - add_definitions (-DNNG_USE_EVENTFD) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_LINUX) + add_definitions(-DNNG_USE_EVENTFD) # Windows subsystem for Linux -- smells like Linux, but it has # some differences (SO_REUSEADDR for one). if (CMAKE_SYSTEM_VERSION MATCHES "Microsoft") - add_definitions (-DNNG_PLATFORM_WSL) - endif() + add_definitions(-DNNG_PLATFORM_WSL) + endif () set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "Android") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-DNNG_PLATFORM_LINUX) - add_definitions (-DNNG_PLATFORM_ANDROID) - add_definitions (-DNNG_USE_EVENTFD) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_LINUX) + add_definitions(-DNNG_PLATFORM_ANDROID) + add_definitions(-DNNG_USE_EVENTFD) set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-DNNG_PLATFORM_DARWIN) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_DARWIN) # macOS 10.12 and later have getentropy, but the older releases # have ARC4_RANDOM, and that is sufficient to our needs. - add_definitions (-DNNG_USE_ARC4RANDOM) - - # macOS added some of CLOCK_MONOTONIC, but the implementation is - # broken and unreliable, so don't use it. - add_definitions (-DNNG_USE_CLOCKID=CLOCK_REALTIME) + add_definitions(-DNNG_USE_ARC4RANDOM) set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-DNNG_PLATFORM_FREEBSD) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_FREEBSD) set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "NetBSD") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-DNNG_PLATFORM_NETBSD) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_NETBSD) set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "OpenBSD") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-DNNG_PLATFORM_OPENBSD) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_OPENBSD) set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "SunOS") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-DNNG_PLATFORM_SUNOS) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_SUNOS) set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") - add_definitions (-DNNG_PLATFORM_WINDOWS) - add_definitions (-D_CRT_SECURE_NO_WARNINGS) - add_definitions (-D_CRT_RAND_S) + add_definitions(-DNNG_PLATFORM_WINDOWS) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_definitions(-D_CRT_RAND_S) set(NNG_PLATFORM_WINDOWS ON) # Target Windows Vista and later - add_definitions (-D_WIN32_WINNT=0x0600) - list (APPEND CMAKE_REQUIRED_DEFINITIONS -D_WIN32_WINNT=0x0600) + add_definitions(-D_WIN32_WINNT=0x0600) + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WIN32_WINNT=0x0600) elseif (CMAKE_SYSTEM_NAME MATCHES "QNX") - add_definitions (-DNNG_PLATFORM_POSIX) - add_definitions (-D__EXT_BSD) - add_definitions (-D_QNX_SOURCE) - add_definitions (-DNNG_PLATFORM_QNX) + add_definitions(-DNNG_PLATFORM_POSIX) + add_definitions(-D__EXT_BSD) + add_definitions(-D_QNX_SOURCE) + add_definitions(-DNNG_PLATFORM_QNX) set(NNG_PLATFORM_POSIX ON) else () - message (AUTHOR_WARNING "WARNING: This platform may not be supported: ${CMAKE_SYSTEM_NAME}") - message (AUTHOR_WARNING "${ISSUE_REPORT_MSG}") + message(AUTHOR_WARNING "WARNING: This platform may not be supported: ${CMAKE_SYSTEM_NAME}") + message(AUTHOR_WARNING "${ISSUE_REPORT_MSG}") # blithely hope for POSIX to work - add_definitions (-DNNG_PLATFORM_POSIX) + add_definitions(-DNNG_PLATFORM_POSIX) endif () -macro (nng_check_func SYM DEF) - check_function_exists (${SYM} ${DEF}) +macro(nng_check_func SYM DEF) + check_function_exists(${SYM} ${DEF}) if (${DEF}) - add_definitions (-D${DEF}=1) + add_definitions(-D${DEF}=1) endif () -endmacro (nng_check_func) +endmacro(nng_check_func) -macro (nng_check_sym SYM HDR DEF) - check_symbol_exists (${SYM} ${HDR} ${DEF}) +macro(nng_check_sym SYM HDR DEF) + check_symbol_exists(${SYM} ${HDR} ${DEF}) if (${DEF}) - add_definitions (-D${DEF}=1) + add_definitions(-D${DEF}=1) endif () -endmacro (nng_check_sym) +endmacro(nng_check_sym) -macro (nng_check_lib LIB SYM DEF) - check_library_exists (${LIB} ${SYM} "" ${DEF}) +macro(nng_check_lib LIB SYM DEF) + check_library_exists(${LIB} ${SYM} "" ${DEF}) if (${DEF}) - add_definitions (-D${DEF}=1) + add_definitions(-D${DEF}=1) list(APPEND NNG_LIBS ${LIB}) endif () -endmacro (nng_check_lib) +endmacro(nng_check_lib) -macro (nng_check_struct_member STR MEM HDR DEF) - check_struct_has_member ("struct ${STR}" ${MEM} ${HDR} ${DEF}) +macro(nng_check_struct_member STR MEM HDR DEF) + check_struct_has_member("struct ${STR}" ${MEM} ${HDR} ${DEF}) if (${DEF}) - add_definitions (-D${DEF}=1) + add_definitions(-D${DEF}=1) endif () -endmacro (nng_check_struct_member) +endmacro(nng_check_struct_member) if (WIN32) # Windows is a special snowflake. list(APPEND NNG_LIBS ws2_32 mswsock advapi32) - nng_check_sym (InitializeConditionVariable windows.h NNG_HAVE_CONDVAR) - nng_check_sym (snprintf stdio.h NNG_HAVE_SNPRINTF) + nng_check_sym(InitializeConditionVariable windows.h NNG_HAVE_CONDVAR) + nng_check_sym(snprintf stdio.h NNG_HAVE_SNPRINTF) if (NOT NNG_HAVE_CONDVAR OR NOT NNG_HAVE_SNPRINTF) - message (FATAL_ERROR - "Modern Windows API support is missing. " - "Versions of Windows prior to Vista are not supported. " - "Further, the 32-bit MinGW environment is not supported. " - "Ensure you have at least Windows Vista or newer, and are " - "using either Visual Studio 2013 or newer or MinGW-W64.") - endif() + message(FATAL_ERROR + "Modern Windows API support is missing. " + "Versions of Windows prior to Vista are not supported. " + "Further, the 32-bit MinGW environment is not supported. " + "Ensure you have at least Windows Vista or newer, and are " + "using either Visual Studio 2013 or newer or MinGW-W64.") + endif () else () # Unconditionally declare the following feature test macros. These are # needed for some platforms (glibc and SunOS/illumos) and are harmless # on the others. - add_definitions (-D_GNU_SOURCE) - add_definitions (-D_REENTRANT) - add_definitions (-D_THREAD_SAFE) - add_definitions (-D_POSIX_PTHREAD_SEMANTICS) + add_definitions(-D_GNU_SOURCE) + add_definitions(-D_REENTRANT) + add_definitions(-D_THREAD_SAFE) + add_definitions(-D_POSIX_PTHREAD_SEMANTICS) list(APPEND NNG_PKGS Threads) find_package(Threads REQUIRED) - nng_check_func (lockf NNG_HAVE_LOCKF) - nng_check_func (flock NNG_HAVE_FLOCK) + nng_check_func(lockf NNG_HAVE_LOCKF) + nng_check_func(flock NNG_HAVE_FLOCK) - nng_check_lib (rt clock_gettime NNG_HAVE_CLOCK_GETTIME) - nng_check_lib (pthread sem_wait NNG_HAVE_SEMAPHORE_PTHREAD) - nng_check_lib (pthread pthread_atfork NNG_HAVE_PTHREAD_ATFORK_PTHREAD) - nng_check_lib (nsl gethostbyname NNG_HAVE_LIBNSL) - nng_check_lib (socket socket NNG_HAVE_LIBSOCKET) + nng_check_lib(rt clock_gettime NNG_HAVE_CLOCK_GETTIME) + nng_check_lib(pthread sem_wait NNG_HAVE_SEMAPHORE_PTHREAD) + nng_check_lib(pthread pthread_atfork NNG_HAVE_PTHREAD_ATFORK_PTHREAD) + nng_check_lib(nsl gethostbyname NNG_HAVE_LIBNSL) + nng_check_lib(socket socket NNG_HAVE_LIBSOCKET) - nng_check_sym (AF_UNIX sys/socket.h NNG_HAVE_UNIX_SOCKETS) - nng_check_sym (backtrace_symbols_fd execinfo.h NNG_HAVE_BACKTRACE) + nng_check_sym(AF_UNIX sys/socket.h NNG_HAVE_UNIX_SOCKETS) + nng_check_sym(backtrace_symbols_fd execinfo.h NNG_HAVE_BACKTRACE) nng_check_struct_member(msghdr msg_control sys/socket.h NNG_HAVE_MSG_CONTROL) - nng_check_sym (eventfd sys/eventfd.h NNG_HAVE_EVENTFD) - nng_check_sym (kqueue sys/event.h NNG_HAVE_KQUEUE) - nng_check_sym (port_create port.h NNG_HAVE_PORT_CREATE) - nng_check_sym (epoll_create sys/epoll.h NNG_HAVE_EPOLL) - nng_check_sym (epoll_create1 sys/epoll.h NNG_HAVE_EPOLL_CREATE1) - nng_check_sym (getpeereid unistd.h NNG_HAVE_GETPEEREID) - nng_check_sym (SO_PEERCRED sys/socket.h NNG_HAVE_SOPEERCRED) - nng_check_struct_member (sockpeercred uid sys/socket.h NNG_HAVE_SOCKPEERCRED) - nng_check_sym (LOCAL_PEERCRED sys/un.h NNG_HAVE_LOCALPEERCRED) - nng_check_sym (getpeerucred ucred.h NNG_HAVE_GETPEERUCRED) - nng_check_sym (atomic_flag_test_and_set stdatomic.h NNG_HAVE_STDATOMIC) + nng_check_sym(eventfd sys/eventfd.h NNG_HAVE_EVENTFD) + nng_check_sym(kqueue sys/event.h NNG_HAVE_KQUEUE) + nng_check_sym(port_create port.h NNG_HAVE_PORT_CREATE) + nng_check_sym(epoll_create sys/epoll.h NNG_HAVE_EPOLL) + nng_check_sym(epoll_create1 sys/epoll.h NNG_HAVE_EPOLL_CREATE1) + nng_check_sym(getpeereid unistd.h NNG_HAVE_GETPEEREID) + nng_check_sym(SO_PEERCRED sys/socket.h NNG_HAVE_SOPEERCRED) + nng_check_struct_member(sockpeercred uid sys/socket.h NNG_HAVE_SOCKPEERCRED) + nng_check_sym(LOCAL_PEERCRED sys/un.h NNG_HAVE_LOCALPEERCRED) + nng_check_sym(getpeerucred ucred.h NNG_HAVE_GETPEERUCRED) + nng_check_sym(atomic_flag_test_and_set stdatomic.h NNG_HAVE_STDATOMIC) endif () -nng_check_sym (strlcat string.h NNG_HAVE_STRLCAT) -nng_check_sym (strlcpy string.h NNG_HAVE_STRLCPY) -nng_check_sym (strnlen string.h NNG_HAVE_STRNLEN) -nng_check_sym (strcasecmp string.h NNG_HAVE_STRCASECMP) -nng_check_sym (strncasecmp string.h NNG_HAVE_STRNCASECMP) +nng_check_sym(strlcat string.h NNG_HAVE_STRLCAT) +nng_check_sym(strlcpy string.h NNG_HAVE_STRLCPY) +nng_check_sym(strnlen string.h NNG_HAVE_STRNLEN) +nng_check_sym(strcasecmp string.h NNG_HAVE_STRCASECMP) +nng_check_sym(strncasecmp string.h NNG_HAVE_STRNCASECMP) # Set a static symbol. We do this for testing, so that tests can # be skipped if they would rely on symbols that might not be exported. # For example, idhash depends on private symbols, so don't test it # when using a shared library on Windows because the symbols won't # resolve. -if (NOT(BUILD_SHARED_LIBS)) - set (NNG_STATIC_LIB ON) +if (NOT (BUILD_SHARED_LIBS)) + set(NNG_STATIC_LIB ON) message(STATUS "Building static libs") -endif() +endif () + +# In order to facilitate testing, we want to add a library that includes +# our common test code. We do this before iterating everywhere else so +# that we can locate our tests inside the directories where we want. +if (NNG_TESTS) + enable_testing() + set(all_tests, "") + + + macro(nng_test NAME) + add_executable(${NAME} ${NAME}.c ${ARGN}) + target_link_libraries(${NAME} ${PROJECT_NAME}_testlib) + target_include_directories(${NAME} PRIVATE + ${PROJECT_SOURCE_DIR}/tests + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/include) + add_test(NAME ${NAME} COMMAND ${NAME} -t) + set_tests_properties(${NAME} PROPERTIES TIMEOUT 180) + endmacro() + + function(nng_sources_testlib) + foreach (f ${ARGN}) + target_sources(${PROJECT_NAME}_testlib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${f}) + endforeach () + endfunction() + + function(nng_headers_testlib) + foreach (f ${ARGN}) + target_sources(${PROJECT_NAME}_testlib PRIVATE ${PROJECT_SOURCE_DIR}/include/${f}) + endforeach () + endfunction() + + function(nng_defines_testlib) + target_compile_definitions(${PROJECT_NAME}_testlib PRIVATE ${ARGN}) + endfunction() + +else () + function(nng_test NAME) + endfunction() -add_subdirectory (src) + function(nng_sources_testlib) + endfunction() -foreach(_PKG IN ITEMS ${NNG_PKGS}) - find_package(${_PKG} REQUIRED) + function(nng_headers_testlib) + endfunction() + + function(nng_defines_testlib) + endfunction() +endif () + +function(nng_sources) + foreach (f ${ARGN}) + target_sources(${PROJECT_NAME}_testlib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${f}) + endforeach () + nng_sources_testlib(${ARGN}) +endfunction() + +function(nng_headers) + foreach (f ${ARGN}) + target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include/${f}) + endforeach () + nng_headers_testlib(${ARGN}) +endfunction() + +function(nng_defines) + target_compile_definitions(${PROJECT_NAME} PRIVATE ${ARGN}) + nng_defines_testlib(${ARGN}) +endfunction() + +# nng_sources_if adds the sources unconditionally to the test library, +# but conditionally to the production library. This allows us to get +# full test coverage while allowing a minimized delivery. +function(nng_sources_if COND) + if (${COND}) + foreach (f ${ARGN}) + target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${f}) + endforeach () + endif () + nng_sources_testlib(${ARGN}) +endfunction() + +function(nng_headers_if COND) + if (COND) + foreach (f ${ARGN}) + target_sources(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include/${f}) + endforeach () + endif () + nng_headers_testlib(${ARGN}) +endfunction() + +function(nng_defines_if COND) + if (${COND}) + # Revisit this one + target_compile_definitions(${PROJECT_NAME} PUBLIC ${ARGN}) + endif () + nng_defines_testlib(${ARGN}) +endfunction() + +add_subdirectory(src) + +foreach (_PKG IN ITEMS ${NNG_PKGS}) + find_package(${_PKG} REQUIRED) endforeach () add_definitions(${NNG_DEFS}) if (NNG_TESTS) - enable_testing() - set(all_tests, "") - add_subdirectory (tests) - add_subdirectory (perf) -endif() + add_subdirectory(tests) + add_subdirectory(perf) +endif () # Build the tools if (NNG_ENABLE_NNGCAT) - add_subdirectory (tools/nngcat) + add_subdirectory(tools/nngcat) endif () -add_subdirectory (docs/man) - -set (CPACK_PACKAGE_NAME ${PROJECT_NAME}) -set (CPACK_PACKAGE_VERSION ${NNG_PACKAGE_VERSION}) -set (CPACK_PACKAGE_CONTACT "nanomsg@freelists.org") -set (CPACK_PACKAGE_VENDOR "nanomsg.org") -set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "nanomsg next generation library") -set (CPACK_SOURCE_GENERATOR "TBZ2;TGZ;ZIP") -set (CPACK_SOURCE_IGNORE_FILES "/build/;/.git/;~$;${CPACK_SOURCE_IGNORE_FILES}") -set (CPACK_SOURCE_PACKAGE_FILE_NAME - "${PROJECT_NAME}-v${NNG_PACKAGE_VERSION}-src") -set (CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt) -set (CPACK_PACKAGE_INSTALL_DIRECTORY "nng") -set (CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-v${NNG_PACKAGE_VERSION}") +add_subdirectory(docs/man) + +set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) +set(CPACK_PACKAGE_VERSION ${NNG_PACKAGE_VERSION}) +set(CPACK_PACKAGE_CONTACT "nanomsg@freelists.org") +set(CPACK_PACKAGE_VENDOR "nanomsg.org") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "nanomsg next generation library") +set(CPACK_SOURCE_GENERATOR "TBZ2;TGZ;ZIP") +set(CPACK_SOURCE_IGNORE_FILES "/build/;/.git/;~$;${CPACK_SOURCE_IGNORE_FILES}") +set(CPACK_SOURCE_PACKAGE_FILE_NAME + "${PROJECT_NAME}-v${NNG_PACKAGE_VERSION}-src") +set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt) +set(CPACK_PACKAGE_INSTALL_DIRECTORY "nng") +set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-v${NNG_PACKAGE_VERSION}") add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) -include (CPack) +include(CPack) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cce1d478..e8d171c9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,9 +24,22 @@ # IN THE SOFTWARE. # +add_library (${PROJECT_NAME} + nng.c + ${PROJECT_SOURCE_DIR}/include/nng/nng.h) + +if (NNG_TESTS) + add_library(${PROJECT_NAME}_testlib STATIC + nng.c + ${PROJECT_SOURCE_DIR}/tests/testutil.c + ${PROJECT_SOURCE_DIR}/tests/testutil.h + ${PROJECT_SOURCE_DIR}/include/nng/nng.h) +endif() + + +#nng.c +#${PROJECT_SOURCE_DIR}/include/nng/nng.h set (NNG_SRCS - nng.c - ${PROJECT_SOURCE_DIR}/include/nng/nng.h core/defs.h @@ -202,7 +215,14 @@ foreach(_PKG IN ITEMS ${NNG_PKGS}) endforeach () # Library -add_library (${PROJECT_NAME} ${NNG_SRCS}) +target_sources(${PROJECT_NAME} PRIVATE ${NNG_SRCS}) + +if (NNG_TESTS) + target_sources(${PROJECT_NAME}_testlib PRIVATE ${NNG_SRCS}) + target_link_libraries (${PROJECT_NAME}_testlib PUBLIC ${NNG_LIBS}) + target_compile_definitions(${PROJECT_NAME}_testlib PUBLIC NNG_STATIC_LIB NNG_TEST_LIB) + target_include_directories (${PROJECT_NAME}_testlib PRIVATE ${PROJECT_SOURCE_DIR}/include) +endif() # When building shared libraries we prefer to suppress default symbol # visibility, so that only the symbols that should be exposed in the diff --git a/src/protocol/pair1/CMakeLists.txt b/src/protocol/pair1/CMakeLists.txt index cbcb2bc2..3f92c585 100644 --- a/src/protocol/pair1/CMakeLists.txt +++ b/src/protocol/pair1/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2018 Staysail Systems, Inc. <info@staystail.tech> +# Copyright 2019 Staysail Systems, Inc. <info@staystail.tech> # Copyright 2018 Capitar IT Group BV <info@capitar.com> # # This software is supplied under the terms of the MIT License, a @@ -12,10 +12,7 @@ option (NNG_PROTO_PAIR1 "Enable PAIRv1 protocol." ON) mark_as_advanced(NNG_PROTO_PAIR1) -if (NNG_PROTO_PAIR1) - set(_DEFS -DNNG_HAVE_PAIR1) - set(_SRCS protocol/pair1/pair.c ${PROJECT_SOURCE_DIR}/include/nng/protocol/pair1/pair.h) - - set(NNG_DEFS ${NNG_DEFS} ${_DEFS} PARENT_SCOPE) - set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) -endif() +nng_sources_if(NNG_PROTO_PAIR1 pair.c) +nng_headers_if(NNG_PROTO_PAIR1 nng/protocol/pair1/pair.h) +nng_defines_if(NNG_PROTO_PAIR1 NNG_HAVE_PAIR1) +nng_test(pair1_test)
\ No newline at end of file diff --git a/src/protocol/pair1/pair.c b/src/protocol/pair1/pair.c index 451122ea..7adb8bd8 100644 --- a/src/protocol/pair1/pair.c +++ b/src/protocol/pair1/pair.c @@ -12,7 +12,6 @@ #include <string.h> #include "core/nng_impl.h" - #include "nng/protocol/pair1/pair.h" // Pair protocol. The PAIRv1 protocol is a simple 1:1 messaging pattern, diff --git a/src/protocol/pair1/pair1_test.c b/src/protocol/pair1/pair1_test.c new file mode 100644 index 00000000..3a78bb18 --- /dev/null +++ b/src/protocol/pair1/pair1_test.c @@ -0,0 +1,607 @@ +// +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2017 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. +// + +#include <string.h> + +#include <nng/nng.h> +#include <nng/protocol/pair1/pair.h> + +#include <testutil.h> + +#include <acutest.h> + +#define SECOND 1000 + +#define APPEND_STR(m, s) TEST_CHECK(nng_msg_append(m, s, strlen(s)) == 0) +#define CHECK_STR(m, s) \ + TEST_CHECK(nng_msg_len(m) == strlen(s)); \ + TEST_CHECK(memcmp(nng_msg_body(m), s, strlen(s)) == 0) + +void +test_mono_cooked(void) +{ + nng_socket s1; + nng_socket c1; + nng_msg * msg; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + TEST_CHECK(testutil_marry(s1, c1) == 0); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_append(msg, "ALPHA", strlen("ALPHA") + 1) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + TEST_CHECK(nng_msg_len(msg) == strlen("ALPHA") + 1); + TEST_CHECK(strcmp(nng_msg_body(msg), "ALPHA") == 0); + nng_msg_free(msg); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_append(msg, "BETA", strlen("BETA") + 1) == 0); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c1, &msg, 0) == 0); + TEST_CHECK(nng_msg_len(msg) == strlen("BETA") + 1); + TEST_CHECK(strcmp(nng_msg_body(msg), "BETA") == 0); + + nng_msg_free(msg); + TEST_CHECK(nng_close(c1) == 0); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_mono_faithful(void) +{ + nng_socket s1; + nng_socket c1; + nng_socket c2; + nng_msg * msg; + const char *addr = "inproc://pair1_mono_faithful"; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + TEST_CHECK(nng_pair1_open(&c2) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, SECOND / 4) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(c2, NNG_OPT_SENDTIMEO, SECOND) == 0); + + TEST_CHECK(nng_listen(s1, addr, NULL, 0) == 0); + TEST_CHECK(nng_dial(c1, addr, NULL, 0) == 0); + testutil_sleep(100); + TEST_CHECK(nng_dial(c2, addr, NULL, 0) == 0); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "ONE"); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + CHECK_STR(msg, "ONE"); + nng_msg_free(msg); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "TWO"); + TEST_CHECK(nng_sendmsg(c2, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c1) == 0); + TEST_CHECK(nng_close(c2) == 0); +} + +void +test_mono_back_pressure(void) +{ + nng_socket s1; + nng_socket c1; + int i; + int rv; + nng_msg * msg; + nng_duration to = 100; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_RECVBUF, 1) == 0); + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_SENDBUF, 1) == 0); + TEST_CHECK(nng_setopt_int(c1, NNG_OPT_RECVBUF, 1) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, to) == 0); + + TEST_CHECK(testutil_marry(s1, c1) == 0); + + // We choose to allow some buffering. In reality the + // buffer size is just 1, and we will fail after 2. + for (i = 0, rv = 0; i < 10; i++) { + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + if ((rv = nng_sendmsg(s1, msg, 0)) != 0) { + nng_msg_free(msg); + break; + } + } + TEST_CHECK(rv == NNG_ETIMEDOUT); + TEST_CHECK(i < 10); + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c1) == 0); +} + +void +test_mono_raw_exchange(void) +{ + nng_socket s1; + nng_socket c1; + + nng_msg *msg; + uint32_t hops; + + TEST_CHECK(nng_pair1_open_raw(&s1) == 0); + TEST_CHECK(nng_pair1_open_raw(&c1) == 0); + + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, SECOND) == 0); + TEST_CHECK(testutil_marry(s1, c1) == 0); + + nng_pipe p = NNG_PIPE_INITIALIZER; + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "GAMMA"); + TEST_CHECK(nng_msg_header_append_u32(msg, 1) == 0); + TEST_CHECK(nng_msg_header_len(msg) == sizeof(uint32_t)); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + p = nng_msg_get_pipe(msg); + TEST_CHECK(nng_pipe_id(p) > 0); + + CHECK_STR(msg, "GAMMA"); + TEST_CHECK(nng_msg_header_len(msg) == sizeof(uint32_t)); + TEST_CHECK(nng_msg_header_trim_u32(msg, &hops) == 0); + TEST_CHECK(hops == 2); + nng_msg_free(msg); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "EPSILON"); + TEST_CHECK(nng_msg_header_append_u32(msg, 1) == 0); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c1, &msg, 0) == 0); + CHECK_STR(msg, "EPSILON"); + TEST_CHECK(nng_msg_header_len(msg) == sizeof(uint32_t)); + TEST_CHECK(nng_msg_header_trim_u32(msg, &hops) == 0); + p = nng_msg_get_pipe(msg); + TEST_CHECK(nng_pipe_id(p) > 0); + + TEST_CHECK(hops == 2); + nng_msg_free(msg); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c1) == 0); +} + +void +test_mono_raw_header(void) +{ + nng_socket s1; + nng_socket c1; + nng_msg * msg; + uint32_t v; + + TEST_CHECK(nng_pair1_open_raw(&s1) == 0); + TEST_CHECK(nng_pair1_open_raw(&c1) == 0); + + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, SECOND / 5) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, SECOND / 5) == 0); + TEST_CHECK(testutil_marry(s1, c1) == 0); + + // Missing bits in the header + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); + + // Valid header works + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_append_u32(msg, 0xFEEDFACE) == 0); + TEST_CHECK(nng_msg_header_append_u32(msg, 1) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + TEST_CHECK(nng_msg_trim_u32(msg, &v) == 0); + TEST_CHECK(v == 0xFEEDFACE); + nng_msg_free(msg); + + // Header with reserved bits set dropped + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_header_append_u32(msg, 0xDEAD0000) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); + + // With the same bits clear it works + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_append_u32(msg, 0xFEEDFACE) == 0); + TEST_CHECK(nng_msg_header_append_u32(msg, 1) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + TEST_CHECK(nng_msg_trim_u32(msg, &v) == 0); + TEST_CHECK(v == 0xFEEDFACE); + nng_msg_free(msg); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c1) == 0); +} + +void +test_poly_best_effort(void) +{ + nng_socket s1; + nng_socket c1; + nng_msg * msg; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); + + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_RECVBUF, 1) == 0); + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_SENDBUF, 1) == 0); + TEST_CHECK(nng_setopt_int(c1, NNG_OPT_RECVBUF, 1) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, SECOND) == 0); + + TEST_CHECK(testutil_marry(s1, c1) == 0); + + for (int i = 0; i < 10; i++) { + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + } + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c1) == 0); +} + +void +test_poly_cooked(void) +{ + nng_socket s1; + nng_socket c1; + nng_socket c2; + nng_msg * msg; + bool v; + nng_pipe p1; + nng_pipe p2; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + TEST_CHECK(nng_pair1_open(&c2) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(c2, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, SECOND / 10) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, SECOND / 10) == 0); + TEST_CHECK(nng_setopt_ms(c2, NNG_OPT_RECVTIMEO, SECOND / 10) == 0); + + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); + TEST_CHECK(v == false); + + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); + TEST_CHECK(v == true); + + TEST_CHECK(testutil_marry(s1, c1) == 0); + TEST_CHECK(testutil_marry(s1, c2) == 0); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "ONE"); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + CHECK_STR(msg, "ONE"); + p1 = nng_msg_get_pipe(msg); + TEST_CHECK(nng_pipe_id(p1) > 0); + nng_msg_free(msg); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "TWO"); + TEST_CHECK(nng_sendmsg(c2, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + CHECK_STR(msg, "TWO"); + p2 = nng_msg_get_pipe(msg); + TEST_CHECK(nng_pipe_id(p2) > 0); + nng_msg_free(msg); + + TEST_CHECK(nng_pipe_id(p1) != nng_pipe_id(p2)); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + + nng_msg_set_pipe(msg, p1); + APPEND_STR(msg, "UNO"); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c1, &msg, 0) == 0); + CHECK_STR(msg, "UNO"); + nng_msg_free(msg); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + nng_msg_set_pipe(msg, p2); + APPEND_STR(msg, "DOS"); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c2, &msg, 0) == 0); + CHECK_STR(msg, "DOS"); + nng_msg_free(msg); + + TEST_CHECK(nng_close(c1) == 0); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + nng_msg_set_pipe(msg, p1); + APPEND_STR(msg, "EIN"); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c2, &msg, 0) == NNG_ETIMEDOUT); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c2) == 0); +} + +void +test_poly_late(void) +{ + nng_socket s1; + nng_socket c1; + bool v; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); + TEST_CHECK(v == false); + + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); + TEST_CHECK(v == true); + + TEST_CHECK(testutil_marry(s1, c1) == 0); + + TEST_CHECK( + nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == NNG_ESTATE); + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c1) == 0); +} + +void +test_poly_default(void) +{ + nng_socket s1; + nng_socket c1; + nng_socket c2; + nng_msg * msg; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + TEST_CHECK(nng_pair1_open(&c2) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(c2, NNG_OPT_SENDTIMEO, SECOND) == 0); + + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); + + TEST_CHECK(testutil_marry(s1, c1) == 0); + TEST_CHECK(testutil_marry(s1, c2) == 0); + + // This assumes poly picks the first suitor. Applications + // should not make the same assumption. + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "YES"); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c1, &msg, 0) == 0); + CHECK_STR(msg, "YES"); + nng_msg_free(msg); + + TEST_CHECK(nng_close(c1) == 0); + testutil_sleep(10); + + // Verify that the other pipe is chosen as the next suitor. + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "AGAIN"); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c2, &msg, 0) == 0); + CHECK_STR(msg, "AGAIN"); + nng_msg_free(msg); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c2) == 0); +} + +void +test_poly_raw(void) +{ + nng_socket s1; + nng_socket c1; + nng_socket c2; + nng_msg * msg; + bool v; + uint32_t hops; + nng_pipe p1; + nng_pipe p2; + + TEST_CHECK(nng_pair1_open_raw(&s1) == 0); + TEST_CHECK(nng_pair1_open(&c1) == 0); + TEST_CHECK(nng_pair1_open(&c2) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, SECOND / 5) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, SECOND / 5) == 0); + TEST_CHECK(nng_setopt_ms(c2, NNG_OPT_RECVTIMEO, SECOND / 5) == 0); + + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); + TEST_CHECK(v == 0); + + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); + TEST_CHECK(v == true); + + v = false; + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_RAW, &v) == 0); + TEST_CHECK(v == true); + + TEST_CHECK(testutil_marry(s1, c1) == 0); + TEST_CHECK(testutil_marry(s1, c2) == 0); + + // send/recv works + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "ONE"); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + CHECK_STR(msg, "ONE"); + p1 = nng_msg_get_pipe(msg); + TEST_CHECK(nng_pipe_id(p1) > 0); + TEST_CHECK(nng_msg_header_trim_u32(msg, &hops) == 0); + TEST_CHECK(hops == 1); + nng_msg_free(msg); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "TWO"); + TEST_CHECK(nng_sendmsg(c2, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + CHECK_STR(msg, "TWO"); + p2 = nng_msg_get_pipe(msg); + TEST_CHECK(nng_pipe_id(p2) > 0); + TEST_CHECK(nng_msg_header_trim_u32(msg, &hops) == 0); + TEST_CHECK(hops == 1); + nng_msg_free(msg); + + TEST_CHECK(nng_pipe_id(p1) != nng_pipe_id(p2)); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + nng_msg_set_pipe(msg, p1); + APPEND_STR(msg, "UNO"); + TEST_CHECK(nng_msg_header_append_u32(msg, 1) == 0); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c1, &msg, 0) == 0); + CHECK_STR(msg, "UNO"); + nng_msg_free(msg); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + nng_msg_set_pipe(msg, p2); + APPEND_STR(msg, "DOS"); + TEST_CHECK(nng_msg_header_append_u32(msg, 1) == 0); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c2, &msg, 0) == 0); + CHECK_STR(msg, "DOS"); + nng_msg_free(msg); + + // Verify closing the pipe stops any of its traffic + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + APPEND_STR(msg, "ONE"); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + CHECK_STR(msg, "ONE"); + p1 = nng_msg_get_pipe(msg); + TEST_CHECK(nng_pipe_id(p1) > 0); + nng_msg_free(msg); + + TEST_CHECK(nng_close(c1) == 0); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + nng_msg_set_pipe(msg, p1); + APPEND_STR(msg, "EIN"); + TEST_CHECK(nng_msg_header_append_u32(msg, 1) == 0); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(c2, &msg, 0) == NNG_ETIMEDOUT); +} + +void +test_raw(void) +{ + nng_socket s1; + bool raw; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_RAW, &raw) == 0); + TEST_CHECK(raw == false); + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_RAW, true) == NNG_EREADONLY); + TEST_CHECK(nng_close(s1) == 0); + + TEST_CHECK(nng_pair1_open_raw(&s1) == 0); + TEST_CHECK(nng_getopt_bool(s1, NNG_OPT_RAW, &raw) == 0); + TEST_CHECK(raw == true); + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_RAW, false) == NNG_EREADONLY); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_ttl(void) +{ + nng_socket s1; + nng_socket c1; + nng_msg * msg; + uint32_t val; + int ttl; + + TEST_CHECK(nng_pair1_open_raw(&s1) == 0); + TEST_CHECK(nng_pair1_open_raw(&c1) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, SECOND / 5) == 0); + TEST_CHECK(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, SECOND / 5) == 0); + + // cannot set insane TTLs + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_MAXTTL, 0) == NNG_EINVAL); + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_MAXTTL, 1000) == NNG_EINVAL); + ttl = 8; + TEST_CHECK(nng_setopt(s1, NNG_OPT_MAXTTL, &ttl, 1) == NNG_EINVAL); + TEST_CHECK(nng_setopt_bool(s1, NNG_OPT_MAXTTL, true) == NNG_EBADTYPE); + + TEST_CHECK(testutil_marry(s1, c1) == 0); + + // Let's check enforcement of TTL + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_MAXTTL, 4) == 0); + TEST_CHECK(nng_getopt_int(s1, NNG_OPT_MAXTTL, &ttl) == 0); + TEST_CHECK(ttl == 4); + + // Bad TTL bounces + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_header_append_u32(msg, 4) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); + + // Good TTL passes + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_append_u32(msg, 0xFEEDFACE) == 0); + TEST_CHECK(nng_msg_header_append_u32(msg, 3) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + TEST_CHECK(nng_msg_trim_u32(msg, &val) == 0); + TEST_CHECK(val == 0xFEEDFACE); + TEST_CHECK(nng_msg_header_trim_u32(msg, &val) == 0); + TEST_CHECK(val == 4); + nng_msg_free(msg); + + // Large TTL passes + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_MAXTTL, 0xff) == 0); + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_append_u32(msg, 1234) == 0); + TEST_CHECK(nng_msg_header_append_u32(msg, 0xfe) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == 0); + TEST_CHECK(nng_msg_trim_u32(msg, &val) == 0); + TEST_CHECK(val == 1234); + TEST_CHECK(nng_msg_header_trim_u32(msg, &val) == 0); + TEST_CHECK(val == 0xff); + nng_msg_free(msg); + + // Max TTL fails + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_MAXTTL, 0xff) == 0); + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_header_append_u32(msg, 0xff) == 0); + TEST_CHECK(nng_sendmsg(c1, msg, 0) == 0); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(c1) == 0); +} + +TEST_LIST = { + { "pair1 monogamous cooked", test_mono_cooked }, + { "pair1 monogamous faithful", test_mono_faithful }, + { "pair1 monogamous back pressure", test_mono_back_pressure }, + { "pair1 monogamous raw exchange", test_mono_raw_exchange }, + { "pair1 monogamous raw header", test_mono_raw_header }, + { "pair1 polyamorous best effort", test_poly_best_effort }, + { "pair1 polyamorous cooked", test_poly_cooked }, + { "pair1 polyamorous late", test_poly_late }, + { "pair1 polyamorous default", test_poly_default }, + { "pair1 polyamorous raw", test_poly_raw }, + { "pair1 raw", test_raw }, + { "pair1 ttl", test_ttl }, + + { NULL, NULL }, +}; diff --git a/src/protocol/reqrep0/CMakeLists.txt b/src/protocol/reqrep0/CMakeLists.txt index bae31433..4778f4ea 100644 --- a/src/protocol/reqrep0/CMakeLists.txt +++ b/src/protocol/reqrep0/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +# 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 @@ -9,30 +9,18 @@ # # Req/Rep protocol -option (NNG_PROTO_REQ0 "Enable REQv0 protocol." ON) +option(NNG_PROTO_REQ0 "Enable REQv0 protocol." ON) mark_as_advanced(NNG_PROTO_REQ0) -option (NNG_PROTO_REP0 "Enable REPv0 protocol." ON) +option(NNG_PROTO_REP0 "Enable REPv0 protocol." ON) mark_as_advanced(NNG_PROTO_REP0) -set(_DEFS) -set(_SRCS) +nng_sources_if(NNG_PROTO_REQ0 req.c xreq.c) +nng_headers_if(NNG_PROTO_REQ0 nng/protocol/reqrep0/req.h) +nng_defines_if(NNG_PROTO_REQ0 NNG_HAVE_REQ0) -if (NNG_PROTO_REQ0) - list(APPEND _DEFS -DNNG_HAVE_REQ0) - list(APPEND _SRCS - protocol/reqrep0/req.c protocol/reqrep0/xreq.c - ${PROJECT_SOURCE_DIR}/include/nng/protocol/reqrep0/req.h) - -endif() +nng_sources_if(NNG_PROTO_REP0 rep.c xrep.c) +nng_headers_if(NNG_PROTO_REP0 nng/protocol/reqrep0/rep.h) +nng_defines_if(NNG_PROTO_REP0 NNG_HAVE_REP0) -if (NNG_PROTO_REP0) - list(APPEND _DEFS -DNNG_HAVE_REP0) - list(APPEND _SRCS - protocol/reqrep0/rep.c protocol/reqrep0/xrep.c - ${PROJECT_SOURCE_DIR}/include/nng/protocol/reqrep0/rep.h) - -endif() - -set(NNG_DEFS ${NNG_DEFS} ${_DEFS} PARENT_SCOPE) -set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) +nng_test(reqrep_test) diff --git a/src/protocol/reqrep0/reqrep_test.c b/src/protocol/reqrep0/reqrep_test.c new file mode 100644 index 00000000..564cf158 --- /dev/null +++ b/src/protocol/reqrep0/reqrep_test.c @@ -0,0 +1,300 @@ +// +// 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. +// + +#include <string.h> + +#include <nng/nng.h> +#include <nng/protocol/reqrep0/rep.h> +#include <nng/protocol/reqrep0/req.h> + +#include <acutest.h> +#include <testutil.h> + +#ifndef NNI_PROTO +#define NNI_PROTO(x, y) (((x) << 4u) | (y)) +#endif + +void +test_req_rep_identity(void) +{ + nng_socket s; + int p; + char * n; + + TEST_CHECK(nng_req0_open(&s) == 0); + TEST_CHECK(nng_getopt_int(s, NNG_OPT_PROTO, &p) == 0); + TEST_CHECK(p == NNI_PROTO(3u, 0u)); // 48 + TEST_CHECK(nng_getopt_int(s, NNG_OPT_PEER, &p) == 0); + TEST_CHECK(p == NNI_PROTO(3u, 1u)); // 49 + TEST_CHECK(nng_getopt_string(s, NNG_OPT_PROTONAME, &n) == 0); + TEST_CHECK(strcmp(n, "req") == 0); + TEST_CHECK(nng_getopt_string(s, NNG_OPT_PEERNAME, &n) == 0); + TEST_CHECK(strcmp(n, "rep") == 0); + TEST_CHECK(nng_close(s) == 0); + + TEST_CHECK(nng_rep0_open(&s) == 0); + TEST_CHECK(nng_getopt_int(s, NNG_OPT_PROTO, &p) == 0); + TEST_CHECK(p == NNI_PROTO(3u, 1u)); // 49 + TEST_CHECK(nng_getopt_int(s, NNG_OPT_PEER, &p) == 0); + TEST_CHECK(p == NNI_PROTO(3u, 0u)); // 48 + TEST_CHECK(nng_getopt_string(s, NNG_OPT_PROTONAME, &n) == 0); + TEST_CHECK(strcmp(n, "rep") == 0); + TEST_CHECK(nng_getopt_string(s, NNG_OPT_PEERNAME, &n) == 0); + TEST_CHECK(strcmp(n, "req") == 0); + TEST_CHECK(nng_close(s) == 0); +} + +void +test_resend_option(void) +{ + nng_socket req; + bool b; + size_t sz = sizeof(b); + const char *opt = NNG_OPT_REQ_RESENDTIME; + + TEST_CHECK(nng_req0_open(&req) == 0); + + TEST_CHECK(nng_setopt_ms(req, opt, 10) == 0); + TEST_CHECK(nng_setopt(req, opt, "", 1) == NNG_EINVAL); + TEST_CHECK(nng_getopt(req, opt, &b, &sz) == NNG_EINVAL); + TEST_CHECK(nng_setopt_bool(req, opt, true) == NNG_EBADTYPE); + TEST_CHECK(nng_getopt_bool(req, opt, &b) == NNG_EBADTYPE); + + TEST_CHECK(nng_close(req) == 0); +} + +void +test_req_recv_bad_state(void) +{ + nng_socket req; + nng_msg * msg = NULL; + + TEST_CHECK(nng_req0_open(&req) == 0); + TEST_CHECK(nng_recvmsg(req, &msg, 0) == NNG_ESTATE); + TEST_CHECK(msg == NULL); + TEST_CHECK(nng_close(req) == 0); +} + +void +test_rep_send_bad_state(void) +{ + nng_socket rep; + nng_msg * msg = NULL; + + TEST_CHECK(nng_rep0_open(&rep) == 0); + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_sendmsg(rep, msg, 0) == NNG_ESTATE); + nng_msg_free(msg); + TEST_CHECK(nng_close(rep) == 0); +} + +#define SECOND 1000 + +void +test_req_rep_exchange(void) +{ + nng_socket req; + nng_socket rep; + nng_msg * msg = NULL; + + TEST_CHECK(nng_req0_open(&req) == 0); + TEST_CHECK(nng_rep0_open(&rep) == 0); + + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_RECVTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(rep, NNG_OPT_RECVTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(rep, NNG_OPT_SENDTIMEO, SECOND) == 0); + + TEST_CHECK(testutil_marry(rep, req) == 0); + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_msg_append(msg, "ping", 5) == 0); + TEST_CHECK(nng_msg_len(msg) == 5); + TEST_CHECK(strcmp(nng_msg_body(msg), "ping") == 0); + TEST_CHECK(nng_sendmsg(req, msg, 0) == 0); + msg = NULL; + TEST_CHECK(nng_recvmsg(rep, &msg, 0) == 0); + TEST_CHECK(msg != NULL); + TEST_CHECK(nng_msg_len(msg) == 5); + TEST_CHECK(strcmp(nng_msg_body(msg), "ping") == 0); + nng_msg_trim(msg, 5); + TEST_CHECK(nng_msg_append(msg, "pong", 5) == 0); + TEST_CHECK(nng_sendmsg(rep, msg, 0) == 0); + msg = NULL; + TEST_CHECK(nng_recvmsg(req, &msg, 0) == 0); + TEST_CHECK(msg != NULL); + TEST_CHECK(nng_msg_len(msg) == 5); + TEST_CHECK(strcmp(nng_msg_body(msg), "pong") == 0); + nng_msg_free(msg); + + TEST_CHECK(nng_close(req) == 0); + TEST_CHECK(nng_close(rep) == 0); +} + +void +test_req_cancel(void) +{ + nng_msg * abc; + nng_msg * def; + nng_msg * cmd; + nng_duration retry = SECOND; + nng_socket req; + nng_socket rep; + + TEST_CHECK(nng_rep_open(&rep) == 0); + TEST_CHECK(nng_req_open(&req) == 0); + + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_RECVTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(rep, NNG_OPT_RECVTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(rep, NNG_OPT_SENDTIMEO, SECOND) == 0); + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_REQ_RESENDTIME, retry) == 0); + TEST_CHECK(nng_setopt_int(req, NNG_OPT_SENDBUF, 16) == 0); + + TEST_CHECK(nng_msg_alloc(&abc, 0) == 0); + TEST_CHECK(nng_msg_append(abc, "abc", 4) == 0); + TEST_CHECK(nng_msg_alloc(&def, 0) == 0); + TEST_CHECK(nng_msg_append(def, "def", 4) == 0); + + TEST_CHECK(testutil_marry(rep, req) == 0); + + // Send req #1 (abc). + TEST_CHECK(nng_sendmsg(req, abc, 0) == 0); + + // Sleep a bit. This is so that we ensure that our request gets + // to the far side. (If we cancel too fast, then our outgoing send + // will be canceled before it gets to the peer.) + testutil_sleep(100); + + // Send the next next request ("def"). Note that + // the REP side server will have already buffered the receive + // request, and should simply be waiting for us to reply to abc. + TEST_CHECK(nng_sendmsg(req, def, 0) == 0); + + // Receive the first request (should be abc) on the REP server. + TEST_CHECK(nng_recvmsg(rep, &cmd, 0) == 0); + TEST_ASSERT(cmd != NULL); + TEST_CHECK(nng_msg_len(cmd) == 4); + TEST_CHECK(strcmp(nng_msg_body(cmd), "abc") == 0); + + // REP sends the reply to first command. This will be discarded + // by the REQ socket. + TEST_CHECK(nng_sendmsg(rep, cmd, 0) == 0); + + // Now get the next command from the REP; should be "def". + TEST_CHECK(nng_recvmsg(rep, &cmd, 0) == 0); + TEST_ASSERT(cmd != NULL); + TEST_CHECK(nng_msg_len(cmd) == 4); + TEST_CHECK(strcmp(nng_msg_body(cmd), "def") == 0); + TEST_MSG("Received body was %s", nng_msg_body(cmd)); + + // And send it back to REQ. + TEST_CHECK(nng_sendmsg(rep, cmd, 0) == 0); + + // Try a req command. This should give back "def" + TEST_CHECK(nng_recvmsg(req, &cmd, 0) == 0); + TEST_CHECK(nng_msg_len(cmd) == 4); + TEST_CHECK(strcmp(nng_msg_body(cmd), "def") == 0); + nng_msg_free(cmd); + + TEST_CHECK(nng_close(req) == 0); + TEST_CHECK(nng_close(rep) == 0); +} + +void +test_req_cancel_abort_recv(void) +{ + + nng_msg * abc; + nng_msg * def; + nng_msg * cmd; + nng_aio * aio; + nng_duration retry = SECOND * 10; // 10s (kind of never) + nng_socket req; + nng_socket rep; + + TEST_CHECK(nng_rep_open(&rep) == 0); + TEST_CHECK(nng_req_open(&req) == 0); + TEST_CHECK(nng_aio_alloc(&aio, NULL, NULL) == 0); + + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_REQ_RESENDTIME, retry) == 0); + TEST_CHECK(nng_setopt_int(req, NNG_OPT_SENDBUF, 16) == 0); + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_RECVTIMEO, 5 * SECOND) == 0); + TEST_CHECK(nng_setopt_ms(rep, NNG_OPT_RECVTIMEO, 5 * SECOND) == 0); + TEST_CHECK(nng_setopt_ms(req, NNG_OPT_SENDTIMEO, 5 * SECOND) == 0); + TEST_CHECK(nng_setopt_ms(rep, NNG_OPT_SENDTIMEO, 5 * SECOND) == 0); + + TEST_CHECK(nng_msg_alloc(&abc, 0) == 0); + TEST_CHECK(nng_msg_append(abc, "abc", 4) == 0); + TEST_CHECK(nng_msg_alloc(&def, 0) == 0); + TEST_CHECK(nng_msg_append(def, "def", 4) == 0); + + TEST_CHECK(testutil_marry(rep, req) == 0); + + // Send req #1 (abc). + TEST_CHECK(nng_sendmsg(req, abc, 0) == 0); + + // Wait for it to get ot the other side. + testutil_sleep(100); + + nng_aio_set_timeout(aio, 5 * SECOND); + nng_recv_aio(req, aio); + + // Give time for this recv to post properly. + testutil_sleep(100); + + // Send the next next request ("def"). Note that + // the REP side server will have already buffered the receive + // request, and should simply be waiting for us to reply to + // abc. + TEST_CHECK(nng_sendmsg(req, def, 0) == 0); + + // Our pending I/O should have been canceled. + nng_aio_wait(aio); + TEST_CHECK(nng_aio_result(aio) == NNG_ECANCELED); + + // Receive the first request (should be abc) on the REP server. + TEST_CHECK(nng_recvmsg(rep, &cmd, 0) == 0); + TEST_CHECK(nng_msg_len(cmd) == 4); + TEST_CHECK(strcmp(nng_msg_body(cmd), "abc") == 0); + + // REP sends the reply to first command. This will be + // discarded by the REQ socket. + TEST_CHECK(nng_sendmsg(rep, cmd, 0) == 0); + + // Now get the next command from the REP; should be "def". + TEST_CHECK(nng_recvmsg(rep, &cmd, 0) == 0); + TEST_CHECK(nng_msg_len(cmd) == 4); + TEST_CHECK(strcmp(nng_msg_body(cmd), "def") == 0); + + // And send it back to REQ. + TEST_CHECK(nng_sendmsg(rep, cmd, 0) == 0); + + // Try a req command. This should give back "def" + TEST_CHECK(nng_recvmsg(req, &cmd, 0) == 0); + TEST_CHECK(nng_msg_len(cmd) == 4); + TEST_CHECK(strcmp(nng_msg_body(cmd), "def") == 0); + nng_msg_free(cmd); + + nng_aio_free(aio); + TEST_CHECK(nng_close(req) == 0); + TEST_CHECK(nng_close(rep) == 0); +} + +TEST_LIST = { + { "req rep identity", test_req_rep_identity }, + { "resend option", test_resend_option }, + { "req recv bad state", test_req_recv_bad_state }, + { "rep send bad state", test_rep_send_bad_state }, + { "req rep exchange", test_req_rep_exchange }, + { "req cancel", test_req_cancel }, + { "req cancel abort recv", test_req_cancel_abort_recv }, + { NULL, NULL }, +}; diff --git a/src/supplemental/base64/CMakeLists.txt b/src/supplemental/base64/CMakeLists.txt index 83740c83..b4c92e6a 100644 --- a/src/supplemental/base64/CMakeLists.txt +++ b/src/supplemental/base64/CMakeLists.txt @@ -12,5 +12,6 @@ if (NNG_SUPP_BASE64) set(_SRCS supplemental/base64/base64.c supplemental/base64/base64.h) + nng_test(base64_test) set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) endif() diff --git a/src/supplemental/base64/base64_test.c b/src/supplemental/base64/base64_test.c new file mode 100644 index 00000000..2c79a243 --- /dev/null +++ b/src/supplemental/base64/base64_test.c @@ -0,0 +1,106 @@ +// +// 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. +// + +#include <string.h> + +#include <nng/nng.h> + +#include "base64.h" + +#include <acutest.h> + +typedef struct { + char *decoded; + char *encoded; +} test_case; + +static test_case cases[] = { + { "", "" }, + { "f", "Zg==" }, + { "fo", "Zm8=" }, + { "foo", "Zm9v" }, + { "foob", "Zm9vYg==" }, + { "fooba", "Zm9vYmE=" }, + { "foobar", "Zm9vYmFy" }, + { NULL, NULL }, +}; + +void +test_encode(void) +{ + int i; + void *dec; + + for (i = 0; (dec = cases[i].decoded) != NULL; i++) { + char buf[1024]; + char name[8]; + int rv; + + (void) snprintf(name, sizeof(name), "%d", i); + TEST_CASE(name); + rv = nni_base64_encode(dec, strlen(dec), buf, 1024); + TEST_CHECK(rv >= 0); + TEST_CHECK(rv == (int) strlen(cases[i].encoded)); + buf[rv] = 0; + TEST_CHECK(strcmp(buf, cases[i].encoded) == 0); + } +} + +void +test_decode(void) +{ + int i; + void *enc; + + for (i = 0; (enc = cases[i].encoded) != NULL; i++) { + char buf[1024]; + char name[8]; + int rv; + + (void) snprintf(name, sizeof(name), "%d", i); + TEST_CASE(name); + + rv = nni_base64_decode(enc, strlen(enc), (void *) buf, 1024); + TEST_CHECK(rv >= 0); + TEST_CHECK(rv == (int) strlen(cases[i].decoded)); + buf[rv] = 0; + TEST_CHECK(strcmp(buf, cases[i].decoded) == 0); + } +} + +void +test_overflow(void) +{ + char tmp[1024]; + for (int i = 1; cases[i].decoded != NULL; i++) { + void *enc = cases[i].encoded; + void *dec = cases[i].decoded; + void *buf = tmp; + + char name[8]; + (void) snprintf(name, sizeof(name), "%d", i); + TEST_CASE(name); + + TEST_CHECK(nni_base64_encode( + dec, strlen(dec), buf, strlen(enc) - 1) == -1); + TEST_CHECK(nni_base64_encode(dec, strlen(dec), buf, 0) == -1); + + TEST_CHECK(nni_base64_decode( + enc, strlen(enc), buf, strlen(dec) - 1) == -1); + TEST_CHECK(nni_base64_encode(enc, strlen(enc), buf, 0) == -1); + } +} + +TEST_LIST = { + { "encode", test_encode }, + { "decode", test_decode }, + { "overflow", test_overflow }, + { NULL, NULL }, +}; diff --git a/src/supplemental/sha1/CMakeLists.txt b/src/supplemental/sha1/CMakeLists.txt index 370dc7c2..de301b08 100644 --- a/src/supplemental/sha1/CMakeLists.txt +++ b/src/supplemental/sha1/CMakeLists.txt @@ -1,6 +1,6 @@ # +# Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> # Copyright 2017 Capitar IT Group BV <info@capitar.com> -# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> # # This software is supplied under the terms of the MIT License, a # copy of which should be located in the distribution where this @@ -8,9 +8,5 @@ # found online at https://opensource.org/licenses/MIT. # -if (NNG_SUPP_SHA1) - set(_SRCS - supplemental/sha1/sha1.c - supplemental/sha1/sha1.h) - set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) -endif() +nng_sources_if(NNG_SUPP_SHA1 sha1.c sha1.h) +nng_test(sha1_test) diff --git a/tests/sha1.c b/src/supplemental/sha1/sha1_test.c index 6877e89e..505175b0 100644 --- a/tests/sha1.c +++ b/src/supplemental/sha1/sha1_test.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// 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 @@ -13,8 +13,10 @@ #include <nng/nng.h> -#include "convey.h" -#include "supplemental/sha1/sha1.h" +#include <acutest.h> + +#include "sha1.h" + // The following test vectors are from RFC 3174. #define TEST1 "abc" @@ -36,26 +38,35 @@ char *resultarray[4] = { "DE A3 56 A2 CD DD 90 C7 A7 EC ED C5 EB B5 63 93 4F 46 04 52" }; -TestMain("SHA1 Verification", { - Convey("SHA1 Works", { - for (int i = 0; i < 4; i++) { - nni_sha1_ctx ctx; - size_t slen = strlen(testarray[i]); - uint8_t digest[20]; - char strout[20 * 3 + 1]; - memset(digest, 0, sizeof(digest)); - nni_sha1_init(&ctx); - for (int j = 0; j < repeatcount[i]; j++) { - nni_sha1_update( - &ctx, (uint8_t *) testarray[i], slen); - } - nni_sha1_final(&ctx, digest); - for (int j = 0; j < 20; j++) { - snprintf( - strout + j * 3, 4, "%02X ", digest[j]); - } - strout[20 * 3 - 1] = '\0'; - So(strcmp(strout, resultarray[i]) == 0); +void +test_sha1(void) +{ + + for (int i = 0; i < 4; i++) { + nni_sha1_ctx ctx; + size_t slen = strlen(testarray[i]); + uint8_t digest[20]; + char strout[20 * 3 + 1]; + char name[8]; + + snprintf(name, sizeof(name), "%d", i); + TEST_CASE(name); + + memset(digest, 0, sizeof(digest)); + nni_sha1_init(&ctx); + for (int j = 0; j < repeatcount[i]; j++) { + nni_sha1_update(&ctx, (uint8_t *) testarray[i], slen); + } + nni_sha1_final(&ctx, digest); + for (int j = 0; j < 20; j++) { + snprintf(strout + j * 3, 4, "%02X ", digest[j]); } - }); -}) + strout[20 * 3 - 1] = '\0'; + TEST_CHECK(strcmp(strout, resultarray[i]) == 0); + } +} + +TEST_LIST = { + { "sha1", test_sha1 }, + { NULL, NULL }, +}; diff --git a/src/transport/inproc/CMakeLists.txt b/src/transport/inproc/CMakeLists.txt index bab4cd7e..3249649e 100644 --- a/src/transport/inproc/CMakeLists.txt +++ b/src/transport/inproc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +# 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 @@ -12,10 +12,6 @@ option (NNG_TRANSPORT_INPROC "Enable inproc transport." ON) mark_as_advanced(NNG_TRANSPORT_INPROC) -if (NNG_TRANSPORT_INPROC) - set(_SRCS transport/inproc/inproc.c ${PROJECT_SOURCE_DIR}/include/nng/transport/inproc/inproc.h) - set(_DEFS -DNNG_TRANSPORT_INPROC) - - set(NNG_SRCS ${NNG_SRCS} ${_SRCS} PARENT_SCOPE) - set(NNG_DEFS ${NNG_DEFS} ${_DEFS} PARENT_SCOPE) -endif() +nng_sources_if(NNG_TRANSPORT_INPROC inproc.c) +nng_headers_if(NNG_TRANSPORT_INPROC nng/transport/inproc/inproc.h) +nng_defines_if(NNG_TRANSPORT_INPROC NNG_TRANSPORT_INPROC) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 60bfed2e..984859bd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +# Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> # Copyright 2018 Capitar IT Group BV <info@capitar.com> # Copyright (c) 2012 Martin Sustrik All rights reserved. # Copyright (c) 2013 GoPivotal, Inc. All rights reserved. @@ -40,107 +40,109 @@ include_directories(AFTER SYSTEM ${PROJECT_SOURCE_DIR}/src) if (NOT WIN32) find_package(Threads) set(THRLIB Threads::Threads) -endif() +endif () if (NNG_TESTS) nng_check_sym(nl_langinfo langinfo.h NNG_HAVE_LANGINFO) if (NOT NNG_SANITIZER STREQUAL "none") set(TIMEOUT_FACTOR 2) - else() - set (TIMEOUT_FACTOR 1) - endif() - set (NNG_TEST_PORT 13000) - macro (add_nng_test NAME TIMEOUT) - add_executable (${NAME} ${NAME}.c convey.c) - target_link_libraries (${NAME} ${PROJECT_NAME} ${THRLIB}) - add_test (NAME ${NAME} COMMAND ${NAME} -v -p TEST_PORT=${NNG_TEST_PORT}) - math (EXPR TIMEOUT ${TIMEOUT}*${TIMEOUT_FACTOR}) - set_tests_properties (${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) - math (EXPR NNG_TEST_PORT "${NNG_TEST_PORT}+20") - endmacro (add_nng_test) + else () + set(TIMEOUT_FACTOR 1) + endif () + set(NNG_TEST_PORT 13000) + macro(add_nng_test NAME TIMEOUT) + add_executable(${NAME} ${NAME}.c convey.c) + target_link_libraries(${NAME} ${PROJECT_NAME} ${THRLIB}) + add_test(NAME ${NAME} COMMAND ${NAME} -v -p TEST_PORT=${NNG_TEST_PORT}) + math(EXPR TIMEOUT ${TIMEOUT}*${TIMEOUT_FACTOR}) + set_tests_properties(${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) + math(EXPR NNG_TEST_PORT "${NNG_TEST_PORT}+20") + endmacro(add_nng_test) # Compatibility tests are only added if all of the legacy protocols # are present. It's not worth trying to figure out which of these # should work and which shouldn't. if (NNG_PROTO_BUS0 AND - NNG_PROTO_PAIR0 AND - NNG_PROTO_REQ0 AND - NNG_PROTO_REP0 AND - NNG_PROTO_PUB0 AND - NNG_PROTO_SUB0 AND - NNG_PROTO_PUSH0 AND - NNG_PROTO_PULL0) - - macro (add_nng_compat_test NAME TIMEOUT) - add_executable (${NAME} ${NAME}.c compat_testutil.c) - target_link_libraries (${NAME} ${PROJECT_NAME}) + NNG_PROTO_PAIR0 AND + NNG_PROTO_REQ0 AND + NNG_PROTO_REP0 AND + NNG_PROTO_PUB0 AND + NNG_PROTO_SUB0 AND + NNG_PROTO_PUSH0 AND + NNG_PROTO_PULL0) + + macro(add_nng_compat_test NAME TIMEOUT) + add_executable(${NAME} ${NAME}.c compat_testutil.c) + target_link_libraries(${NAME} ${PROJECT_NAME}) target_include_directories(${NAME} PUBLIC - ${PROJECT_SOURCE_DIR}/src/compat) + ${PROJECT_SOURCE_DIR}/src/compat) - add_test (NAME ${NAME} COMMAND ${NAME} ${NNG_TEST_PORT}) - set_tests_properties (${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) - math (EXPR NNG_TEST_PORT "${NNG_TEST_PORT}+20") - endmacro (add_nng_compat_test) + add_test(NAME ${NAME} COMMAND ${NAME} ${NNG_TEST_PORT}) + set_tests_properties(${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) + math(EXPR NNG_TEST_PORT "${NNG_TEST_PORT}+20") + endmacro(add_nng_compat_test) else () - macro (add_nng_compat_test NAME TIMEOUT) - endmacro (add_nng_compat_test) - message (STATUS "Compatibility tests disabled (unconfigured protocols)") + macro(add_nng_compat_test NAME TIMEOUT) + endmacro(add_nng_compat_test) + message(STATUS "Compatibility tests disabled (unconfigured protocols)") endif () - macro (add_nng_cpp_test NAME TIMEOUT) + macro(add_nng_cpp_test NAME TIMEOUT) if (NOT NNG_ENABLE_COVERAGE) - enable_language (CXX) - add_executable (${NAME} ${NAME}.cc) - target_link_libraries (${NAME} ${PROJECT_NAME}) - add_test (NAME ${NAME} COMMAND ${NAME} ${TEST_PORT}) - set_tests_properties (${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) - math (EXPR TEST_PORT "${NNG_TEST_PORT}+20") - endif() - endmacro (add_nng_cpp_test) - - macro (add_nng_test1 NAME TIMEOUT COND1) + enable_language(CXX) + add_executable(${NAME} ${NAME}.cc) + target_link_libraries(${NAME} ${PROJECT_NAME}) + add_test(NAME ${NAME} COMMAND ${NAME} ${TEST_PORT}) + set_tests_properties(${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) + math(EXPR TEST_PORT "${NNG_TEST_PORT}+20") + endif () + endmacro(add_nng_cpp_test) + + macro(add_nng_test1 NAME TIMEOUT COND1) if (${COND1}) add_nng_test(${NAME} ${TIMEOUT}) - else() - message (STATUS "Test ${NAME} disabled (unconfigured)") - endif() + else () + message(STATUS "Test ${NAME} disabled (unconfigured)") + endif () endmacro() - macro (add_nng_test2 NAME TIMEOUT COND1 COND2) + macro(add_nng_test2 NAME TIMEOUT COND1 COND2) if (${COND1} AND ${COND2}) add_nng_test(${NAME} ${TIMEOUT}) - else() - message (STATUS "Test ${NAME} disabled (unconfigured)") - endif() + else () + message(STATUS "Test ${NAME} disabled (unconfigured)") + endif () endmacro() - macro (add_nng_test3 NAME TIMEOUT COND1 COND2 COND3) + macro(add_nng_test3 NAME TIMEOUT COND1 COND2 COND3) if (${COND1} AND ${COND2} AND ${COND3}) add_nng_test(${NAME} ${TIMEOUT} ON) - else() - message (STATUS "Test ${NAME} disabled (unconfigured)") - endif() + else () + message(STATUS "Test ${NAME} disabled (unconfigured)") + endif () endmacro() else () - macro (add_nng_test NAME TIMEOUT) - endmacro (add_nng_test) - macro (add_nng_compat_test NAME TIMEOUT) - endmacro (add_nng_compat_test) - macro (add_nng_cpp_test NAME TIMEOUT) - endmacro (add_nng_cpp_test) - macro (add_nng_test1 NAME TIMEOUT COND1) + macro(add_nng_test NAME TIMEOUT) + endmacro(add_nng_test) + macro(add_nng_compat_test NAME TIMEOUT) + endmacro(add_nng_compat_test) + macro(add_nng_cpp_test NAME TIMEOUT) + endmacro(add_nng_cpp_test) + macro(add_nng_test1 NAME TIMEOUT COND1) endmacro(add_nng_test1) - macro (add_nng_test2 NAME TIMEOUT COND1 COND2) + macro(add_nng_test2 NAME TIMEOUT COND1 COND2) endmacro(add_nng_test2) - macro (add_nng_test3 NAME TIMEOUT COND1 COND2 COND3) + macro(add_nng_test3 NAME TIMEOUT COND1 COND2 COND3) endmacro(add_nng_test3) endif () -add_nng_test(aio 5) -add_nng_test2(base64 5 NNG_STATIC_LIB NNG_SUPP_BASE64) -add_nng_test1(bufsz 5 NNG_PROTO_PAIR0) +nng_test(aio) +nng_test(bufsz) +nng_test(platform) +nng_test(sock) + add_nng_test(device 5) add_nng_test(errors 2) add_nng_test1(files 5 NNG_STATIC_LIB) @@ -158,15 +160,12 @@ add_nng_test(multistress 60) add_nng_test(nonblock 60) add_nng_test(options 5) add_nng_test(pipe 5) -add_nng_test(platform 5) add_nng_test(pollfd 5) add_nng_test(pubsubpollfd 5) add_nng_test(reconnect 5) add_nng_test1(resolv 10 NNG_STATIC_LIB) add_nng_test(scalability 20 ON) add_nng_test(set_recvmaxsize 2) -add_nng_test2(sha1 5 NNG_STATIC_LIB NNG_SUPP_SHA1) -add_nng_test(sock 5) add_nng_test1(stats 5 NNG_ENABLE_STATS) add_nng_test1(synch 5 NNG_STATIC_LIB) add_nng_test2(tls 60 NNG_STATIC_LIB NNG_TRANSPORT_TLS) @@ -184,18 +183,16 @@ add_nng_test1(zt 60 NNG_TRANSPORT_ZEROTIER) add_nng_test1(bus 5 NNG_PROTO_BUS0) add_nng_test2(pipeline 5 NNG_PROTO_PULL0 NNG_PROTO_PUSH0) -add_nng_test1(pair1 5 NNG_PROTO_PAIR1) add_nng_test2(pubsub 5 NNG_PROTO_PUB0 NNG_PROTO_SUB0) add_nng_test2(reqctx 5 NNG_PROTO_REQ0 NNG_PROTO_REP0) add_nng_test2(reqpoll 5 NNG_PROTO_REQ0 NNG_PROTO_REP0) -add_nng_test2(reqrep 5 NNG_PROTO_REQ0 NNG_PROTO_REP0) add_nng_test2(reqstress 60 NNG_PROTO_REQ0 NNG_PROTO_REP0) add_nng_test2(respondpoll 5 NNG_PROTO_SURVEYOR0 NNG_PROTO_RESPONDENT0) add_nng_test2(survey 5 NNG_PROTO_SURVEYOR0 NNG_PROTO_RESPONDENT0) add_nng_test2(surveyctx 5 NNG_PROTO_SURVEYOR0 NNG_PROTO_RESPONDENT0) add_nng_test2(surveypoll 5 NNG_PROTO_SURVEYOR0 NNG_PROTO_RESPONDENT0) -# compatbility tests +# compatibility tests # We only support these if ALL the legacy protocols are supported. This # is because we don't want to make modifications to partially enable some # of these tests. Folks minimizing the library probably don't care too diff --git a/tests/acutest.h b/tests/acutest.h new file mode 100644 index 00000000..26d961e7 --- /dev/null +++ b/tests/acutest.h @@ -0,0 +1,1650 @@ +/* + * Acutest -- Another C/C++ Unit Test facility + * <http://github.com/mity/acutest> + * + * Copyright (c) 2013-2019 Martin Mitas + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef ACUTEST_H__ +#define ACUTEST_H__ + + +/************************ + *** Public interface *** + ************************/ + +/* By default, "acutest.h" provides the main program entry point (function + * main()). However, if the test suite is composed of multiple source files + * which include "acutest.h", then this causes a problem of multiple main() + * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all + * compilation units but one. + */ + +/* Macro to specify list of unit tests in the suite. + * The unit test implementation MUST provide list of unit tests it implements + * with this macro: + * + * TEST_LIST = { + * { "test1_name", test1_func_ptr }, + * { "test2_name", test2_func_ptr }, + * ... + * { 0 } + * }; + * + * The list specifies names of each test (must be unique) and pointer to + * a function implementing it. The function does not take any arguments + * and has no return values, i.e. every test function has to be compatible + * with this prototype: + * + * void test_func(void); + */ +#define TEST_LIST const struct test__ test_list__[] + + +/* Macros for testing whether an unit test succeeds or fails. These macros + * can be used arbitrarily in functions implementing the unit tests. + * + * If any condition fails throughout execution of a test, the test fails. + * + * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows + * also to specify an error message to print out if the condition fails. + * (It expects printf-like format string and its parameters). The macros + * return non-zero (condition passes) or 0 (condition fails). + * + * That can be useful when more conditions should be checked only if some + * preceding condition passes, as illustrated in this code snippet: + * + * SomeStruct* ptr = allocate_some_struct(); + * if(TEST_CHECK(ptr != NULL)) { + * TEST_CHECK(ptr->member1 < 100); + * TEST_CHECK(ptr->member2 > 200); + * } + */ +#define TEST_CHECK_(cond,...) test_check__((cond), __FILE__, __LINE__, __VA_ARGS__) +#define TEST_CHECK(cond) test_check__((cond), __FILE__, __LINE__, "%s", #cond) + + +/* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the + * condition fails, the currently executed unit test is immediately aborted. + * + * That is done either by calling abort() if the unit test is executed as a + * child process; or via longjmp() if the unit test is executed within the + * main Acutest process. + * + * As a side effect of such abortion, your unit tests may cause memory leaks, + * unflushed file descriptors, and other fenomena caused by the abortion. + * + * Therefore you should not use these as a general replacement for TEST_CHECK. + * Use it with some caution, especially if your test causes some other side + * effects to the outside world (e.g. communicating with some server, inserting + * into a database etc.). + */ +#define TEST_ASSERT_(cond,...) \ + do { \ + if (!test_check__((cond), __FILE__, __LINE__, __VA_ARGS__)) \ + test_abort__(); \ + } while(0) +#define TEST_ASSERT(cond) \ + do { \ + if (!test_check__((cond), __FILE__, __LINE__, "%s", #cond)) \ + test_abort__(); \ + } while(0) + + +#ifdef __cplusplus +/* Macros to verify that the code (the 1st argument) throws exception of given + * type (the 2nd argument). (Note these macros are only available in C++.) + * + * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like + * message. + * + * For example: + * + * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType); + * + * If the function_that_throw() throws ExpectedExceptionType, the check passes. + * If the function throws anything incompatible with ExpectedExceptionType + * (or if it does not thrown an exception at all), the check fails. + */ +#define TEST_EXCEPTION(code, exctype) \ + do { \ + bool exc_ok__ = false; \ + const char *msg__ = NULL; \ + try { \ + code; \ + msg__ = "No exception thrown."; \ + } catch(exctype const&) { \ + exc_ok__= true; \ + } catch(...) { \ + msg__ = "Unexpected exception thrown."; \ + } \ + test_check__(exc_ok__, __FILE__, __LINE__, #code " throws " #exctype); \ + if(msg__ != NULL) \ + test_message__("%s", msg__); \ + } while(0) +#define TEST_EXCEPTION_(code, exctype, ...) \ + do { \ + bool exc_ok__ = false; \ + const char *msg__ = NULL; \ + try { \ + code; \ + msg__ = "No exception thrown."; \ + } catch(exctype const&) { \ + exc_ok__= true; \ + } catch(...) { \ + msg__ = "Unexpected exception thrown."; \ + } \ + test_check__(exc_ok__, __FILE__, __LINE__, __VA_ARGS__); \ + if(msg__ != NULL) \ + test_message__("%s", msg__); \ + } while(0) +#endif /* #ifdef __cplusplus */ + + +/* Sometimes it is useful to split execution of more complex unit tests to some + * smaller parts and associate those parts with some names. + * + * This is especially handy if the given unit test is implemented as a loop + * over some vector of multiple testing inputs. Using these macros allow to use + * sort of subtitle for each iteration of the loop (e.g. outputting the input + * itself or a name associated to it), so that if any TEST_CHECK condition + * fails in the loop, it can be easily seen which iteration triggers the + * failure, without the need to manually output the iteration-specific data in + * every single TEST_CHECK inside the loop body. + * + * TEST_CASE allows to specify only single string as the name of the case, + * TEST_CASE_ provides all the power of printf-like string formatting. + * + * Note that the test cases cannot be nested. Starting a new test case ends + * implicitly the previous one. To end the test case explicitly (e.g. to end + * the last test case after exiting the loop), you may use TEST_CASE(NULL). + */ +#define TEST_CASE_(...) test_case__(__VA_ARGS__) +#define TEST_CASE(name) test_case__("%s", name) + + +/* printf-like macro for outputting an extra information about a failure. + * + * Intended use is to output some computed output versus the expected value, + * e.g. like this: + * + * if(!TEST_CHECK(produced == expected)) { + * TEST_MSG("Expected: %d", expected); + * TEST_MSG("Produced: %d", produced); + * } + * + * Note the message is only written down if the most recent use of any checking + * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed. + * This means the above is equivalent to just this: + * + * TEST_CHECK(produced == expected); + * TEST_MSG("Expected: %d", expected); + * TEST_MSG("Produced: %d", produced); + * + * The macro can deal with multi-line output fairly well. It also automatically + * adds a final new-line if there is none present. + */ +#define TEST_MSG(...) test_message__(__VA_ARGS__) + + +/* Maximal output per TEST_MSG call. Longer messages are cut. + * You may define another limit prior including "acutest.h" + */ +#ifndef TEST_MSG_MAXSIZE + #define TEST_MSG_MAXSIZE 1024 +#endif + + +/* Macro for dumping a block of memory. + * + * Its intended use is very similar to what TEST_MSG is for, but instead of + * generating any printf-like message, this is for dumping raw block of a + * memory in a hexadecimal form: + * + * TEST_CHECK(size_produced == size_expected && memcmp(addr_produced, addr_expected, size_produced) == 0); + * TEST_DUMP("Expected:", addr_expected, size_expected); + * TEST_DUMP("Produced:", addr_produced, size_produced); + */ +#define TEST_DUMP(title, addr, size) test_dump__(title, addr, size) + +/* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut. + * You may define another limit prior including "acutest.h" + */ +#ifndef TEST_DUMP_MAXSIZE + #define TEST_DUMP_MAXSIZE 1024 +#endif + + +/********************** + *** Implementation *** + **********************/ + +/* The unit test files should not rely on anything below. */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> + +#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) + #define ACUTEST_UNIX__ 1 + #include <errno.h> + #include <libgen.h> + #include <unistd.h> + #include <sys/types.h> + #include <sys/wait.h> + #include <signal.h> + #include <time.h> + + #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC + #define ACUTEST_HAS_POSIX_TIMER__ 1 + #endif +#endif + +#if defined(__gnu_linux__) + #define ACUTEST_LINUX__ 1 + #include <fcntl.h> + #include <sys/stat.h> +#endif + +#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define ACUTEST_WIN__ 1 + #include <windows.h> + #include <io.h> +#endif + +#ifdef __cplusplus + #include <exception> +#endif + + +/* Note our global private identifiers end with '__' to mitigate risk of clash + * with the unit tests implementation. */ + + +#ifdef __cplusplus + extern "C" { +#endif + + +struct test__ { + const char* name; + void (*func)(void); +}; + +struct test_detail__ { + unsigned char flags; + double duration; +}; + +enum { + TEST_FLAG_RUN__ = 1 << 0, + TEST_FLAG_SUCCESS__ = 1 << 1, + TEST_FLAG_FAILURE__ = 1 << 2, +}; + +extern const struct test__ test_list__[]; + +int test_check__(int cond, const char* file, int line, const char* fmt, ...); +void test_case__(const char* fmt, ...); +void test_message__(const char* fmt, ...); +void test_dump__(const char* title, const void* addr, size_t size); +void test_abort__(void); + + +#ifndef TEST_NO_MAIN + +static char* test_argv0__ = NULL; +static size_t test_list_size__ = 0; +static struct test_detail__ *test_details__ = NULL; +static size_t test_count__ = 0; +static int test_no_exec__ = -1; +static int test_no_summary__ = 0; +static int test_tap__ = 0; +static int test_skip_mode__ = 0; +static int test_worker__ = 0; +static int test_worker_index__ = 0; +static int test_cond_failed__ = 0; +static FILE *test_xml_output__ = NULL; + +static int test_stat_failed_units__ = 0; +static int test_stat_run_units__ = 0; + +static const struct test__* test_current_unit__ = NULL; +static int test_current_index__ = 0; +static char test_case_name__[64] = ""; +static int test_current_already_logged__ = 0; +static int test_case_current_already_logged__ = 0; +static int test_verbose_level__ = 2; +static int test_current_failures__ = 0; +static int test_colorize__ = 0; +static int test_timer__ = 0; + +static int test_abort_has_jmp_buf__ = 0; +static jmp_buf test_abort_jmp_buf__; + +#if defined ACUTEST_WIN__ + typedef LARGE_INTEGER test_timer_type__; + static LARGE_INTEGER test_timer_freq__; + static test_timer_type__ test_timer_start__; + static test_timer_type__ test_timer_end__; + + static void + test_timer_init__(void) + { + QueryPerformanceFrequency(&test_timer_freq__); + } + + static void + test_timer_get_time__(LARGE_INTEGER* ts) + { + QueryPerformanceCounter(ts); + } + + static double + test_timer_diff__(LARGE_INTEGER start, LARGE_INTEGER end) + { + double duration = (double)(end.QuadPart - start.QuadPart); + duration /= (double)test_timer_freq__.QuadPart; + return duration; + } + + static void + test_timer_print_diff__(void) + { + printf("%.6lf secs", test_timer_diff__(test_timer_start__, test_timer_end__)); + } +#elif defined ACUTEST_HAS_POSIX_TIMER__ + static clockid_t test_timer_id__; + typedef struct timespec test_timer_type__; + static test_timer_type__ test_timer_start__; + static test_timer_type__ test_timer_end__; + + static void + test_timer_init__(void) + { + if(test_timer__ == 1) + #ifdef CLOCK_MONOTONIC_RAWxx + /* linux specific; not subject of NTP adjustments or adjtime() */ + test_timer_id__ = CLOCK_MONOTONIC_RAW; + #else + test_timer_id__ = CLOCK_MONOTONIC; + #endif + else if(test_timer__ == 2) + test_timer_id__ = CLOCK_PROCESS_CPUTIME_ID; + } + + static void + test_timer_get_time__(struct timespec* ts) + { + clock_gettime(test_timer_id__, ts); + } + + static double + test_timer_diff__(struct timespec start, struct timespec end) + { + double endns; + double startns; + + endns = end.tv_sec; + endns *= 10e9; + endns += end.tv_nsec; + + startns = start.tv_sec; + startns *= 10e9; + startns += start.tv_nsec; + + return ((endns - startns)/ 10e9); + } + + static void + test_timer_print_diff__(void) + { + printf("%.6lf secs", + test_timer_diff__(test_timer_start__, test_timer_end__)); + } +#else + typedef int test_timer_type__; + static test_timer_type__ test_timer_start__; + static test_timer_type__ test_timer_end__; + + void + test_timer_init__(void) + {} + + static void + test_timer_get_time__(int* ts) + { + (void) ts; + } + + static double + test_timer_diff__(int start, int end) + { + (void) start; + (void) end; + return 0.0; + } + + static void + test_timer_print_diff__(void) + {} +#endif + +#define TEST_COLOR_DEFAULT__ 0 +#define TEST_COLOR_GREEN__ 1 +#define TEST_COLOR_RED__ 2 +#define TEST_COLOR_DEFAULT_INTENSIVE__ 3 +#define TEST_COLOR_GREEN_INTENSIVE__ 4 +#define TEST_COLOR_RED_INTENSIVE__ 5 + +static int +test_print_in_color__(int color, const char* fmt, ...) +{ + va_list args; + char buffer[256]; + int n; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + buffer[sizeof(buffer)-1] = '\0'; + + if(!test_colorize__) { + return printf("%s", buffer); + } + +#if defined ACUTEST_UNIX__ + { + const char* col_str; + switch(color) { + case TEST_COLOR_GREEN__: col_str = "\033[0;32m"; break; + case TEST_COLOR_RED__: col_str = "\033[0;31m"; break; + case TEST_COLOR_GREEN_INTENSIVE__: col_str = "\033[1;32m"; break; + case TEST_COLOR_RED_INTENSIVE__: col_str = "\033[1;31m"; break; + case TEST_COLOR_DEFAULT_INTENSIVE__: col_str = "\033[1m"; break; + default: col_str = "\033[0m"; break; + } + printf("%s", col_str); + n = printf("%s", buffer); + printf("\033[0m"); + return n; + } +#elif defined ACUTEST_WIN__ + { + HANDLE h; + CONSOLE_SCREEN_BUFFER_INFO info; + WORD attr; + + h = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(h, &info); + + switch(color) { + case TEST_COLOR_GREEN__: attr = FOREGROUND_GREEN; break; + case TEST_COLOR_RED__: attr = FOREGROUND_RED; break; + case TEST_COLOR_GREEN_INTENSIVE__: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; + case TEST_COLOR_RED_INTENSIVE__: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; + case TEST_COLOR_DEFAULT_INTENSIVE__: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; + default: attr = 0; break; + } + if(attr != 0) + SetConsoleTextAttribute(h, attr); + n = printf("%s", buffer); + SetConsoleTextAttribute(h, info.wAttributes); + return n; + } +#else + n = printf("%s", buffer); + return n; +#endif +} + +static void +test_begin_test_line__(const struct test__* test) +{ + if(!test_tap__) { + if(test_verbose_level__ >= 3) { + test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s:\n", test->name); + test_current_already_logged__++; + } else if(test_verbose_level__ >= 1) { + int n; + char spaces[48]; + + n = test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Test %s... ", test->name); + memset(spaces, ' ', sizeof(spaces)); + if(n < (int) sizeof(spaces)) + printf("%.*s", (int) sizeof(spaces) - n, spaces); + } else { + test_current_already_logged__ = 1; + } + } +} + +static void +test_finish_test_line__(int result) +{ + if(test_tap__) { + const char* str = (result == 0) ? "ok" : "not ok"; + + printf("%s %u - %s\n", str, test_current_index__ + 1, test_current_unit__->name); + + if(result == 0 && test_timer__) { + printf("# Duration: "); + test_timer_print_diff__(); + printf("\n"); + } + } else { + int color = (result == 0) ? TEST_COLOR_GREEN_INTENSIVE__ : TEST_COLOR_RED_INTENSIVE__; + const char* str = (result == 0) ? "OK" : "FAILED"; + printf("[ "); + test_print_in_color__(color, str); + printf(" ]"); + + if(result == 0 && test_timer__) { + printf(" "); + test_timer_print_diff__(); + } + + printf("\n"); + } +} + +static void +test_line_indent__(int level) +{ + static const char spaces[] = " "; + int n = level * 2; + + if(test_tap__ && n > 0) { + n--; + printf("#"); + } + + while(n > 16) { + printf("%s", spaces); + n -= 16; + } + printf("%.*s", n, spaces); +} + +int +test_check__(int cond, const char* file, int line, const char* fmt, ...) +{ + const char *result_str; + int result_color; + int verbose_level; + + if(cond) { + result_str = "ok"; + result_color = TEST_COLOR_GREEN__; + verbose_level = 3; + } else { + if(!test_current_already_logged__ && test_current_unit__ != NULL) + test_finish_test_line__(-1); + + result_str = "failed"; + result_color = TEST_COLOR_RED__; + verbose_level = 2; + test_current_failures__++; + test_current_already_logged__++; + } + + if(test_verbose_level__ >= verbose_level) { + va_list args; + + if(!test_case_current_already_logged__ && test_case_name__[0]) { + test_line_indent__(1); + test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); + test_current_already_logged__++; + test_case_current_already_logged__++; + } + + test_line_indent__(test_case_name__[0] ? 2 : 1); + if(file != NULL) { + if(test_verbose_level__ < 3) { +#ifdef ACUTEST_WIN__ + const char* lastsep1 = strrchr(file, '\\'); + const char* lastsep2 = strrchr(file, '/'); + if(lastsep1 == NULL) + lastsep1 = file-1; + if(lastsep2 == NULL) + lastsep2 = file-1; + file = (lastsep1 > lastsep2 ? lastsep1 : lastsep2) + 1; +#else + const char* lastsep = strrchr(file, '/'); + if(lastsep != NULL) + file = lastsep+1; +#endif + } + printf("%s:%d: Check ", file, line); + } + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + printf("... "); + test_print_in_color__(result_color, result_str); + printf("\n"); + test_current_already_logged__++; + } + + test_cond_failed__ = (cond == 0); + return !test_cond_failed__; +} + +void +test_case__(const char* fmt, ...) +{ + va_list args; + + if(test_verbose_level__ < 2) + return; + + if(test_case_name__[0]) { + test_case_current_already_logged__ = 0; + test_case_name__[0] = '\0'; + } + + if(fmt == NULL) + return; + + va_start(args, fmt); + vsnprintf(test_case_name__, sizeof(test_case_name__) - 1, fmt, args); + va_end(args); + test_case_name__[sizeof(test_case_name__) - 1] = '\0'; + + if(test_verbose_level__ >= 3) { + test_line_indent__(1); + test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Case %s:\n", test_case_name__); + test_current_already_logged__++; + test_case_current_already_logged__++; + } +} + +void +test_message__(const char* fmt, ...) +{ + char buffer[TEST_MSG_MAXSIZE]; + char* line_beg; + char* line_end; + va_list args; + + if(test_verbose_level__ < 2) + return; + + /* We allow extra message only when something is already wrong in the + * current test. */ + if(test_current_unit__ == NULL || !test_cond_failed__) + return; + + va_start(args, fmt); + vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); + va_end(args); + buffer[TEST_MSG_MAXSIZE-1] = '\0'; + + line_beg = buffer; + while(1) { + line_end = strchr(line_beg, '\n'); + if(line_end == NULL) + break; + test_line_indent__(test_case_name__[0] ? 3 : 2); + printf("%.*s\n", (int)(line_end - line_beg), line_beg); + line_beg = line_end + 1; + } + if(line_beg[0] != '\0') { + test_line_indent__(test_case_name__[0] ? 3 : 2); + printf("%s\n", line_beg); + } +} + +void +test_dump__(const char* title, const void* addr, size_t size) +{ + static const size_t BYTES_PER_LINE = 16; + size_t line_beg; + size_t truncate = 0; + + if(test_verbose_level__ < 2) + return; + + /* We allow extra message only when something is already wrong in the + * current test. */ + if(test_current_unit__ == NULL || !test_cond_failed__) + return; + + if(size > TEST_DUMP_MAXSIZE) { + truncate = size - TEST_DUMP_MAXSIZE; + size = TEST_DUMP_MAXSIZE; + } + + test_line_indent__(test_case_name__[0] ? 3 : 2); + printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title); + + for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) { + size_t line_end = line_beg + BYTES_PER_LINE; + size_t off; + + test_line_indent__(test_case_name__[0] ? 4 : 3); + printf("%08lx: ", (unsigned long)line_beg); + for(off = line_beg; off < line_end; off++) { + if(off < size) + printf(" %02x", ((unsigned char*)addr)[off]); + else + printf(" "); + } + + printf(" "); + for(off = line_beg; off < line_end; off++) { + unsigned char byte = ((unsigned char*)addr)[off]; + if(off < size) + printf("%c", (iscntrl(byte) ? '.' : byte)); + else + break; + } + + printf("\n"); + } + + if(truncate > 0) { + test_line_indent__(test_case_name__[0] ? 4 : 3); + printf(" ... (and more %u bytes)\n", (unsigned) truncate); + } +} + +void +test_abort__(void) +{ + if(test_abort_has_jmp_buf__) + longjmp(test_abort_jmp_buf__, 1); + else + abort(); +} + +static void +test_list_names__(void) +{ + const struct test__* test; + + printf("Unit tests:\n"); + for(test = &test_list__[0]; test->func != NULL; test++) + printf(" %s\n", test->name); +} + +static void +test_remember__(int i) +{ + if(test_details__[i].flags & TEST_FLAG_RUN__) + return; + + test_details__[i].flags |= TEST_FLAG_RUN__; + test_count__++; +} + +static void +test_set_success__(int i, int success) +{ + test_details__[i].flags |= success ? TEST_FLAG_SUCCESS__ : TEST_FLAG_FAILURE__; +} + +static void +test_set_duration__(int i, double duration) +{ + test_details__[i].duration = duration; +} + +static int +test_name_contains_word__(const char* name, const char* pattern) +{ + static const char word_delim[] = " \t-_."; + const char* substr; + size_t pattern_len; + int starts_on_word_boundary; + int ends_on_word_boundary; + + pattern_len = strlen(pattern); + + substr = strstr(name, pattern); + while(substr != NULL) { + starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); + ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); + + if(starts_on_word_boundary && ends_on_word_boundary) + return 1; + + substr = strstr(substr+1, pattern); + } + + return 0; +} + +static int +test_lookup__(const char* pattern) +{ + int i; + int n = 0; + + /* Try exact match. */ + for(i = 0; i < (int) test_list_size__; i++) { + if(strcmp(test_list__[i].name, pattern) == 0) { + test_remember__(i); + n++; + break; + } + } + if(n > 0) + return n; + + /* Try word match. */ + for(i = 0; i < (int) test_list_size__; i++) { + if(test_name_contains_word__(test_list__[i].name, pattern)) { + test_remember__(i); + n++; + } + } + if(n > 0) + return n; + + /* Try relaxed match. */ + for(i = 0; i < (int) test_list_size__; i++) { + if(strstr(test_list__[i].name, pattern) != NULL) { + test_remember__(i); + n++; + } + } + + return n; +} + + +/* Called if anything goes bad in Acutest, or if the unit test ends in other + * way then by normal returning from its function (e.g. exception or some + * abnormal child process termination). */ +static void +test_error__(const char* fmt, ...) +{ + va_list args; + + if(test_verbose_level__ == 0) + return; + + if(test_verbose_level__ <= 2 && !test_current_already_logged__ && test_current_unit__ != NULL) { + if(test_tap__) { + test_finish_test_line__(-1); + } else { + printf("[ "); + test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED"); + printf(" ]\n"); + } + } + + if(test_verbose_level__ >= 2) { + test_line_indent__(1); + if(test_verbose_level__ >= 3) + test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "ERROR: "); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); + } + + if(test_verbose_level__ >= 3) { + printf("\n"); + } +} + +/* Call directly the given test unit function. */ +static int +test_do_run__(const struct test__* test, int index) +{ + test_current_unit__ = test; + test_current_index__ = index; + test_current_failures__ = 0; + test_current_already_logged__ = 0; + test_cond_failed__ = 0; + + test_begin_test_line__(test); + +#ifdef __cplusplus + try { +#endif + + /* This is good to do for case the test unit e.g. crashes. */ + fflush(stdout); + fflush(stderr); + + if(!test_worker__) { + test_abort_has_jmp_buf__ = 1; + if(setjmp(test_abort_jmp_buf__) != 0) + goto aborted; + } + + test_timer_get_time__(&test_timer_start__); + test->func(); +aborted: + test_abort_has_jmp_buf__ = 0; + test_timer_get_time__(&test_timer_end__); + + if(test_verbose_level__ >= 3) { + test_line_indent__(1); + if(test_current_failures__ == 0) { + test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS: "); + printf("All conditions have passed.\n"); + + if(test_timer__) { + test_line_indent__(1); + printf("Duration: "); + test_timer_print_diff__(); + printf("\n"); + } + } else { + test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED: "); + printf("%d condition%s %s failed.\n", + test_current_failures__, + (test_current_failures__ == 1) ? "" : "s", + (test_current_failures__ == 1) ? "has" : "have"); + } + printf("\n"); + } else if(test_verbose_level__ >= 1 && test_current_failures__ == 0) { + test_finish_test_line__(0); + } + + test_case__(NULL); + test_current_unit__ = NULL; + return (test_current_failures__ == 0) ? 0 : -1; + +#ifdef __cplusplus + } catch(std::exception& e) { + const char* what = e.what(); + test_check__(0, NULL, 0, "Threw std::exception"); + if(what != NULL) + test_message__("std::exception::what(): %s", what); + return -1; + } catch(...) { + test_check__(0, NULL, 0, "Threw an exception"); + return -1; + } +#endif +} + +/* Trigger the unit test. If possible (and not suppressed) it starts a child + * process who calls test_do_run__(), otherwise it calls test_do_run__() + * directly. */ +static void +test_run__(const struct test__* test, int index, int master_index) +{ + int failed = 1; + test_timer_type__ start, end; + + test_current_unit__ = test; + test_current_already_logged__ = 0; + test_timer_get_time__(&start); + + if(!test_no_exec__) { + +#if defined(ACUTEST_UNIX__) + + pid_t pid; + int exit_code; + + /* Make sure the child starts with empty I/O buffers. */ + fflush(stdout); + fflush(stderr); + + pid = fork(); + if(pid == (pid_t)-1) { + test_error__("Cannot fork. %s [%d]", strerror(errno), errno); + failed = 1; + } else if(pid == 0) { + /* Child: Do the test. */ + test_worker__ = 1; + failed = (test_do_run__(test, index) != 0); + exit(failed ? 1 : 0); + } else { + /* Parent: Wait until child terminates and analyze its exit code. */ + waitpid(pid, &exit_code, 0); + if(WIFEXITED(exit_code)) { + switch(WEXITSTATUS(exit_code)) { + case 0: failed = 0; break; /* test has passed. */ + case 1: /* noop */ break; /* "normal" failure. */ + default: test_error__("Unexpected exit code [%d]", WEXITSTATUS(exit_code)); + } + } else if(WIFSIGNALED(exit_code)) { + char tmp[32]; + const char* signame; + switch(WTERMSIG(exit_code)) { + case SIGINT: signame = "SIGINT"; break; + case SIGHUP: signame = "SIGHUP"; break; + case SIGQUIT: signame = "SIGQUIT"; break; + case SIGABRT: signame = "SIGABRT"; break; + case SIGKILL: signame = "SIGKILL"; break; + case SIGSEGV: signame = "SIGSEGV"; break; + case SIGILL: signame = "SIGILL"; break; + case SIGTERM: signame = "SIGTERM"; break; + default: sprintf(tmp, "signal %d", WTERMSIG(exit_code)); signame = tmp; break; + } + test_error__("Test interrupted by %s", signame); + } else { + test_error__("Test ended in an unexpected way [%d]", exit_code); + } + } + +#elif defined(ACUTEST_WIN__) + + char buffer[512] = {0}; + STARTUPINFOA startupInfo; + PROCESS_INFORMATION processInfo; + DWORD exitCode; + + /* Windows has no fork(). So we propagate all info into the child + * through a command line arguments. */ + _snprintf(buffer, sizeof(buffer)-1, + "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", + test_argv0__, index, test_timer__ ? "--timer" : "", + test_tap__ ? "--tap" : "", test_verbose_level__, + test_colorize__ ? "always" : "never", + test->name); + memset(&startupInfo, 0, sizeof(startupInfo)); + startupInfo.cb = sizeof(STARTUPINFO); + if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { + WaitForSingleObject(processInfo.hProcess, INFINITE); + GetExitCodeProcess(processInfo.hProcess, &exitCode); + CloseHandle(processInfo.hThread); + CloseHandle(processInfo.hProcess); + failed = (exitCode != 0); + } else { + test_error__("Cannot create unit test subprocess [%ld].", GetLastError()); + failed = 1; + } + +#else + + /* A platform where we don't know how to run child process. */ + failed = (test_do_run__(test, index) != 0); + +#endif + + } else { + /* Child processes suppressed through --no-exec. */ + failed = (test_do_run__(test, index) != 0); + } + test_timer_get_time__(&end); + + test_current_unit__ = NULL; + + test_stat_run_units__++; + if(failed) + test_stat_failed_units__++; + + test_set_success__(master_index, !failed); + test_set_duration__(master_index, test_timer_diff__(start, end)); +} + +#if defined(ACUTEST_WIN__) +/* Callback for SEH events. */ +static LONG CALLBACK +test_seh_exception_filter__(EXCEPTION_POINTERS *ptrs) +{ + test_check__(0, NULL, 0, "Unhandled SEH exception"); + test_message__("Exception code: 0x%08lx", ptrs->ExceptionRecord->ExceptionCode); + test_message__("Exception address: 0x%p", ptrs->ExceptionRecord->ExceptionAddress); + + fflush(stdout); + fflush(stderr); + + return EXCEPTION_EXECUTE_HANDLER; +} +#endif + + +#define TEST_CMDLINE_OPTFLAG_OPTIONALARG__ 0x0001 +#define TEST_CMDLINE_OPTFLAG_REQUIREDARG__ 0x0002 + +#define TEST_CMDLINE_OPTID_NONE__ 0 +#define TEST_CMDLINE_OPTID_UNKNOWN__ (-0x7fffffff + 0) +#define TEST_CMDLINE_OPTID_MISSINGARG__ (-0x7fffffff + 1) +#define TEST_CMDLINE_OPTID_BOGUSARG__ (-0x7fffffff + 2) + +typedef struct TEST_CMDLINE_OPTION__ { + char shortname; + const char* longname; + int id; + unsigned flags; +} TEST_CMDLINE_OPTION__; + +static int +test_cmdline_handle_short_opt_group__(const TEST_CMDLINE_OPTION__* options, + const char* arggroup, + int (*callback)(int /*optval*/, const char* /*arg*/)) +{ + const TEST_CMDLINE_OPTION__* opt; + int i; + int ret = 0; + + for(i = 0; arggroup[i] != '\0'; i++) { + for(opt = options; opt->id != 0; opt++) { + if(arggroup[i] == opt->shortname) + break; + } + + if(opt->id != 0 && !(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { + ret = callback(opt->id, NULL); + } else { + /* Unknown option. */ + char badoptname[3]; + badoptname[0] = '-'; + badoptname[1] = arggroup[i]; + badoptname[2] = '\0'; + ret = callback((opt->id != 0 ? TEST_CMDLINE_OPTID_MISSINGARG__ : TEST_CMDLINE_OPTID_UNKNOWN__), + badoptname); + } + + if(ret != 0) + break; + } + + return ret; +} + +#define TEST_CMDLINE_AUXBUF_SIZE__ 32 + +static int +test_cmdline_read__(const TEST_CMDLINE_OPTION__* options, int argc, char** argv, + int (*callback)(int /*optval*/, const char* /*arg*/)) +{ + + const TEST_CMDLINE_OPTION__* opt; + char auxbuf[TEST_CMDLINE_AUXBUF_SIZE__+1]; + int after_doubledash = 0; + int i = 1; + int ret = 0; + + auxbuf[TEST_CMDLINE_AUXBUF_SIZE__] = '\0'; + + while(i < argc) { + if(after_doubledash || strcmp(argv[i], "-") == 0) { + /* Non-option argument. */ + ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); + } else if(strcmp(argv[i], "--") == 0) { + /* End of options. All the remaining members are non-option arguments. */ + after_doubledash = 1; + } else if(argv[i][0] != '-') { + /* Non-option argument. */ + ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); + } else { + for(opt = options; opt->id != 0; opt++) { + if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { + size_t len = strlen(opt->longname); + if(strncmp(argv[i]+2, opt->longname, len) == 0) { + /* Regular long option. */ + if(argv[i][2+len] == '\0') { + /* with no argument provided. */ + if(!(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) + ret = callback(opt->id, NULL); + else + ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); + break; + } else if(argv[i][2+len] == '=') { + /* with an argument provided. */ + if(opt->flags & (TEST_CMDLINE_OPTFLAG_OPTIONALARG__ | TEST_CMDLINE_OPTFLAG_REQUIREDARG__)) { + ret = callback(opt->id, argv[i]+2+len+1); + } else { + sprintf(auxbuf, "--%s", opt->longname); + ret = callback(TEST_CMDLINE_OPTID_BOGUSARG__, auxbuf); + } + break; + } else { + continue; + } + } + } else if(opt->shortname != '\0' && argv[i][0] == '-') { + if(argv[i][1] == opt->shortname) { + /* Regular short option. */ + if(opt->flags & TEST_CMDLINE_OPTFLAG_REQUIREDARG__) { + if(argv[i][2] != '\0') + ret = callback(opt->id, argv[i]+2); + else if(i+1 < argc) + ret = callback(opt->id, argv[++i]); + else + ret = callback(TEST_CMDLINE_OPTID_MISSINGARG__, argv[i]); + break; + } else { + ret = callback(opt->id, NULL); + + /* There might be more (argument-less) short options + * grouped together. */ + if(ret == 0 && argv[i][2] != '\0') + ret = test_cmdline_handle_short_opt_group__(options, argv[i]+2, callback); + break; + } + } + } + } + + if(opt->id == 0) { /* still not handled? */ + if(argv[i][0] != '-') { + /* Non-option argument. */ + ret = callback(TEST_CMDLINE_OPTID_NONE__, argv[i]); + } else { + /* Unknown option. */ + char* badoptname = argv[i]; + + if(strncmp(badoptname, "--", 2) == 0) { + /* Strip any argument from the long option. */ + char* assignment = strchr(badoptname, '='); + if(assignment != NULL) { + size_t len = assignment - badoptname; + if(len > TEST_CMDLINE_AUXBUF_SIZE__) + len = TEST_CMDLINE_AUXBUF_SIZE__; + strncpy(auxbuf, badoptname, len); + auxbuf[len] = '\0'; + badoptname = auxbuf; + } + } + + ret = callback(TEST_CMDLINE_OPTID_UNKNOWN__, badoptname); + } + } + } + + if(ret != 0) + return ret; + i++; + } + + return ret; +} + +static void +test_help__(void) +{ + printf("Usage: %s [options] [test...]\n", test_argv0__); + printf("\n"); + printf("Run the specified unit tests; or if the option '--skip' is used, run all\n"); + printf("tests in the suite but those listed. By default, if no tests are specified\n"); + printf("on the command line, all unit tests in the suite are run.\n"); + printf("\n"); + printf("Options:\n"); + printf(" -s, --skip Execute all unit tests but the listed ones\n"); + printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); + printf(" (WHEN is one of 'auto', 'always', 'never')\n"); +#if defined ACUTEST_WIN__ + printf(" -t, --timer Measure test duration\n"); +#elif defined ACUTEST_HAS_POSIX_TIMER__ + printf(" -t, --timer Measure test duration (real time)\n"); + printf(" --timer=TIMER Measure test duration, using given timer\n"); + printf(" (TIMER is one of 'real', 'cpu')\n"); +#endif + printf(" -E, --no-exec Same as --exec=never\n"); + printf(" --no-summary Suppress printing of test results summary\n"); + printf(" --tap Produce TAP-compliant output\n"); + printf(" (See https://testanything.org/)\n"); + printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n"); + printf(" -l, --list List unit tests in the suite and exit\n"); + printf(" -v, --verbose Make output more verbose\n"); + printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); + printf(" 0 ... Be silent\n"); + printf(" 1 ... Output one line per test (and summary)\n"); + printf(" 2 ... As 1 and failed conditions (this is default)\n"); + printf(" 3 ... As 1 and all conditions (and extended summary)\n"); + printf(" --color[=WHEN] Enable colorized output\n"); + printf(" (WHEN is one of 'auto', 'always', 'never')\n"); + printf(" --no-color Same as --color=never\n"); + printf(" -h, --help Display this help and exit\n"); + + if(test_list_size__ < 16) { + printf("\n"); + test_list_names__(); + } +} + +static const TEST_CMDLINE_OPTION__ test_cmdline_options__[] = { + { 's', "skip", 's', 0 }, + { 0, "exec", 'e', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, + { 'E', "no-exec", 'E', 0 }, +#if defined ACUTEST_WIN__ + { 't', "timer", 't', 0 }, +#elif defined ACUTEST_HAS_POSIX_TIMER__ + { 't', "timer", 't', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, +#endif + { 0, "no-summary", 'S', 0 }, + { 0, "tap", 'T', 0 }, + { 'l', "list", 'l', 0 }, + { 'v', "verbose", 'v', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, + { 0, "color", 'c', TEST_CMDLINE_OPTFLAG_OPTIONALARG__ }, + { 0, "no-color", 'C', 0 }, + { 'h', "help", 'h', 0 }, + { 0, "worker", 'w', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, /* internal */ + { 'x', "xml-output", 'x', TEST_CMDLINE_OPTFLAG_REQUIREDARG__ }, + { 0, NULL, 0, 0 } +}; + +static int +test_cmdline_callback__(int id, const char* arg) +{ + switch(id) { + case 's': + test_skip_mode__ = 1; + break; + + case 'e': + if(arg == NULL || strcmp(arg, "always") == 0) { + test_no_exec__ = 0; + } else if(strcmp(arg, "never") == 0) { + test_no_exec__ = 1; + } else if(strcmp(arg, "auto") == 0) { + /*noop*/ + } else { + fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", test_argv0__, arg); + fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); + exit(2); + } + break; + + case 'E': + test_no_exec__ = 1; + break; + + case 't': +#if defined ACUTEST_WIN__ || defined ACUTEST_HAS_POSIX_TIMER__ + if(arg == NULL || strcmp(arg, "real") == 0) { + test_timer__ = 1; + #ifndef ACUTEST_WIN__ + } else if(strcmp(arg, "cpu") == 0) { + test_timer__ = 2; + #endif + } else { + fprintf(stderr, "%s: Unrecognized argument '%s' for option --timer.\n", test_argv0__, arg); + fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); + exit(2); + } +#endif + break; + + case 'S': + test_no_summary__ = 1; + break; + + case 'T': + test_tap__ = 1; + break; + + case 'l': + test_list_names__(); + exit(0); + + case 'v': + test_verbose_level__ = (arg != NULL ? atoi(arg) : test_verbose_level__+1); + break; + + case 'c': + if(arg == NULL || strcmp(arg, "always") == 0) { + test_colorize__ = 1; + } else if(strcmp(arg, "never") == 0) { + test_colorize__ = 0; + } else if(strcmp(arg, "auto") == 0) { + /*noop*/ + } else { + fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", test_argv0__, arg); + fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); + exit(2); + } + break; + + case 'C': + test_colorize__ = 0; + break; + + case 'h': + test_help__(); + exit(0); + + case 'w': + test_worker__ = 1; + test_worker_index__ = atoi(arg); + break; + case 'x': + test_xml_output__ = fopen(arg, "w"); + if (!test_xml_output__) { + fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno)); + exit(2); + } + break; + + case 0: + if(test_lookup__(arg) == 0) { + fprintf(stderr, "%s: Unrecognized unit test '%s'\n", test_argv0__, arg); + fprintf(stderr, "Try '%s --list' for list of unit tests.\n", test_argv0__); + exit(2); + } + break; + + case TEST_CMDLINE_OPTID_UNKNOWN__: + fprintf(stderr, "Unrecognized command line option '%s'.\n", arg); + fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); + exit(2); + + case TEST_CMDLINE_OPTID_MISSINGARG__: + fprintf(stderr, "The command line option '%s' requires an argument.\n", arg); + fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); + exit(2); + + case TEST_CMDLINE_OPTID_BOGUSARG__: + fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg); + fprintf(stderr, "Try '%s --help' for more information.\n", test_argv0__); + exit(2); + } + + return 0; +} + + +#ifdef ACUTEST_LINUX__ +static int +test_is_tracer_present__(void) +{ + char buf[256+32+1]; + int tracer_present = 0; + int fd; + ssize_t n_read; + + fd = open("/proc/self/status", O_RDONLY); + if(fd == -1) + return 0; + + n_read = read(fd, buf, sizeof(buf)-1); + while(n_read > 0) { + static const char pattern[] = "TracerPid:"; + const char* field; + + buf[n_read] = '\0'; + field = strstr(buf, pattern); + if(field != NULL && field < buf + sizeof(buf) - 32) { + pid_t tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); + tracer_present = (tracer_pid != 0); + break; + } + + if(n_read == sizeof(buf)-1) { + memmove(buf, buf + sizeof(buf)-1 - 32, 32); + n_read = read(fd, buf+32, sizeof(buf)-1-32); + if(n_read > 0) + n_read += 32; + } + } + + close(fd); + return tracer_present; +} +#endif + +int +main(int argc, char** argv) +{ + int i; + test_argv0__ = argv[0]; + +#if defined ACUTEST_UNIX__ + test_colorize__ = isatty(STDOUT_FILENO); +#elif defined ACUTEST_WIN__ + #if defined __BORLANDC__ + test_colorize__ = isatty(_fileno(stdout)); + #else + test_colorize__ = _isatty(_fileno(stdout)); + #endif +#else + test_colorize__ = 0; +#endif + + test_timer_init__(); + + /* Count all test units */ + test_list_size__ = 0; + for(i = 0; test_list__[i].func != NULL; i++) + test_list_size__++; + + test_details__ = (struct test_detail__*)calloc(test_list_size__, sizeof(struct test_detail__)); + if(test_details__ == NULL) { + fprintf(stderr, "Out of memory.\n"); + exit(2); + } + + /* Parse options */ + test_cmdline_read__(test_cmdline_options__, argc, argv, test_cmdline_callback__); + +#if defined(ACUTEST_WIN__) + SetUnhandledExceptionFilter(test_seh_exception_filter__); +#endif + + /* By default, we want to run all tests. */ + if(test_count__ == 0) { + for(i = 0; test_list__[i].func != NULL; i++) + test_remember__(i); + } + + /* Guess whether we want to run unit tests as child processes. */ + if(test_no_exec__ < 0) { + test_no_exec__ = 0; + + if(test_count__ <= 1) { + test_no_exec__ = 1; + } else { +#ifdef ACUTEST_WIN__ + if(IsDebuggerPresent()) + test_no_exec__ = 1; +#endif +#ifdef ACUTEST_LINUX__ + if(test_is_tracer_present__()) + test_no_exec__ = 1; +#endif + } + } + + if(test_tap__) { + /* TAP requires we know test result ("ok", "not ok") before we output + * anything about the test, and this gets problematic for larger verbose + * levels. */ + if(test_verbose_level__ > 2) + test_verbose_level__ = 2; + + /* TAP harness should provide some summary. */ + test_no_summary__ = 1; + + if(!test_worker__) + printf("1..%d\n", (int) test_count__); + } + + int index = test_worker_index__; + for(i = 0; test_list__[i].func != NULL; i++) { + int run = (test_details__[i].flags & TEST_FLAG_RUN__); + if (test_skip_mode__) /* Run all tests except those listed. */ + run = !run; + if(run) + test_run__(&test_list__[i], index++, i); + } + + /* Write a summary */ + if(!test_no_summary__ && test_verbose_level__ >= 1) { + if(test_verbose_level__ >= 3) { + test_print_in_color__(TEST_COLOR_DEFAULT_INTENSIVE__, "Summary:\n"); + + printf(" Count of all unit tests: %4d\n", (int) test_list_size__); + printf(" Count of run unit tests: %4d\n", test_stat_run_units__); + printf(" Count of failed unit tests: %4d\n", test_stat_failed_units__); + printf(" Count of skipped unit tests: %4d\n", (int) test_list_size__ - test_stat_run_units__); + } + + if(test_stat_failed_units__ == 0) { + test_print_in_color__(TEST_COLOR_GREEN_INTENSIVE__, "SUCCESS:"); + printf(" All unit tests have passed.\n"); + } else { + test_print_in_color__(TEST_COLOR_RED_INTENSIVE__, "FAILED:"); + printf(" %d of %d unit tests %s failed.\n", + test_stat_failed_units__, test_stat_run_units__, + (test_stat_failed_units__ == 1) ? "has" : "have"); + } + + if(test_verbose_level__ >= 3) + printf("\n"); + } + + if (test_xml_output__) { +#if defined ACUTEST_UNIX__ + char *suite_name = basename(argv[0]); +#elif defined ACUTEST_WIN__ + char suite_name[_MAX_FNAME]; + _splitpath(argv[0], NULL, NULL, suite_name, NULL); +#else + const char *suite_name = argv[0]; +#endif + fprintf(test_xml_output__, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + fprintf(test_xml_output__, "<testsuite name=\"%s\" tests=\"%d\" errors=\"%d\" failures=\"%d\" skip=\"%d\">\n", + suite_name, (int)test_list_size__, test_stat_failed_units__, test_stat_failed_units__, + (int)test_list_size__ - test_stat_run_units__); + for(i = 0; test_list__[i].func != NULL; i++) { + struct test_detail__ *details = &test_details__[i]; + fprintf(test_xml_output__, " <testcase name=\"%s\" time=\"%.2f\">\n", test_list__[i].name, details->duration); + if (details->flags & TEST_FLAG_FAILURE__) + fprintf(test_xml_output__, " <failure />\n"); + if (!(details->flags & TEST_FLAG_FAILURE__) && !(details->flags & TEST_FLAG_SUCCESS__)) + fprintf(test_xml_output__, " <skipped />\n"); + fprintf(test_xml_output__, " </testcase>\n"); + } + fprintf(test_xml_output__, "</testsuite>\n"); + fclose(test_xml_output__); + } + + free((void*) test_details__); + + return (test_stat_failed_units__ == 0) ? 0 : 1; +} + + +#endif /* #ifndef TEST_NO_MAIN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + + +#endif /* #ifndef ACUTEST_H__ */ diff --git a/tests/aio.c b/tests/aio.c index 9bb7de19..fdc68cb8 100644 --- a/tests/aio.c +++ b/tests/aio.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// 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 @@ -14,13 +14,7 @@ #include <nng/protocol/pair1/pair.h> #include <nng/supplemental/util/platform.h> -#include "convey.h" -#include "stubs.h" - -#define APPENDSTR(m, s) nng_msg_append(m, s, strlen(s)) -#define CHECKSTR(m, s) \ - So(nng_msg_len(m) == strlen(s)); \ - So(memcmp(nng_msg_body(m), s, strlen(s)) == 0) +#include "acutest.h" void cbdone(void *p) @@ -41,164 +35,209 @@ cancelfn(nng_aio *aio, void *arg, int rv) nng_aio_finish(aio, rv); } -Main({ - Test("AIO operations", { - const char *addr = "inproc://aio"; - - Convey("Sleep works", { - nng_time start = 0; - nng_time end = 0; - nng_aio *saio; - So(nng_aio_alloc(&saio, sleepdone, &end) == 0); - start = nng_clock(); - nng_sleep_aio(200, saio); - nng_aio_wait(saio); - So(nng_aio_result(saio) == 0); - So(end != 0); - So((end - start) >= 200); - So((end - start) <= 1000); - So((nng_clock() - start) >= 200); - So((nng_clock() - start) <= 1000); - nng_aio_free(saio); - }); - - Convey("Sleep timeout works", { - nng_time start = 0; - nng_time end = 0; - nng_aio *saio; - So(nng_aio_alloc(&saio, sleepdone, &end) == 0); - nng_aio_set_timeout(saio, 100); - start = nng_clock(); - nng_sleep_aio(2000, saio); - nng_aio_wait(saio); - So(nng_aio_result(saio) == NNG_ETIMEDOUT); - So(end != 0); - So((end - start) >= 100); - So((end - start) <= 1000); - So((nng_clock() - start) >= 100); - So((nng_clock() - start) <= 1000); - nng_aio_free(saio); - }); - - Convey("Given a connected pair of sockets", { - nng_socket s1; - nng_socket s2; - nng_aio * txaio; - nng_aio * rxaio; - int txdone = 0; - int rxdone = 0; - nng_msg * m; - - So(nng_pair1_open(&s1) == 0); - So(nng_pair1_open(&s2) == 0); - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(s2, addr, NULL, 0) == 0); - - So(nng_aio_alloc(&rxaio, cbdone, &rxdone) == 0); - So(nng_aio_alloc(&txaio, cbdone, &txdone) == 0); - - Reset({ - nng_aio_free(rxaio); - nng_aio_free(txaio); - nng_close(s1); - nng_close(s2); - }); - - nng_aio_set_timeout(rxaio, 100); - nng_aio_set_timeout(txaio, 100); - - So(nng_msg_alloc(&m, 0) == 0); - APPENDSTR(m, "hello"); - - nng_recv_aio(s2, rxaio); - - nng_aio_set_msg(txaio, m); - nng_send_aio(s1, txaio); - - nng_aio_wait(txaio); - nng_aio_wait(rxaio); - - So(nng_aio_result(rxaio) == 0); - So(nng_aio_result(txaio) == 0); - - So((m = nng_aio_get_msg(rxaio)) != NULL); - CHECKSTR(m, "hello"); - - nng_msg_free(m); - - So(rxdone == 1); - So(txdone == 1); - }); - - Convey("Failure modes work", { - nng_socket s; - nng_aio * a; - int done = 0; - - So(nng_pair1_open(&s) == 0); - - So(nng_aio_alloc(&a, cbdone, &done) == 0); - - Reset({ - nng_aio_free(a); - nng_close(s); - }); - - Convey("Explicit timeout works", { - nng_aio_set_timeout(a, 40); - nng_recv_aio(s, a); - nng_aio_wait(a); - So(done == 1); - So(nng_aio_result(a) == NNG_ETIMEDOUT); - }); - Convey("Default timeout works", { - So(nng_setopt_ms(s, NNG_OPT_RECVTIMEO, 40) == - 0); - nng_recv_aio(s, a); - nng_aio_wait(a); - So(done == 1); - So(nng_aio_result(a) == NNG_ETIMEDOUT); - }); - Convey("Zero timeout works", { - nng_aio_set_timeout(a, NNG_DURATION_ZERO); - nng_recv_aio(s, a); - nng_aio_wait(a); - So(done == 1); - So(nng_aio_result(a) == NNG_ETIMEDOUT); - }); - Convey("Cancellation works", { - nng_aio_set_timeout(a, NNG_DURATION_INFINITE); - nng_recv_aio(s, a); - nng_aio_cancel(a); - nng_aio_wait(a); - So(done == 1); - So(nng_aio_result(a) == NNG_ECANCELED); - }) - }); - - Convey("We cannot set insane IOVs", { - nng_aio *aio; - nng_iov iov; - - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - So(nng_aio_set_iov(aio, 1024, &iov) == NNG_EINVAL); - nng_aio_free(aio); - }); - - Convey("Provider cancellation works", { - nng_aio *aio; - int rv = 0; - // We fake an empty provider that does not do anything. - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - So(nng_aio_begin(aio) == true); - nng_aio_defer(aio, cancelfn, &rv); - nng_aio_cancel(aio); - nng_aio_wait(aio); - So(rv == NNG_ECANCELED); - nng_aio_free(aio); - }); - }); - - nng_fini(); -}) +void +test_sleep(void) +{ + nng_time start = 0; + nng_time end = 0; + nng_aio *saio; + + TEST_CHECK(nng_aio_alloc(&saio, sleepdone, &end) == 0); + start = nng_clock(); + nng_sleep_aio(200, saio); + nng_aio_wait(saio); + TEST_CHECK(nng_aio_result(saio) == 0); + TEST_CHECK(end != 0); + TEST_CHECK((end - start) >= 200); + TEST_CHECK((end - start) <= 1000); + TEST_CHECK((nng_clock() - start) >= 200); + TEST_CHECK((nng_clock() - start) <= 1000); + nng_aio_free(saio); +} + +void +test_sleep_timeout(void) +{ + nng_time start = 0; + nng_time end = 0; + nng_aio *saio; + TEST_CHECK(nng_aio_alloc(&saio, sleepdone, &end) == 0); + nng_aio_set_timeout(saio, 100); + start = nng_clock(); + nng_sleep_aio(2000, saio); + nng_aio_wait(saio); + TEST_CHECK(nng_aio_result(saio) == NNG_ETIMEDOUT); + TEST_CHECK(end != 0); + TEST_CHECK((end - start) >= 100); + TEST_CHECK((end - start) <= 1000); + TEST_CHECK((nng_clock() - start) >= 100); + TEST_CHECK((nng_clock() - start) <= 1000); + nng_aio_free(saio); +} + +void +test_insane_niov(void) +{ + nng_aio *aio; + nng_iov iov; + + TEST_CHECK(nng_aio_alloc(&aio, NULL, NULL) == 0); + TEST_CHECK(nng_aio_set_iov(aio, 1024, &iov) == NNG_EINVAL); + nng_aio_free(aio); +} + +void +test_provider_cancel(void) +{ + nng_aio *aio; + int rv = 0; + // We fake an empty provider that does not do anything. + TEST_CHECK(nng_aio_alloc(&aio, NULL, NULL) == 0); + TEST_CHECK(nng_aio_begin(aio) == true); + nng_aio_defer(aio, cancelfn, &rv); + nng_aio_cancel(aio); + nng_aio_wait(aio); + TEST_CHECK(rv == NNG_ECANCELED); + nng_aio_free(aio); +} + +void +test_consumer_cancel(void) +{ + nng_aio * a; + nng_socket s1; + int done = 0; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_aio_alloc(&a, cbdone, &done) == 0); + + nng_aio_set_timeout(a, NNG_DURATION_INFINITE); + nng_recv_aio(s1, a); + nng_aio_cancel(a); + nng_aio_wait(a); + TEST_CHECK(done == 1); + TEST_CHECK(nng_aio_result(a) == NNG_ECANCELED); + + nng_aio_free(a); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_traffic(void) +{ + nng_socket s1; + nng_socket s2; + nng_aio * txaio; + nng_aio * rxaio; + int txdone = 0; + int rxdone = 0; + nng_msg * m; + char * addr = "inproc://traffic"; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&s2) == 0); + + TEST_CHECK(nng_listen(s1, addr, NULL, 0) == 0); + TEST_CHECK(nng_dial(s2, addr, NULL, 0) == 0); + + TEST_CHECK(nng_aio_alloc(&rxaio, cbdone, &rxdone) == 0); + TEST_CHECK(nng_aio_alloc(&txaio, cbdone, &txdone) == 0); + + nng_aio_set_timeout(rxaio, 1000); + nng_aio_set_timeout(txaio, 1000); + + TEST_CHECK(nng_msg_alloc(&m, 0) == 0); + TEST_CHECK(nng_msg_append(m, "hello", strlen("hello")) == 0); + + nng_recv_aio(s2, rxaio); + + nng_aio_set_msg(txaio, m); + nng_send_aio(s1, txaio); + + nng_aio_wait(txaio); + nng_aio_wait(rxaio); + + TEST_CHECK(nng_aio_result(rxaio) == 0); + TEST_CHECK(nng_aio_result(txaio) == 0); + + TEST_CHECK((m = nng_aio_get_msg(rxaio)) != NULL); + TEST_CHECK(nng_msg_len(m) == strlen("hello")); + TEST_CHECK(memcmp(nng_msg_body(m), "hello", strlen("hello")) == 0); + + nng_msg_free(m); + + TEST_CHECK(rxdone == 1); + TEST_CHECK(txdone == 1); + + nng_aio_free(rxaio); + nng_aio_free(txaio); + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(s2) == 0); +} + +void +test_explicit_timeout(void) +{ + nng_socket s; + nng_aio * a; + int done = 0; + + TEST_CHECK(nng_pair1_open(&s) == 0); + TEST_CHECK(nng_aio_alloc(&a, cbdone, &done) == 0); + nng_aio_set_timeout(a, 40); + nng_recv_aio(s, a); + nng_aio_wait(a); + TEST_CHECK(done == 1); + TEST_CHECK(nng_aio_result(a) == NNG_ETIMEDOUT); + nng_aio_free(a); + TEST_CHECK(nng_close(s) == 0); +} + +void +test_inherited_timeout(void) +{ + nng_socket s; + nng_aio * a; + int done = 0; + + TEST_CHECK(nng_pair1_open(&s) == 0); + TEST_CHECK(nng_aio_alloc(&a, cbdone, &done) == 0); + TEST_CHECK(nng_setopt_ms(s, NNG_OPT_RECVTIMEO, 40) == 0); + nng_recv_aio(s, a); + nng_aio_wait(a); + TEST_CHECK(done == 1); + TEST_CHECK(nng_aio_result(a) == NNG_ETIMEDOUT); + nng_aio_free(a); + TEST_CHECK(nng_close(s) == 0); +} + +void +test_zero_timeout(void) +{ + nng_socket s; + nng_aio * a; + int done = 0; + + TEST_CHECK(nng_pair1_open(&s) == 0); + TEST_CHECK(nng_aio_alloc(&a, cbdone, &done) == 0); + nng_aio_set_timeout(a, NNG_DURATION_ZERO); + nng_recv_aio(s, a); + nng_aio_wait(a); + TEST_CHECK(done == 1); + TEST_CHECK(nng_aio_result(a) == NNG_ETIMEDOUT); + nng_aio_free(a); + TEST_CHECK(nng_close(s) == 0); +} + +TEST_LIST = { + { "sleep", test_sleep }, + { "sleep timeout", test_sleep_timeout }, + { "insane niov", test_insane_niov }, + { "provider cancel", test_provider_cancel }, + { "consumer cancel", test_consumer_cancel }, + { "traffic", test_traffic }, + { "explicit timeout", test_explicit_timeout }, + { "inherited timeout", test_inherited_timeout }, + { "zero timeout", test_zero_timeout }, + { NULL, NULL }, +};
\ No newline at end of file diff --git a/tests/base64.c b/tests/base64.c deleted file mode 100644 index 1781c5ef..00000000 --- a/tests/base64.c +++ /dev/null @@ -1,84 +0,0 @@ -// -// Copyright 2018 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. -// - -#include <string.h> -#include <nng/nng.h> - -#include "convey.h" -#include "supplemental/base64/base64.h" - -typedef struct testcase { - char *decoded; - char *encoded; -} testcase; - -static struct testcase cases[] = { - // clang-format off - { "", "" }, - { "f", "Zg==" }, - { "fo", "Zm8=" }, - { "foo", "Zm9v" }, - { "foob", "Zm9vYg==" }, - { "fooba", "Zm9vYmE=" }, - { "foobar", "Zm9vYmFy" }, - { NULL, NULL } - // clang-format on -}; - -TestMain("Base64 Verification", { - - Convey("Encode Works", { - int rv; - char buf[1024]; - int i; - void *dec; - - for (i = 0; (dec = cases[i].decoded) != NULL; i++) { - rv = nni_base64_encode(dec, strlen(dec), buf, 1024); - So(rv >= 0); - So(rv == (int) strlen(cases[i].encoded)); - buf[rv] = 0; - So(strcmp(buf, cases[i].encoded) == 0); - } - }); - - Convey("Decode Works", { - int rv; - char buf[1024]; - int i; - void *enc; - - for (i = 0; (enc = cases[i].encoded) != NULL; i++) { - rv = nni_base64_decode( - enc, strlen(enc), (void *) buf, 1024); - So(rv >= 0); - So(rv == (int) strlen(cases[i].decoded)); - buf[rv] = 0; - So(strcmp(buf, cases[i].decoded) == 0); - } - }); - - Convey("Overflow Works", { - char tmp[1024]; - for (int i = 1; cases[i].decoded != NULL; i++) { - void *enc = cases[i].encoded; - void *dec = cases[i].decoded; - void *buf = tmp; - - So(nni_base64_encode( - dec, strlen(dec), buf, strlen(enc) - 1) == -1); - So(nni_base64_encode(dec, strlen(dec), buf, 0) == -1); - - So(nni_base64_decode( - enc, strlen(enc), buf, strlen(dec) - 1) == -1); - So(nni_base64_encode(enc, strlen(enc), buf, 0) == -1); - } - }) -}) diff --git a/tests/bufsz.c b/tests/bufsz.c index da6a14cb..4f62905d 100644 --- a/tests/bufsz.c +++ b/tests/bufsz.c @@ -8,97 +8,109 @@ // found online at https://opensource.org/licenses/MIT. // -#include <string.h> - -#include <nng/compat/nanomsg/nn.h> #include <nng/nng.h> -#include <nng/protocol/pubsub0/sub.h> #include <nng/protocol/pair1/pair.h> #include <nng/supplemental/util/platform.h> -#include "trantest.h" -#include "convey.h" -#include "stubs.h" - -#define SECONDS(x) ((x) *1000) - -TestMain("Buffer Options", { - - atexit(nng_fini); - - Convey("We are able to open a PAIR socket", { - nng_socket s1; - - So(nng_pair_open(&s1) == 0); - - Reset({ nng_close(s1); }); - - Convey("Set/Get Recv Buf Option", { - int cnt; - So(nng_setopt_int(s1, NNG_OPT_RECVBUF, 10) == 0); - So(nng_getopt_int(s1, NNG_OPT_RECVBUF, &cnt) == 0); - So(cnt == 10); - So(nng_setopt_size(s1, NNG_OPT_RECVBUF, 42) == - NNG_EBADTYPE); - - }); - Convey("Set/Get Send Buf Option", { - int cnt; - So(nng_setopt_int(s1, NNG_OPT_SENDBUF, 10) == 0); - So(nng_getopt_int(s1, NNG_OPT_SENDBUF, &cnt) == 0); - So(cnt == 10); - So(nng_setopt_size(s1, NNG_OPT_SENDBUF, 42) == - NNG_EBADTYPE); - - }); - - // NOTE: We are going to use the compat mode, but - // this assumes that the socket is the same between compat - // and current mode. This is true, but normal applications - // MUST NOT assume this. We only do so for testing. - Convey("Legacy Recv Buf Option", { - int cnt; - int os = (int) s1.id; - size_t sz = sizeof(cnt); - So(nng_setopt_int(s1, NNG_OPT_RECVBUF, 10) == 0); - So(nn_getsockopt( - os, NN_SOL_SOCKET, NN_RCVBUF, &cnt, &sz) == 0); - So(cnt == 10240); - cnt = 1; - So(nn_setsockopt( - os, NN_SOL_SOCKET, NN_RCVBUF, &cnt, sz) == 0); - So(nn_getsockopt( - os, NN_SOL_SOCKET, NN_RCVBUF, &cnt, &sz) == 0); - So(cnt == 1024); // round up! - So(nng_getopt_int(s1, NNG_OPT_RECVBUF, &cnt) == 0); - So(cnt == 1); - - So(nn_setsockopt( - os, NN_SOL_SOCKET, NN_RCVBUF, &cnt, 100) == -1); - So(nn_errno() == EINVAL); - }); - Convey("Legacy Send Buf Option", { - int cnt; - int os = (int) s1.id; - size_t sz = sizeof(cnt); - So(nng_setopt_int(s1, NNG_OPT_SENDBUF, 10) == 0); - So(nn_getsockopt( - os, NN_SOL_SOCKET, NN_SNDBUF, &cnt, &sz) == 0); - So(cnt == 10240); - cnt = 1; - So(nn_setsockopt( - os, NN_SOL_SOCKET, NN_SNDBUF, &cnt, sz) == 0); - So(nn_getsockopt( - os, NN_SOL_SOCKET, NN_SNDBUF, &cnt, &sz) == 0); - So(cnt == 1024); // round up! - So(nng_getopt_int(s1, NNG_OPT_SENDBUF, &cnt) == 0); - So(cnt == 1); - - So(nn_setsockopt( - os, NN_SOL_SOCKET, NN_SNDBUF, &cnt, 100) == -1); - So(nn_errno() == EINVAL); - }); - - }); -}) +#include <nng/compat/nanomsg/nn.h> + +#include "acutest.h" + +void +test_buffer_options(void) +{ + nng_socket s1; + int val; + size_t sz; + char * opt; + + char *cases[] = { + NNG_OPT_RECVBUF, + NNG_OPT_SENDBUF, + NULL, + }; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + for (int i = 0; (opt = cases[i]) != NULL; i++) { + + TEST_CASE(opt); + + // Can't receive a size into zero bytes. + sz = 0; + TEST_CHECK(nng_getopt(s1, opt, &val, &sz) == NNG_EINVAL); + + // Can set a valid size + TEST_CHECK(nng_setopt_int(s1, opt, 1234) == 0); + TEST_CHECK(nng_getopt_int(s1, opt, &val) == 0); + TEST_CHECK(val == 1234); + + val = 0; + sz = sizeof(val); + TEST_CHECK(nng_getopt(s1, opt, &val, &sz) == 0); + TEST_CHECK(val == 1234); + TEST_CHECK(sz == sizeof(val)); + + // Can't set a negative size + TEST_CHECK(nng_setopt_int(s1, opt, -5) == NNG_EINVAL); + + // Can't pass a buf too small for size + sz = sizeof(val) - 1; + val = 1; + TEST_CHECK(nng_setopt(s1, opt, &val, sz) == NNG_EINVAL); + // Buffer sizes are limited to sane levels + TEST_CHECK(nng_setopt_int(s1, opt, 0x100000) == NNG_EINVAL); + } + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_buffer_legacy(void) +{ + nng_socket s1; + char * opt; + + char *cases[] = { + NNG_OPT_RECVBUF, + NNG_OPT_SENDBUF, + NULL, + }; + int legacy[] = { + NN_RCVBUF, + NN_SNDBUF, + }; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + for (int i = 0; (opt = cases[i]) != NULL; i++) { + int cnt; + int os = (int) s1.id; + size_t sz; + int nnopt = legacy[i]; + + TEST_CASE(opt); + + sz = sizeof(cnt); + TEST_CHECK(nng_setopt_int(s1, opt, 10) == 0); + TEST_CHECK( + nn_getsockopt(os, NN_SOL_SOCKET, nnopt, &cnt, &sz) == 0); + TEST_CHECK(cnt == 10240); // 1k multiple + + cnt = 1; + TEST_CHECK( + nn_setsockopt(os, NN_SOL_SOCKET, nnopt, &cnt, sz) == 0); + TEST_CHECK(nn_getsockopt(os, NN_SOL_SOCKET, nnopt, &cnt, &sz) == 0); + TEST_CHECK(cnt == 1024); // round up! + TEST_CHECK(nng_getopt_int(s1, opt, &cnt) == 0); + TEST_CHECK(cnt == 1); + + TEST_CHECK(nn_setsockopt(os, NN_SOL_SOCKET, nnopt, &cnt, 100) == -1); + TEST_CHECK(nn_errno() == EINVAL); + } + TEST_CHECK(nng_close(s1) == 0); +} + +TEST_LIST = { + { "buffer options", test_buffer_options }, + { "buffer legacy", test_buffer_legacy }, + { NULL, NULL }, +}; diff --git a/tests/pair1.c b/tests/pair1.c deleted file mode 100644 index f55ea08a..00000000 --- a/tests/pair1.c +++ /dev/null @@ -1,541 +0,0 @@ -// -// Copyright 2017 Garrett D'Amore <garrett@damore.org> -// Copyright 2017 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. -// - -#include <string.h> - -#include <nng/nng.h> -#include <nng/protocol/pair1/pair.h> - -#include "convey.h" -#include "stubs.h" -#include "trantest.h" - -#define SECOND(x) ((x) *1000) -#define MILLISECOND(x) (x) - -#define APPENDSTR(m, s) nng_msg_append(m, s, strlen(s)) -#define CHECKSTR(m, s) \ - So(nng_msg_len(m) == strlen(s)); \ - So(memcmp(nng_msg_body(m), s, strlen(s)) == 0) - -TestMain("PAIRv1 protocol", { - const char * templ = "inproc://pairv1/%u"; - char addr[NNG_MAXADDRLEN + 1]; - nng_socket s1 = NNG_SOCKET_INITIALIZER; - nng_socket c1 = NNG_SOCKET_INITIALIZER; - nng_socket c2 = NNG_SOCKET_INITIALIZER; - nng_duration tmo; - uint32_t v; - size_t sz; - - atexit(nng_fini); - - Convey("Given a few sockets", { - trantest_next_address(addr, templ); - So(nng_pair1_open(&s1) == 0); - So(nng_pair1_open(&c1) == 0); - So(nng_pair1_open(&c2) == 0); - - So(nng_socket_id(s1) > 0); - So(nng_socket_id(c1) > 0); - So(nng_socket_id(c2) > 0); - So(nng_socket_id(s1) != nng_socket_id(c1)); - So(nng_socket_id(s1) != nng_socket_id(c2)); - So(nng_socket_id(c1) != nng_socket_id(c2)); - - Reset({ - nng_close(s1); - nng_close(c1); - nng_close(c2); - }); - - tmo = MILLISECOND(300); - So(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, tmo) == 0); - So(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, tmo) == 0); - So(nng_setopt_ms(c2, NNG_OPT_RECVTIMEO, tmo) == 0); - tmo = 0; - So(nng_getopt_ms(s1, NNG_OPT_RECVTIMEO, &tmo) == 0); - So(tmo == MILLISECOND(300)); - - Convey("Monogamous cooked mode works", { - nng_msg *msg; - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - nng_msleep(20); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "ALPHA"); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - CHECKSTR(msg, "ALPHA"); - nng_msg_free(msg); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "BETA"); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c1, &msg, 0) == 0); - CHECKSTR(msg, "BETA"); - nng_msg_free(msg); - }); - - Convey("Monogamous mode ignores new conns", { - nng_msg *msg; - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - nng_msleep(100); - So(nng_dial(c2, addr, NULL, 0) == 0); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "ONE"); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - CHECKSTR(msg, "ONE"); - nng_msg_free(msg); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "TWO"); - So(nng_sendmsg(c2, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); - }); - - Convey("Cannot set raw mode after connect", { - So(nng_setopt_bool(s1, NNG_OPT_RAW, true) == - NNG_EREADONLY); - }); - - Convey("Polyamorous mode is best effort", { - int rv; - int i; - nng_msg * msg; - nng_duration to = MILLISECOND(100); - - So(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); - - So(nng_setopt_int(s1, NNG_OPT_RECVBUF, 1) == 0); - So(nng_setopt_int(s1, NNG_OPT_SENDBUF, 1) == 0); - So(nng_setopt_int(c1, NNG_OPT_RECVBUF, 1) == 0); - So(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, to) == 0); - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - nng_msleep(20); - - for (i = 0, rv = 0; i < 10; i++) { - So(nng_msg_alloc(&msg, 0) == 0); - if ((rv = nng_sendmsg(s1, msg, 0)) != 0) { - nng_msg_free(msg); - break; - } - } - So(rv == 0); - So(i == 10); - }); - - Convey("Monogamous mode exerts backpressure", { - int i; - int rv; - nng_msg * msg; - nng_duration to = MILLISECOND(30); - - So(nng_setopt_int(s1, NNG_OPT_RECVBUF, 1) == 0); - So(nng_setopt_int(s1, NNG_OPT_SENDBUF, 1) == 0); - So(nng_setopt_int(c1, NNG_OPT_RECVBUF, 1) == 0); - So(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, to) == 0); - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - nng_msleep(20); - - // We choose to allow some buffering. In reality the - // buffer size is just 1, and we will fail after 2. - for (i = 0, rv = 0; i < 10; i++) { - So(nng_msg_alloc(&msg, 0) == 0); - if ((rv = nng_sendmsg(s1, msg, 0)) != 0) { - nng_msg_free(msg); - break; - } - } - So(rv == NNG_ETIMEDOUT); - So(i < 10); - }); - - Convey("Cannot set polyamorous mode after connect", { - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - nng_msleep(100); - - So(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == - NNG_ESTATE); - }); - - Convey("We cannot set insane TTLs", { - int ttl; - - ttl = 0; - So(nng_setopt_int(s1, NNG_OPT_MAXTTL, 0) == - NNG_EINVAL); - - So(nng_setopt_int(s1, NNG_OPT_MAXTTL, 1000) == - NNG_EINVAL); - - sz = 1; - ttl = 8; - So(nng_setopt(s1, NNG_OPT_MAXTTL, &ttl, sz) == - NNG_EINVAL); - }); - - Convey("Polyamorous cooked mode works", { - nng_msg *msg; - bool v; - nng_pipe p1; - nng_pipe p2; - - So(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); - So(v == false); - - So(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); - So(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); - So(v == true); - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - So(nng_dial(c2, addr, NULL, 0) == 0); - nng_msleep(20); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "ONE"); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - CHECKSTR(msg, "ONE"); - p1 = nng_msg_get_pipe(msg); - So(nng_pipe_id(p1) > 0); - nng_msg_free(msg); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "TWO"); - So(nng_sendmsg(c2, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - CHECKSTR(msg, "TWO"); - p2 = nng_msg_get_pipe(msg); - So(nng_pipe_id(p2) > 0); - nng_msg_free(msg); - - So(nng_pipe_id(p1) != nng_pipe_id(p2)); - - So(nng_msg_alloc(&msg, 0) == 0); - - nng_msg_set_pipe(msg, p1); - APPENDSTR(msg, "UNO"); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c1, &msg, 0) == 0); - CHECKSTR(msg, "UNO"); - nng_msg_free(msg); - - So(nng_msg_alloc(&msg, 0) == 0); - nng_msg_set_pipe(msg, p2); - APPENDSTR(msg, "DOS"); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c2, &msg, 0) == 0); - CHECKSTR(msg, "DOS"); - nng_msg_free(msg); - - nng_close(c1); - - So(nng_msg_alloc(&msg, 0) == 0); - nng_msg_set_pipe(msg, p1); - APPENDSTR(msg, "EIN"); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c2, &msg, 0) == NNG_ETIMEDOUT); - }); - - Convey("Polyamorous default works", { - nng_msg *msg; - - So(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - nng_msleep(100); - So(nng_dial(c2, addr, NULL, 0) == 0); - nng_msleep(20); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "YES"); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c1, &msg, 0) == 0); - CHECKSTR(msg, "YES"); - nng_msg_free(msg); - - nng_close(c1); - nng_msleep(10); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "AGAIN"); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c2, &msg, 0) == 0); - CHECKSTR(msg, "AGAIN"); - nng_msg_free(msg); - }); - }); - - Convey("Monogamous raw mode works", { - nng_msg *msg; - uint32_t hops; - - So(nng_pair1_open_raw(&s1) == 0); - So(nng_pair1_open_raw(&c1) == 0); - So(nng_pair1_open_raw(&c2) == 0); - - Reset({ - nng_close(s1); - nng_close(c1); - nng_close(c2); - }); - - tmo = MILLISECOND(300); - So(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, tmo) == 0); - So(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, tmo) == 0); - So(nng_setopt_ms(c2, NNG_OPT_RECVTIMEO, tmo) == 0); - tmo = 0; - So(nng_getopt_ms(s1, NNG_OPT_RECVTIMEO, &tmo) == 0); - So(tmo == MILLISECOND(300)); - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - nng_msleep(20); - - Convey("Send/recv work", { - nng_pipe p = NNG_PIPE_INITIALIZER; - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "GAMMA"); - So(nng_msg_header_append_u32(msg, 1) == 0); - So(nng_msg_header_len(msg) == sizeof(uint32_t)); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - p = nng_msg_get_pipe(msg); - So(nng_pipe_id(p) > 0); - - CHECKSTR(msg, "GAMMA"); - So(nng_msg_header_len(msg) == sizeof(uint32_t)); - So(nng_msg_header_trim_u32(msg, &hops) == 0); - So(hops == 2); - nng_msg_free(msg); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "EPSILON"); - So(nng_msg_header_append_u32(msg, 1) == 0); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c1, &msg, 0) == 0); - CHECKSTR(msg, "EPSILON"); - So(nng_msg_header_len(msg) == sizeof(uint32_t)); - So(nng_msg_header_trim_u32(msg, &hops) == 0); - p = nng_msg_get_pipe(msg); - So(nng_pipe_id(p) > 0); - - So(hops == 2); - nng_msg_free(msg); - }); - - Convey("Missing raw header fails", { - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); - - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_msg_append_u32(msg, 0xFEEDFACE) == 0); - So(nng_msg_header_append_u32(msg, 1) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - So(nng_msg_trim_u32(msg, &v) == 0); - So(v == 0xFEEDFACE); - nng_msg_free(msg); - }); - - Convey("Reserved bits in raw header", { - Convey("Nonzero bits fail", { - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_msg_header_append_u32( - msg, 0xDEAD0000) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); - }); - Convey("Zero bits pass", { - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_msg_append_u32(msg, 0xFEEDFACE) == 0); - So(nng_msg_header_append_u32(msg, 1) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - So(nng_msg_trim_u32(msg, &v) == 0); - So(v == 0xFEEDFACE); - nng_msg_free(msg); - }); - }); - - Convey("TTL is honored", { - int ttl; - - So(nng_setopt_int(s1, NNG_OPT_MAXTTL, 4) == 0); - So(nng_getopt_int(s1, NNG_OPT_MAXTTL, &ttl) == 0); - So(ttl == 4); - Convey("Bad TTL bounces", { - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_msg_header_append_u32(msg, 4) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); - }); - Convey("Good TTL passes", { - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_msg_append_u32(msg, 0xFEEDFACE) == 0); - So(nng_msg_header_append_u32(msg, 3) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - So(nng_msg_trim_u32(msg, &v) == 0); - So(v == 0xFEEDFACE); - So(nng_msg_header_trim_u32(msg, &v) == 0); - So(v == 4); - nng_msg_free(msg); - }); - - Convey("Large TTL passes", { - ttl = 0xff; - So(nng_setopt_int(s1, NNG_OPT_MAXTTL, 0xff) == - 0); - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_msg_append_u32(msg, 1234) == 0); - So(nng_msg_header_append_u32(msg, 0xfe) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - So(nng_msg_trim_u32(msg, &v) == 0); - So(v == 1234); - So(nng_msg_header_trim_u32(msg, &v) == 0); - So(v == 0xff); - nng_msg_free(msg); - }); - - Convey("Max TTL fails", { - ttl = 0xff; - So(nng_setopt_int(s1, NNG_OPT_MAXTTL, 0xff) == - 0); - So(nng_msg_alloc(&msg, 0) == 0); - So(nng_msg_header_append_u32(msg, 0xff) == 0); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); - }); - }); - }); - - Convey("Polyamorous raw mode works", { - nng_msg *msg; - bool v; - uint32_t hops; - nng_pipe p1; - nng_pipe p2; - - So(nng_pair1_open_raw(&s1) == 0); - So(nng_pair1_open(&c1) == 0); - So(nng_pair1_open(&c2) == 0); - - Reset({ - nng_close(s1); - nng_close(c1); - nng_close(c2); - }); - - tmo = MILLISECOND(300); - So(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, tmo) == 0); - So(nng_setopt_ms(c1, NNG_OPT_RECVTIMEO, tmo) == 0); - So(nng_setopt_ms(c2, NNG_OPT_RECVTIMEO, tmo) == 0); - tmo = 0; - So(nng_getopt_ms(s1, NNG_OPT_RECVTIMEO, &tmo) == 0); - So(tmo == MILLISECOND(300)); - - So(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); - So(v == 0); - - So(nng_setopt_bool(s1, NNG_OPT_PAIR1_POLY, true) == 0); - So(nng_getopt_bool(s1, NNG_OPT_PAIR1_POLY, &v) == 0); - So(v == true); - - v = false; - So(nng_getopt_bool(s1, NNG_OPT_RAW, &v) == 0); - So(v == true); - - So(nng_listen(s1, addr, NULL, 0) == 0); - So(nng_dial(c1, addr, NULL, 0) == 0); - So(nng_dial(c2, addr, NULL, 0) == 0); - nng_msleep(20); - - Convey("Send/recv works", { - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "ONE"); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - CHECKSTR(msg, "ONE"); - p1 = nng_msg_get_pipe(msg); - So(nng_pipe_id(p1) > 0); - So(nng_msg_header_trim_u32(msg, &hops) == 0); - So(hops == 1); - nng_msg_free(msg); - - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "TWO"); - So(nng_sendmsg(c2, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - CHECKSTR(msg, "TWO"); - p2 = nng_msg_get_pipe(msg); - So(nng_pipe_id(p2) > 0); - So(nng_msg_header_trim_u32(msg, &hops) == 0); - So(hops == 1); - nng_msg_free(msg); - - So(nng_pipe_id(p1) != nng_pipe_id(p2)); - - So(nng_msg_alloc(&msg, 0) == 0); - nng_msg_set_pipe(msg, p1); - APPENDSTR(msg, "UNO"); - So(nng_msg_header_append_u32(msg, 1) == 0); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c1, &msg, 0) == 0); - CHECKSTR(msg, "UNO"); - nng_msg_free(msg); - - So(nng_msg_alloc(&msg, 0) == 0); - nng_msg_set_pipe(msg, p2); - APPENDSTR(msg, "DOS"); - So(nng_msg_header_append_u32(msg, 1) == 0); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c2, &msg, 0) == 0); - CHECKSTR(msg, "DOS"); - nng_msg_free(msg); - }); - - Convey("Closed pipes don't work", { - So(nng_msg_alloc(&msg, 0) == 0); - APPENDSTR(msg, "ONE"); - So(nng_sendmsg(c1, msg, 0) == 0); - So(nng_recvmsg(s1, &msg, 0) == 0); - CHECKSTR(msg, "ONE"); - p1 = nng_msg_get_pipe(msg); - So(nng_pipe_id(p1) > 0); - nng_msg_free(msg); - - nng_close(c1); - - So(nng_msg_alloc(&msg, 0) == 0); - nng_msg_set_pipe(msg, p1); - APPENDSTR(msg, "EIN"); - So(nng_msg_header_append_u32(msg, 1) == 0); - So(nng_sendmsg(s1, msg, 0) == 0); - So(nng_recvmsg(c2, &msg, 0) == NNG_ETIMEDOUT); - }); - }); -}) diff --git a/tests/platform.c b/tests/platform.c index 7af18029..7546fa7f 100644 --- a/tests/platform.c +++ b/tests/platform.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// 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 @@ -8,11 +8,12 @@ // found online at https://opensource.org/licenses/MIT. // +#include "testutil.h" + #include <nng/nng.h> #include <nng/supplemental/util/platform.h> -#include "convey.h" -#include "stubs.h" +#include "acutest.h" struct addarg { int cnt; @@ -31,84 +32,116 @@ add(void *arg) nng_mtx_unlock(aa->mx); } -TestMain("Platform Operations", { - // This is required for anything else to work - Convey("The clock works", { - uint64_t now = getms(); - - Convey("usleep works", { - nng_msleep(100); - - So((getms() - now) >= 100); // cannot be *shorter*!! - So((getms() - now) < 200); // crummy clock resolution? - }); - Convey("times work", { - uint64_t msend; - int usdelta; - int msdelta; - nng_time usend; - nng_time usnow = nng_clock(); - nng_msleep(200); - usend = nng_clock(); - msend = getms(); - - So(usend > usnow); - So(msend > now); - usdelta = (int) (usend - usnow); - msdelta = (int) (msend - now); - So(usdelta >= 200); - So(usdelta < 250); // increased tolerance for CIs - So(abs(msdelta - usdelta) < 50); - }); - }); - Convey("Mutexes work", { - static nng_mtx *mx; - - So(nng_mtx_alloc(&mx) == 0); - Reset({ nng_mtx_free(mx); }); - - Convey("We can lock and unlock mutex", { - nng_mtx_lock(mx); - So(1); - nng_mtx_unlock(mx); - So(1); - Convey("And then lock it again", { - nng_mtx_lock(mx); - So(1); - nng_mtx_unlock(mx); - So(1); - }); - }); - }); - - Convey("Threads work", { - static nng_thread *thr; - int rv; - struct addarg aa; - - So(nng_mtx_alloc(&aa.mx) == 0); - So(nng_cv_alloc(&aa.cv, aa.mx) == 0); - Reset({ - nng_mtx_free(aa.mx); - nng_cv_free(aa.cv); - }); - aa.cnt = 0; - - Convey("We can create threads", { - rv = nng_thread_create(&thr, add, &aa); - So(rv == 0); - - Reset({ nng_thread_destroy(thr); }); - - Convey("It ran", { - int val; - nng_mtx_lock(aa.mx); - while ((val = aa.cnt) == 0) { - nng_cv_wait(aa.cv); - } - nng_mtx_unlock(aa.mx); - So(val == 1); - }); - }); - }); -}) +void +test_sleep(void) +{ + uint64_t start, end; + start = testutil_clock(); + nng_msleep(100); + end = testutil_clock(); + TEST_CHECK((end - start) >= 100); + TEST_CHECK((end - start) <= 500); +} + +void +test_clock(void) +{ + uint64_t mstart; + uint64_t msend; + uint64_t usdelta; + uint64_t msdelta; + nng_time usend; + nng_time usnow; + + mstart = testutil_clock(); + usnow = nng_clock(); + nng_msleep(200); + usend = nng_clock(); + msend = testutil_clock(); + + TEST_CHECK(usend > usnow); + TEST_CHECK(msend > mstart); + usdelta = usend - usnow; + msdelta = msend - mstart; + TEST_CHECK(usdelta >= 200); + TEST_CHECK(usdelta < 500); // increased tolerance for CIs + if (msdelta > usdelta) { + TEST_CHECK((msdelta - usdelta) < 50); + } else { + TEST_CHECK((usdelta - msdelta) < 50); + } +} + +void +test_mutex(void) +{ + nng_mtx *mx, *mx2; + + TEST_CHECK(nng_mtx_alloc(&mx) == 0); + nng_mtx_lock(mx); + nng_mtx_unlock(mx); + + nng_mtx_lock(mx); + nng_mtx_unlock(mx); + nng_mtx_free(mx); + + // Verify that the mutexes are not always the same! + TEST_CHECK(nng_mtx_alloc(&mx) == 0); + TEST_CHECK(nng_mtx_alloc(&mx2) == 0); + TEST_CHECK(mx != mx2); + nng_mtx_free(mx); + nng_mtx_free(mx2); +} + +void +test_thread(void) +{ + nng_thread * thr; + int rv; + struct addarg aa; + + TEST_CHECK(nng_mtx_alloc(&aa.mx) == 0); + TEST_CHECK(nng_cv_alloc(&aa.cv, aa.mx) == 0); + aa.cnt = 0; + + TEST_CHECK((rv = nng_thread_create(&thr, add, &aa)) == 0); + nng_thread_destroy(thr); + TEST_CHECK(aa.cnt == 1); + + nng_cv_free(aa.cv); + nng_mtx_free(aa.mx); +} + +void +test_condvar(void) +{ + nng_thread * thr; + int rv; + struct addarg aa; + + TEST_CHECK(nng_mtx_alloc(&aa.mx) == 0); + TEST_CHECK(nng_cv_alloc(&aa.cv, aa.mx) == 0); + aa.cnt = 0; + + TEST_CHECK((rv = nng_thread_create(&thr, add, &aa)) == 0); + + nng_mtx_lock(aa.mx); + while (aa.cnt == 0) { + nng_cv_wait(aa.cv); + } + nng_mtx_unlock(aa.mx); + nng_thread_destroy(thr); + TEST_CHECK(aa.cnt == 1); + + nng_cv_free(aa.cv); + nng_mtx_free(aa.mx); +} + +TEST_LIST = { + { "sleep", test_sleep }, + { "clock", test_clock }, + { "mutex", test_mutex }, + { "thread", test_thread }, + { "condvar", test_condvar }, + { NULL, NULL }, +}; diff --git a/tests/reqrep.c b/tests/reqrep.c deleted file mode 100644 index 526891ca..00000000 --- a/tests/reqrep.c +++ /dev/null @@ -1,289 +0,0 @@ -// -// Copyright 2018 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. -// - -#include <string.h> - -#include <nng/nng.h> -#include <nng/protocol/reqrep0/rep.h> -#include <nng/protocol/reqrep0/req.h> -#include <nng/supplemental/util/platform.h> - -#include "convey.h" -#include "stubs.h" - -TestMain("REQ/REP pattern", { - int rv; - const char *addr = "inproc://test"; - Convey("We can create a REQ socket", { - nng_socket req; - - So(nng_req_open(&req) == 0); - - Reset({ nng_close(req); }); - - Convey("Protocol & peer options match", { - int p; - char *s; - So(nng_getopt_int(req, NNG_OPT_PROTO, &p) == 0); - So(p == 48); - So(nng_getopt_string(req, NNG_OPT_PROTONAME, &s) == 0); - So(strcmp(s, "req") == 0); - nng_strfree(s); - So(nng_getopt_int(req, NNG_OPT_PEER, &p) == 0); - So(p == 49); - So(nng_getopt_string(req, NNG_OPT_PEERNAME, &s) == 0); - So(strcmp(s, "rep") == 0); - nng_strfree(s); - }); - - Convey("Resend time option id works", { - // Set timeout. - So(nng_setopt_ms(req, NNG_OPT_REQ_RESENDTIME, 10) == - 0); - // Check invalid size - So(nng_setopt(req, NNG_OPT_REQ_RESENDTIME, "", 1) == - NNG_EINVAL); - }); - - Convey("Recv with no send fails", { - nng_msg *msg; - rv = nng_recvmsg(req, &msg, 0); - So(rv == NNG_ESTATE); - }); - }); - - Convey("We can create a REP socket", { - nng_socket rep; - So(nng_rep_open(&rep) == 0); - - Reset({ nng_close(rep); }); - - Convey("Protocol & peer options match", { - int p; - char *s; - So(nng_getopt_int(rep, NNG_OPT_PROTO, &p) == 0); - So(p == 49); - So(nng_getopt_string(rep, NNG_OPT_PROTONAME, &s) == 0); - So(strcmp(s, "rep") == 0); - nng_strfree(s); - So(nng_getopt_int(rep, NNG_OPT_PEER, &p) == 0); - So(p == 48); - So(nng_getopt_string(rep, NNG_OPT_PEERNAME, &s) == 0); - So(strcmp(s, "req") == 0); - nng_strfree(s); - }); - - Convey("Send with no recv fails", { - nng_msg *msg; - rv = nng_msg_alloc(&msg, 0); - So(rv == 0); - rv = nng_sendmsg(rep, msg, 0); - So(rv == NNG_ESTATE); - nng_msg_free(msg); - }); - - Convey("Cannot set resend time", { - So(nng_setopt_ms(rep, NNG_OPT_REQ_RESENDTIME, 100) == - NNG_ENOTSUP); - }); - }); - - Convey("We can create a linked REQ/REP pair", { - nng_socket req; - nng_socket rep; - - So(nng_rep_open(&rep) == 0); - - So(nng_req_open(&req) == 0); - - Reset({ - nng_close(rep); - nng_close(req); - }); - - So(nng_listen(rep, addr, NULL, 0) == 0); - So(nng_dial(req, addr, NULL, 0) == 0); - - Convey("They can REQ/REP exchange", { - nng_msg *ping; - nng_msg *pong; - - So(nng_msg_alloc(&ping, 0) == 0); - So(nng_msg_append(ping, "ping", 5) == 0); - So(nng_msg_len(ping) == 5); - So(memcmp(nng_msg_body(ping), "ping", 5) == 0); - So(nng_sendmsg(req, ping, 0) == 0); - pong = NULL; - So(nng_recvmsg(rep, &pong, 0) == 0); - So(pong != NULL); - So(nng_msg_len(pong) == 5); - So(memcmp(nng_msg_body(pong), "ping", 5) == 0); - nng_msg_trim(pong, 5); - So(nng_msg_append(pong, "pong", 5) == 0); - So(nng_sendmsg(rep, pong, 0) == 0); - ping = 0; - So(nng_recvmsg(req, &ping, 0) == 0); - So(ping != NULL); - So(nng_msg_len(ping) == 5); - So(memcmp(nng_msg_body(ping), "pong", 5) == 0); - nng_msg_free(ping); - }); - }); - - Convey("Request cancellation works", { - nng_msg * abc; - nng_msg * def; - nng_msg * cmd; - nng_duration retry = 100; // 100 ms - - nng_socket req; - nng_socket rep; - - So(nng_rep_open(&rep) == 0); - - So(nng_req_open(&req) == 0); - - Reset({ - nng_close(rep); - nng_close(req); - }); - - So(nng_setopt_ms(req, NNG_OPT_REQ_RESENDTIME, retry) == 0); - So(nng_setopt_int(req, NNG_OPT_SENDBUF, 16) == 0); - - So(nng_msg_alloc(&abc, 0) == 0); - So(nng_msg_append(abc, "abc", 4) == 0); - So(nng_msg_alloc(&def, 0) == 0); - So(nng_msg_append(def, "def", 4) == 0); - - So(nng_listen(rep, addr, NULL, 0) == 0); - So(nng_dial(req, addr, NULL, 0) == 0); - - // Send req #1 (abc). - So(nng_sendmsg(req, abc, 0) == 0); - - // Sleep a bit. This is so that we ensure that our - // request gets to the far side. (If we cancel too - // fast, then our outgoing send will be canceled before - // it gets to the wire.) - nng_msleep(20); - - // Send the next next request ("def"). Note that - // the REP side server will have already buffered the receive - // request, and should simply be waiting for us to reply to - // abc. - So(nng_sendmsg(req, def, 0) == 0); - - // Receive the first request (should be abc) on the REP server. - So(nng_recvmsg(rep, &cmd, 0) == 0); - So(nng_msg_len(cmd) == 4); - So(strcmp(nng_msg_body(cmd), "abc") == 0); - - // REP sends the reply to first command. This will be - // discarded by the REQ server. - So(nng_sendmsg(rep, cmd, 0) == 0); - - // Now get the next command from the REP; should be "def". - So(nng_recvmsg(rep, &cmd, 0) == 0); - So(nng_msg_len(cmd) == 4); - So(strcmp(nng_msg_body(cmd), "def") == 0); - - // And send it back to REQ. - So(nng_sendmsg(rep, cmd, 0) == 0); - - // Try a req command. This should give back "def" - So(nng_recvmsg(req, &cmd, 0) == 0); - So(nng_msg_len(cmd) == 4); - So(strcmp(nng_msg_body(cmd), "def") == 0); - nng_msg_free(cmd); - }); - - Convey("Request cancellation aborts pending recv", { - nng_msg * abc; - nng_msg * def; - nng_msg * cmd; - nng_aio * aio; - nng_duration retry = 100; // 100 ms - - nng_socket req; - nng_socket rep; - - So(nng_rep_open(&rep) == 0); - - So(nng_req_open(&req) == 0); - So(nng_aio_alloc(&aio, NULL, NULL) == 0); - - Reset({ - nng_close(rep); - nng_close(req); - nng_aio_free(aio); - }); - - So(nng_setopt_ms(req, NNG_OPT_REQ_RESENDTIME, retry) == 0); - So(nng_setopt_int(req, NNG_OPT_SENDBUF, 16) == 0); - - So(nng_msg_alloc(&abc, 0) == 0); - So(nng_msg_append(abc, "abc", 4) == 0); - So(nng_msg_alloc(&def, 0) == 0); - So(nng_msg_append(def, "def", 4) == 0); - - So(nng_listen(rep, addr, NULL, 0) == 0); - So(nng_dial(req, addr, NULL, 0) == 0); - - // Send req #1 (abc). - So(nng_sendmsg(req, abc, 0) == 0); - - // Sleep a bit. This is so that we ensure that our - // request gets to the far side. (If we cancel too - // fast, then our outgoing send will be canceled before - // it gets to the wire.) - nng_msleep(20); - - nng_aio_set_timeout(aio, 1000); // an entire second - nng_recv_aio(req, aio); - - // Give time for this recv to post properly. - nng_msleep(20); - - // Send the next next request ("def"). Note that - // the REP side server will have already buffered the receive - // request, and should simply be waiting for us to reply to - // abc. - So(nng_sendmsg(req, def, 0) == 0); - - nng_aio_wait(aio); - So(nng_aio_result(aio) == NNG_ECANCELED); - - // Receive the first request (should be abc) on the REP server. - So(nng_recvmsg(rep, &cmd, 0) == 0); - So(nng_msg_len(cmd) == 4); - So(strcmp(nng_msg_body(cmd), "abc") == 0); - - // REP sends the reply to first command. This will be - // discarded by the REQ server. - So(nng_sendmsg(rep, cmd, 0) == 0); - - // Now get the next command from the REP; should be "def". - So(nng_recvmsg(rep, &cmd, 0) == 0); - So(nng_msg_len(cmd) == 4); - So(strcmp(nng_msg_body(cmd), "def") == 0); - - // And send it back to REQ. - So(nng_sendmsg(rep, cmd, 0) == 0); - - // Try a req command. This should give back "def" - So(nng_recvmsg(req, &cmd, 0) == 0); - So(nng_msg_len(cmd) == 4); - So(strcmp(nng_msg_body(cmd), "def") == 0); - nng_msg_free(cmd); - }); - - nng_fini(); -}) diff --git a/tests/sock.c b/tests/sock.c index 02cd0d43..cf3cc705 100644 --- a/tests/sock.c +++ b/tests/sock.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// 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 @@ -12,532 +12,615 @@ #include <nng/nng.h> #include <nng/protocol/pair1/pair.h> -#include <nng/protocol/pubsub0/sub.h> #include <nng/supplemental/util/platform.h> -#include "convey.h" -#include "stubs.h" -#include "trantest.h" - -#define SECONDS(x) ((x) *1000) - -TestMain("Socket Operations", { - atexit(nng_fini); - // Reset({ nng_fini(); }); - Reset({ nng_closeall(); }); - - Convey("We are able to open a PAIR socket", { - int rv; - nng_socket s1; - - So(nng_pair_open(&s1) == 0); - - Reset({ nng_close(s1); }); - - Convey("Recv with no pipes times out correctly", { - nng_msg * msg = NULL; - nng_duration to = 100; - uint64_t now; - - now = getms(); - So(now > 0); - So(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, to) == 0); - So(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); - So(msg == NULL); - So(getms() >= (now + to)); - So(getms() < (now + (to * 2))); - }); - - Convey("Recv nonblock with no pipes gives EAGAIN", { - nng_msg *msg = NULL; - So(nng_recvmsg(s1, &msg, NNG_FLAG_NONBLOCK) == - NNG_EAGAIN); - So(msg == NULL); - }); - - Convey("Send with no pipes times out correctly", { - nng_msg * msg = NULL; - nng_duration to = 100; - uint64_t now; - - // We cheat to get access to the core's clock. - So(nng_msg_alloc(&msg, 0) == 0); - So(msg != NULL); - now = getms(); - - So(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, to) == 0); - So(nng_sendmsg(s1, msg, 0) == NNG_ETIMEDOUT); - So(getms() >= (now + to)); - So(getms() < (now + (to * 2))); - nng_msg_free(msg); - }); - - Convey("We can set and get options", { - nng_duration to = 1234; - - So(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, to) == 0); - - Convey("Read only options handled properly", { - So(nng_setopt_int(s1, NNG_OPT_RECVFD, 0) == - NNG_EREADONLY); - So(nng_setopt_int(s1, NNG_OPT_SENDFD, 0) == - NNG_EREADONLY); - So(nng_setopt(s1, NNG_OPT_LOCADDR, "a", 1) == - NNG_EREADONLY); - }); - - Convey("Sockname option works", { - char name[128]; // 64 is max - char * allocd; - size_t sz; - sz = sizeof(name); - So(nng_getopt( - s1, NNG_OPT_SOCKNAME, name, &sz) == 0); - So(sz > 0 && sz < 64); - So(sz == strlen(name) + 1); - So(atoi(name) == (int) s1.id); - - So(nng_setopt( - s1, NNG_OPT_SOCKNAME, "hello", 6) == 0); - sz = sizeof(name); - So(nng_getopt( - s1, NNG_OPT_SOCKNAME, name, &sz) == 0); - So(sz == 6); - So(strcmp(name, "hello") == 0); - - memset(name, 'A', 64); - name[64] = '\0'; - - // strings must be NULL terminated - So(nng_setopt(s1, NNG_OPT_SOCKNAME, name, 5) == - NNG_EINVAL); - - So(nng_getopt_string( - s1, NNG_OPT_SOCKNAME, &allocd) == 0); - So(strlen(allocd) == 5); - So(strcmp(allocd, "hello") == 0); - nng_strfree(allocd); - }); - - Convey("Oversize sockname handled right", { - char name[256]; // 64 is max - size_t sz = sizeof(name); - memset(name, 'A', sz); - So(nng_setopt(s1, NNG_OPT_SOCKNAME, name, - sz) == NNG_EINVAL); - name[sz - 1] = '\0'; - So(nng_setopt(s1, NNG_OPT_SOCKNAME, name, - sz) == NNG_EINVAL); - - strcpy(name, "hello"); - So(nng_setopt( - s1, NNG_OPT_SOCKNAME, name, sz) == 0); - sz = sizeof(name); - memset(name, 'B', sz); - So(nng_getopt( - s1, NNG_OPT_SOCKNAME, name, &sz) == 0); - So(sz == 6); - So(strcmp(name, "hello") == 0); - }); - - Convey("RAW option works", { - bool raw; - So(nng_getopt_bool(s1, NNG_OPT_RAW, &raw) == - 0); - So(raw == false); - }); - - Convey("URL option works", { - char url[NNG_MAXADDRLEN]; - nng_listener l; - nng_dialer d; - size_t sz; - - So(nng_listener_create( - &l, s1, "inproc://url1") == 0); - So(nng_dialer_create( - &d, s1, "inproc://url2") == 0); - memset(url, 0, sizeof(url)); - sz = sizeof(url); - So(nng_listener_getopt( - l, NNG_OPT_URL, url, &sz) == 0); - So(strcmp(url, "inproc://url1") == 0); - So(nng_listener_setopt(l, NNG_OPT_URL, url, - sz) == NNG_EREADONLY); - sz = sizeof(url); - So(nng_dialer_getopt( - d, NNG_OPT_URL, url, &sz) == 0); - So(strcmp(url, "inproc://url2") == 0); - - So(nng_dialer_setopt(d, NNG_OPT_URL, url, - sz) == NNG_EREADONLY); - Reset({ - nng_dialer_close(d); - nng_listener_close(l); - }); - }); - - Convey("We can apply options before endpoint", { - nng_listener l; - char addr[NNG_MAXADDRLEN]; - size_t sz; - - trantest_next_address( - addr, "ipc:///tmp/lopt_%u"); - - So(nng_setopt_size( - s1, NNG_OPT_RECVMAXSZ, 543) == 0); - So(nng_listener_create(&l, s1, addr) == 0); - So(nng_listener_getopt_size( - l, NNG_OPT_RECVMAXSZ, &sz) == 0); - So(sz == 543); - - Convey("Endpoint option can be overridden", { - So(nng_listener_setopt_size(l, - NNG_OPT_RECVMAXSZ, 678) == 0); - So(nng_listener_getopt_size(l, - NNG_OPT_RECVMAXSZ, &sz) == 0); - So(sz == 678); - So(nng_getopt_size(s1, - NNG_OPT_RECVMAXSZ, &sz) == 0); - So(sz == 543); - }); - - Convey("And socket overrides again", { - So(nng_setopt_size(s1, - NNG_OPT_RECVMAXSZ, 911) == 0); - So(nng_listener_getopt_size(l, - NNG_OPT_RECVMAXSZ, &sz) == 0); - So(sz == 911); - }); - }); - Convey("Short size is not copied", { - size_t sz = 0; - to = 0; - So(nng_getopt(s1, NNG_OPT_SENDTIMEO, &to, - &sz) == NNG_EINVAL); - So(sz == sizeof(to)); - So(to == 0); - sz = 0; - So(nng_getopt(s1, NNG_OPT_RECONNMINT, &to, - &sz) == NNG_EINVAL); - - So(to == 0); - sz = 0; - So(nng_getopt(s1, NNG_OPT_RECONNMAXT, &to, - &sz) == NNG_EINVAL); - So(to == 0); - }); - - Convey("Correct size is copied", { - size_t sz = sizeof(to); - So(nng_getopt( - s1, NNG_OPT_SENDTIMEO, &to, &sz) == 0); - So(sz == sizeof(to)); - So(to == 1234); - }); - - Convey("Short size buf is not copied", { - int l = 5; - size_t sz = 0; - So(nng_getopt(s1, NNG_OPT_RECVBUF, &l, &sz) == - NNG_EINVAL); - So(sz == sizeof(l)); - So(l == 5); - }); - - Convey("Insane buffer size fails", { - So(nng_setopt_int(s1, NNG_OPT_RECVBUF, - 0x100000) == NNG_EINVAL); - So(nng_setopt_int(s1, NNG_OPT_RECVBUF, -200) == - NNG_EINVAL); - }); - - Convey("Negative timeout fails", { - So(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, -5) == - NNG_EINVAL); - }); - - Convey("Short timeout fails", { - size_t sz = sizeof(to) - 1; - to = 0; - So(nng_setopt(s1, NNG_OPT_RECVTIMEO, &to, - sz) == NNG_EINVAL); - So(nng_setopt(s1, NNG_OPT_RECONNMINT, &to, - sz) == NNG_EINVAL); - }); - - Convey("Cannot set raw", { - So(nng_setopt_bool(s1, NNG_OPT_RAW, true) == - NNG_EREADONLY); - }); - - Convey("Unsupported options fail", { - char *crap = "crap"; - So(nng_setopt(s1, NNG_OPT_SUB_SUBSCRIBE, crap, - strlen(crap)) == NNG_ENOTSUP); - }); - - Convey("Bogus sizes fail", { - size_t v; - int i; - - So(nng_setopt_size( - s1, NNG_OPT_RECVMAXSZ, 6550) == 0); - So(nng_getopt_size( - s1, NNG_OPT_RECVMAXSZ, &v) == 0); - So(v == 6550); - - v = 102400; - So(nng_setopt(s1, NNG_OPT_RECVMAXSZ, &v, 1) == - NNG_EINVAL); - So(nng_getopt_size( - s1, NNG_OPT_RECVMAXSZ, &v) == 0); - So(v == 6550); - - i = 42; - So(nng_setopt(s1, NNG_OPT_RECVBUF, &i, 1) == - NNG_EINVAL); - - if (sizeof(size_t) == 8) { - v = 0x10000; - v <<= 30; - So(nng_setopt_size(s1, - NNG_OPT_RECVMAXSZ, - v) == NNG_EINVAL); - So(nng_getopt_size(s1, - NNG_OPT_RECVMAXSZ, &v) == 0); - So(v == 6550); - } - }); - }); - - Convey("Bogus URLs not supported", { - Convey("Dialing fails properly", { - rv = nng_dial(s1, "bogus://1", NULL, 0); - So(rv == NNG_ENOTSUP); - }); - Convey("Listening fails properly", { - rv = nng_listen(s1, "bogus://2", NULL, 0); - So(rv == NNG_ENOTSUP); - }); - }); - - Convey("Dialing synch can get refused", { - rv = nng_dial(s1, "inproc://no", NULL, 0); - So(rv == NNG_ECONNREFUSED); - }); - - Convey("Dialing asynch does not get refused", { - char * buf; - size_t sz; - nng_socket s2; - char * a = "inproc://asy"; - So(nng_setopt_ms(s1, NNG_OPT_RECONNMINT, 10) == 0); - So(nng_setopt_ms(s1, NNG_OPT_RECONNMAXT, 10) == 0); - So(nng_dial(s1, a, NULL, NNG_FLAG_NONBLOCK) == 0); - Convey("And connects late", { - So(nng_pair_open(&s2) == 0); - Reset({ nng_close(s2); }); - So(nng_listen(s2, a, NULL, 0) == 0); - nng_msleep(100); - So(nng_send(s1, "abc", 4, 0) == 0); - So(nng_recv(s2, &buf, &sz, NNG_FLAG_ALLOC) == - 0); - So(sz == 4); - So(memcmp(buf, "abc", 4) == 0); - nng_free(buf, sz); - }); - }); - - Convey("Listening works", { - char * a = "inproc://here"; - nng_listener l = NNG_LISTENER_INITIALIZER; - - So(nng_listener_id(l) < 0); - rv = nng_listen(s1, a, &l, 0); - So(rv == 0); - So(nng_listener_id(l) > 0); - - Convey("Second listen fails ADDRINUSE", { - rv = nng_listen(s1, a, NULL, 0); - So(rv == NNG_EADDRINUSE); - }); - - Convey("We cannot try to start a listener again", - { So(nng_listener_start(l, 0) == NNG_ESTATE); }); - - Convey("We can connect to it", { - nng_socket s2 = NNG_SOCKET_INITIALIZER; - So(nng_socket_id(s2) < 0); - So(nng_pair_open(&s2) == 0); - Reset({ nng_close(s2); }); - So(nng_dial(s2, a, NULL, 0) == 0); - nng_close(s2); - }); - }); - - Convey("Dialer creation ok", { - nng_dialer ep = NNG_DIALER_INITIALIZER; - char * a = "tcp://127.0.0.1:2929"; - - So(nng_dialer_id(ep) < 0); - So(nng_dialer_create(&ep, s1, a) == 0); - So(nng_dialer_id(ep) > 0); - - Convey("Options work", { - size_t sz; - So(nng_dialer_setopt_size( - ep, NNG_OPT_RECVMAXSZ, 4321) == 0); - So(nng_dialer_getopt_size( - ep, NNG_OPT_RECVMAXSZ, &sz) == 0); - So(sz == 4321); - }); - - Convey("Cannot access as listener", { - bool b; - nng_listener l; - l.id = ep.id; - So(nng_listener_getopt_bool( - l, NNG_OPT_RAW, &b) == NNG_ENOENT); - So(nng_listener_close(l) == NNG_ENOENT); - }); - - Convey("Socket opts not for dialer", { - // Not appropriate for dialer. - So(nng_dialer_setopt_bool( - ep, NNG_OPT_RAW, true) == NNG_ENOTSUP); - So(nng_dialer_setopt_ms(ep, NNG_OPT_SENDTIMEO, - 1) == NNG_ENOTSUP); - So(nng_dialer_setopt_string(ep, - NNG_OPT_SOCKNAME, - "bogus") == NNG_ENOTSUP); - }); - - Convey("URL is readonly", { - So(nng_dialer_setopt_string(ep, NNG_OPT_URL, - "tcp://somewhere.else.com:8888") == - NNG_EREADONLY); - }); - Convey("Bad size checks", { - So(nng_dialer_setopt(ep, NNG_OPT_RECVMAXSZ, - "a", 1) == NNG_EINVAL); - }); - }); - - Convey("Listener creation ok", { - nng_listener ep; - char * a = "tcp://127.0.0.1:2929"; - So(nng_listener_create(&ep, s1, a) == 0); - Convey("Options work", { - size_t sz; - So(nng_listener_setopt_size( - ep, NNG_OPT_RECVMAXSZ, 4321) == 0); - So(nng_listener_getopt_size( - ep, NNG_OPT_RECVMAXSZ, &sz) == 0); - So(sz == 4321); - }); - Convey("Cannot access as dialer", { - bool b; - nng_dialer d; - d.id = ep.id; - So(nng_dialer_getopt_bool( - d, NNG_OPT_RAW, &b) == NNG_ENOENT); - So(nng_dialer_close(d) == NNG_ENOENT); - }); - - Convey("Socket opts not for listener", { - // Not appropriate for dialer. - So(nng_listener_setopt_bool( - ep, NNG_OPT_RAW, true) == NNG_ENOTSUP); - So(nng_listener_setopt_ms(ep, - NNG_OPT_RECONNMINT, 1) == NNG_ENOTSUP); - So(nng_listener_setopt_string(ep, - NNG_OPT_SOCKNAME, - "bogus") == NNG_ENOTSUP); - }); - - Convey("URL is readonly", { - So(nng_listener_setopt_string(ep, NNG_OPT_URL, - "tcp://somewhere.else.com:8888") == - NNG_EREADONLY); - }); - - Convey("Bad size checks", { - So(nng_listener_setopt(ep, NNG_OPT_RECVMAXSZ, - "a", 1) == NNG_EINVAL); - }); - }); - - Convey("Cannot access absent ep options", { - size_t s; - int i; - nng_duration t; - bool b; - nng_dialer d; - nng_listener l; - d.id = 1999; - l.id = 1999; - - So(nng_dialer_setopt_size(d, NNG_OPT_RECVMAXSZ, 10) == - NNG_ENOENT); - So(nng_listener_setopt_size( - l, NNG_OPT_RECVMAXSZ, 10) == NNG_ENOENT); - - s = 1; - So(nng_dialer_getopt_bool(d, NNG_OPT_RAW, &b) == - NNG_ENOENT); - So(nng_listener_getopt_bool(l, NNG_OPT_RAW, &b) == - NNG_ENOENT); - - So(nng_dialer_getopt_size(d, NNG_OPT_RECVMAXSZ, &s) == - NNG_ENOENT); - So(nng_listener_getopt_size( - l, NNG_OPT_RECVMAXSZ, &s) == NNG_ENOENT); - - So(nng_dialer_getopt_int(d, NNG_OPT_RAW, &i) == - NNG_ENOENT); - So(nng_listener_getopt_int(l, NNG_OPT_RAW, &i) == - NNG_ENOENT); - - So(nng_dialer_getopt_ms(d, NNG_OPT_RECVTIMEO, &t) == - NNG_ENOENT); - So(nng_listener_getopt_ms(l, NNG_OPT_SENDTIMEO, &t) == - NNG_ENOENT); - }); - - Convey("We can send and receive messages", { - nng_socket s2; - int len; - size_t sz; - nng_duration to = SECONDS(3); - char * buf; - char * a = "inproc://t1"; - - So(nng_pair_open(&s2) == 0); - Reset({ nng_close(s2); }); - - So(nng_setopt_int(s1, NNG_OPT_RECVBUF, 1) == 0); - So(nng_getopt_int(s1, NNG_OPT_RECVBUF, &len) == 0); - So(len == 1); - - So(nng_setopt_int(s1, NNG_OPT_SENDBUF, 1) == 0); - So(nng_setopt_int(s2, NNG_OPT_SENDBUF, 1) == 0); - - So(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, to) == 0); - So(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, to) == 0); - So(nng_setopt_ms(s2, NNG_OPT_SENDTIMEO, to) == 0); - So(nng_setopt_ms(s2, NNG_OPT_RECVTIMEO, to) == 0); - - So(nng_listen(s1, a, NULL, 0) == 0); - So(nng_dial(s2, a, NULL, 0) == 0); - - So(nng_send(s1, "abc", 4, 0) == 0); - So(nng_recv(s2, &buf, &sz, NNG_FLAG_ALLOC) == 0); - So(buf != NULL); - So(sz == 4); - So(memcmp(buf, "abc", 4) == 0); - nng_free(buf, sz); - }); - }); -}) +#include "acutest.h" +#include "testutil.h" + +void +test_recv_timeout(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg = NULL; + + TEST_CHECK(nng_pair_open(&s1) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, 10) == 0); + now = testutil_clock(); + TEST_CHECK(nng_recvmsg(s1, &msg, 0) == NNG_ETIMEDOUT); + TEST_CHECK(msg == NULL); + TEST_CHECK(testutil_clock() >= (now + 9)); + TEST_CHECK(testutil_clock() < (now + 500)); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_recv_nonblock(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg = NULL; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, 10) == 0); + now = testutil_clock(); + TEST_CHECK(nng_recvmsg(s1, &msg, NNG_FLAG_NONBLOCK) == NNG_EAGAIN); + TEST_CHECK(msg == NULL); + TEST_CHECK(testutil_clock() < (now + 500)); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_send_timeout(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg; + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_pair_open(&s1) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, 100) == 0); + now = testutil_clock(); + TEST_CHECK(nng_sendmsg(s1, msg, 0) == NNG_ETIMEDOUT); + TEST_CHECK(testutil_clock() >= (now + 9)); + TEST_CHECK(testutil_clock() < (now + 500)); + nng_msg_free(msg); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_send_nonblock(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg; + + TEST_CHECK(nng_msg_alloc(&msg, 0) == 0); + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, 500) == 0); + now = testutil_clock(); + TEST_CHECK(nng_sendmsg(s1, msg, NNG_FLAG_NONBLOCK) == NNG_EAGAIN); + TEST_CHECK(testutil_clock() < (now + 100)); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_readonly_options(void) +{ + nng_socket s1; + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_RECVFD, 0) == NNG_EREADONLY); + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_SENDFD, 0) == NNG_EREADONLY); + TEST_CHECK(nng_setopt(s1, NNG_OPT_LOCADDR, "a", 1) == NNG_EREADONLY); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_socket_base(void) +{ + nng_socket s1 = NNG_SOCKET_INITIALIZER; + + TEST_CHECK(nng_socket_id(s1) < 0); + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_socket_id(s1) > 0); + + // Cannot set bogus options + TEST_CHECK(nng_setopt_bool(s1, "BAD_OPT", false) == NNG_ENOTSUP); + + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_socket_name(void) +{ + nng_socket s1; + char name[128]; // 64 is max + char * str; + long id; + char * end; + size_t sz; + + sz = sizeof(name); + TEST_CHECK(nng_pair_open(&s1) == 0); + TEST_CHECK(nng_getopt(s1, NNG_OPT_SOCKNAME, name, &sz) == 0); + TEST_CHECK(sz > 0 && sz < 64); + TEST_CHECK(sz == strlen(name) + 1); + id = strtol(name, &end, 10); + TEST_CHECK(id == (long) s1.id); + TEST_CHECK(end != NULL && *end == '\0'); + + TEST_CHECK(nng_setopt(s1, NNG_OPT_SOCKNAME, "hello", 6) == 0); + sz = sizeof(name); + TEST_CHECK(nng_getopt(s1, NNG_OPT_SOCKNAME, name, &sz) == 0); + TEST_CHECK(sz == 6); + TEST_CHECK(strcmp(name, "hello") == 0); + + memset(name, 'A', 64); + name[64] = '\0'; + + // strings must be NULL terminated + TEST_CHECK(nng_setopt(s1, NNG_OPT_SOCKNAME, name, 5) == NNG_EINVAL); + + TEST_CHECK(nng_getopt_string(s1, NNG_OPT_SOCKNAME, &str) == 0); + TEST_ASSERT(str != NULL); + TEST_CHECK(strlen(str) == 5); + TEST_CHECK(strcmp(str, "hello") == 0); + nng_strfree(str); + + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_socket_name_oversize(void) +{ + nng_socket s1; + char name[256]; // 64 is max + size_t sz = sizeof(name); + + memset(name, 'A', sz); + TEST_CHECK(nng_pair_open(&s1) == 0); + + TEST_CHECK(nng_setopt(s1, NNG_OPT_SOCKNAME, name, sz) == NNG_EINVAL); + name[sz - 1] = '\0'; + TEST_CHECK(nng_setopt(s1, NNG_OPT_SOCKNAME, name, sz) == NNG_EINVAL); + + strcpy(name, "hello"); + TEST_CHECK(nng_setopt(s1, NNG_OPT_SOCKNAME, name, sz) == 0); + sz = sizeof(name); + memset(name, 'B', sz); + TEST_CHECK(nng_getopt(s1, NNG_OPT_SOCKNAME, name, &sz) == 0); + TEST_CHECK(sz == 6); + TEST_CHECK(strcmp(name, "hello") == 0); + + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_send_recv(void) +{ + nng_socket s1; + nng_socket s2; + int len; + size_t sz; + nng_duration to = 3000; // 3 seconds + char * buf; + char * a = "inproc://t1"; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&s2) == 0); + + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_RECVBUF, 1) == 0); + TEST_CHECK(nng_getopt_int(s1, NNG_OPT_RECVBUF, &len) == 0); + TEST_CHECK(len == 1); + + TEST_CHECK(nng_setopt_int(s1, NNG_OPT_SENDBUF, 1) == 0); + TEST_CHECK(nng_setopt_int(s2, NNG_OPT_SENDBUF, 1) == 0); + + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_SENDTIMEO, to) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECVTIMEO, to) == 0); + TEST_CHECK(nng_setopt_ms(s2, NNG_OPT_SENDTIMEO, to) == 0); + TEST_CHECK(nng_setopt_ms(s2, NNG_OPT_RECVTIMEO, to) == 0); + + TEST_CHECK(nng_listen(s1, a, NULL, 0) == 0); + TEST_CHECK(nng_dial(s2, a, NULL, 0) == 0); + + TEST_CHECK(nng_send(s1, "abc", 4, 0) == 0); + TEST_CHECK(nng_recv(s2, &buf, &sz, NNG_FLAG_ALLOC) == 0); + TEST_CHECK(buf != NULL); + TEST_CHECK(sz == 4); + TEST_CHECK(memcmp(buf, "abc", 4) == 0); + nng_free(buf, sz); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(s2) == 0); +} + +void +test_connection_refused(void) +{ + nng_socket s1; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_dial(s1, "inproc://no", NULL, 0) == NNG_ECONNREFUSED); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_late_connection(void) +{ + char * buf; + size_t sz; + nng_socket s1; + nng_socket s2; + char * a = "inproc://asy"; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&s2) == 0); + + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECONNMINT, 10) == 0); + TEST_CHECK(nng_setopt_ms(s1, NNG_OPT_RECONNMAXT, 10) == 0); + + TEST_CHECK(nng_dial(s1, a, NULL, NNG_FLAG_NONBLOCK) == 0); + TEST_CHECK(nng_listen(s2, a, NULL, 0) == 0); + nng_msleep(100); + TEST_CHECK(nng_send(s1, "abc", 4, 0) == 0); + TEST_CHECK(nng_recv(s2, &buf, &sz, NNG_FLAG_ALLOC) == 0); + TEST_CHECK(sz == 4); + TEST_CHECK(memcmp(buf, "abc", 4) == 0); + nng_free(buf, sz); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(s2) == 0); +} + +void +test_address_busy(void) +{ + char * a = "inproc://eaddrinuse"; + nng_listener l = NNG_LISTENER_INITIALIZER; + nng_dialer d = NNG_DIALER_INITIALIZER; + nng_socket s1; + nng_socket s2; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_pair1_open(&s2) == 0); + + TEST_CHECK(nng_listener_id(l) < 0); + TEST_CHECK(nng_listen(s1, a, &l, 0) == 0); + TEST_CHECK(nng_listener_id(l) > 0); + + // Cannot start another one. + TEST_CHECK(nng_listen(s1, a, NULL, 0) == NNG_EADDRINUSE); + + // We can't restart it -- it's already running + TEST_CHECK(nng_listener_start(l, 0) == NNG_ESTATE); + + // We can connect to it. + TEST_CHECK(nng_dialer_id(d) < 0); + TEST_CHECK(nng_dial(s2, a, &d, 0) == 0); + TEST_CHECK(nng_dialer_id(d) > 0); + + TEST_CHECK(nng_close(s1) == 0); + TEST_CHECK(nng_close(s2) == 0); +} + +void +test_endpoint_types(void) +{ + nng_socket s1; + nng_dialer d = NNG_DIALER_INITIALIZER; + nng_listener l = NNG_LISTENER_INITIALIZER; + nng_dialer d2; + nng_listener l2; + char * a = "inproc://mumble..."; + bool b; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + + TEST_CHECK(nng_dialer_id(d) < 0); + TEST_CHECK(nng_dialer_create(&d, s1, a) == 0); + TEST_CHECK(nng_dialer_id(d) > 0); + + // Forge a listener + l2.id = nng_dialer_id(d); + TEST_CHECK( + nng_listener_getopt_bool(l2, NNG_OPT_RAW, &b) == NNG_ENOENT); + TEST_CHECK(nng_listener_close(l2) == NNG_ENOENT); + TEST_CHECK(nng_dialer_close(d) == 0); + + TEST_CHECK(nng_listener_id(l) < 0); + TEST_CHECK(nng_listener_create(&l, s1, a) == 0); + TEST_CHECK(nng_listener_id(l) > 0); + + // Forge a dialer + d2.id = nng_listener_id(l); + TEST_CHECK(nng_dialer_getopt_bool(d2, NNG_OPT_RAW, &b) == NNG_ENOENT); + TEST_CHECK(nng_dialer_close(d2) == NNG_ENOENT); + TEST_CHECK(nng_listener_close(l) == 0); + + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_bad_url(void) +{ + nng_socket s1; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + TEST_CHECK(nng_dial(s1, "bogus://1", NULL, 0) == NNG_ENOTSUP); + TEST_CHECK(nng_listen(s1, "bogus://2", NULL, 0) == NNG_ENOTSUP); + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_url_option(void) +{ + nng_socket s1; + char url[NNG_MAXADDRLEN]; + nng_listener l; + nng_dialer d; + size_t sz; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + + // Listener + TEST_CHECK(nng_listener_create(&l, s1, "inproc://url1") == 0); + memset(url, 0, sizeof(url)); + sz = sizeof(url); + TEST_CHECK(nng_listener_getopt(l, NNG_OPT_URL, url, &sz) == 0); + TEST_CHECK(strcmp(url, "inproc://url1") == 0); + TEST_CHECK( + nng_listener_setopt(l, NNG_OPT_URL, url, sz) == NNG_EREADONLY); + sz = sizeof(url); + + // Dialer + TEST_CHECK(nng_dialer_create(&d, s1, "inproc://url2") == 0); + TEST_CHECK(nng_dialer_getopt(d, NNG_OPT_URL, url, &sz) == 0); + TEST_CHECK(strcmp(url, "inproc://url2") == 0); + TEST_CHECK( + nng_dialer_setopt(d, NNG_OPT_URL, url, sz) == NNG_EREADONLY); + + nng_close(s1); +} + +void +test_listener_options(void) +{ + nng_socket s1; + nng_listener l; + size_t sz; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + + // Create a listener with the specified options + TEST_CHECK(nng_setopt_size(s1, NNG_OPT_RECVMAXSZ, 543) == 0); + TEST_CHECK(nng_listener_create(&l, s1, "inproc://listener_opts") == 0); + TEST_CHECK(nng_listener_getopt_size(l, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 543); + + // Verify endpoint overrides + TEST_CHECK(nng_listener_setopt_size(l, NNG_OPT_RECVMAXSZ, 678) == 0); + TEST_CHECK(nng_listener_getopt_size(l, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 678); + TEST_CHECK(nng_getopt_size(s1, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 543); + + // And socket overrides again + TEST_CHECK(nng_setopt_size(s1, NNG_OPT_RECVMAXSZ, 911) == 0); + TEST_CHECK(nng_listener_getopt_size(l, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 911); + + // Cannot set invalid options + TEST_CHECK(nng_listener_setopt_size(l, "BAD_OPT", 1) == NNG_ENOTSUP); + TEST_CHECK(nng_listener_setopt_bool(l, NNG_OPT_RECVMAXSZ, true) == + NNG_EBADTYPE); + TEST_CHECK( + nng_listener_setopt(l, NNG_OPT_RECVMAXSZ, &sz, 1) == NNG_EINVAL); + + // Cannot set inappropriate options + TEST_CHECK(nng_listener_setopt_string(l, NNG_OPT_SOCKNAME, "1") == + NNG_ENOTSUP); + TEST_CHECK( + nng_listener_setopt_bool(l, NNG_OPT_RAW, true) == NNG_ENOTSUP); + TEST_CHECK( + nng_listener_setopt_ms(l, NNG_OPT_RECONNMINT, 1) == NNG_ENOTSUP); + TEST_CHECK(nng_listener_setopt_string(l, NNG_OPT_SOCKNAME, "bogus") == + NNG_ENOTSUP); + + // Read only options + TEST_CHECK(nng_listener_setopt_string( + l, NNG_OPT_URL, "inproc://junk") == NNG_EREADONLY); + + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_dialer_options(void) +{ + nng_socket s1; + nng_dialer d; + size_t sz; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + + // Create a listener with the specified options + TEST_CHECK(nng_setopt_size(s1, NNG_OPT_RECVMAXSZ, 543) == 0); + TEST_CHECK(nng_dialer_create(&d, s1, "inproc://dialer_opts") == 0); + TEST_CHECK(nng_dialer_getopt_size(d, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 543); + + // Verify endpoint overrides + TEST_CHECK(nng_dialer_setopt_size(d, NNG_OPT_RECVMAXSZ, 678) == 0); + TEST_CHECK(nng_dialer_getopt_size(d, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 678); + TEST_CHECK(nng_getopt_size(s1, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 543); + + // And socket overrides again + TEST_CHECK(nng_setopt_size(s1, NNG_OPT_RECVMAXSZ, 911) == 0); + TEST_CHECK(nng_dialer_getopt_size(d, NNG_OPT_RECVMAXSZ, &sz) == 0); + TEST_CHECK(sz == 911); + + // Cannot set invalid options + TEST_CHECK(nng_dialer_setopt_size(d, "BAD_OPT", 1) == NNG_ENOTSUP); + TEST_CHECK(nng_dialer_setopt_bool(d, NNG_OPT_RECVMAXSZ, true) == + NNG_EBADTYPE); + TEST_CHECK( + nng_dialer_setopt(d, NNG_OPT_RECVMAXSZ, &sz, 1) == NNG_EINVAL); + + // Cannot set inappropriate options + TEST_CHECK( + nng_dialer_setopt_string(d, NNG_OPT_SOCKNAME, "1") == NNG_ENOTSUP); + TEST_CHECK( + nng_dialer_setopt_bool(d, NNG_OPT_RAW, true) == NNG_ENOTSUP); + TEST_CHECK( + nng_dialer_setopt_ms(d, NNG_OPT_SENDTIMEO, 1) == NNG_ENOTSUP); + TEST_CHECK(nng_dialer_setopt_string(d, NNG_OPT_SOCKNAME, "bogus") == + NNG_ENOTSUP); + + // Read only options + TEST_CHECK(nng_dialer_setopt_string(d, NNG_OPT_URL, "inproc://junk") == + NNG_EREADONLY); + + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_endpoint_absent_options(void) +{ + size_t s; + int i; + nng_duration t; + bool b; + nng_dialer d; + nng_listener l; + d.id = 1999; + l.id = 1999; + + TEST_CHECK( + nng_dialer_setopt_size(d, NNG_OPT_RECVMAXSZ, 10) == NNG_ENOENT); + TEST_CHECK( + nng_listener_setopt_size(l, NNG_OPT_RECVMAXSZ, 10) == NNG_ENOENT); + + TEST_CHECK(nng_dialer_getopt_bool(d, NNG_OPT_RAW, &b) == NNG_ENOENT); + TEST_CHECK(nng_listener_getopt_bool(l, NNG_OPT_RAW, &b) == NNG_ENOENT); + + TEST_CHECK( + nng_dialer_getopt_size(d, NNG_OPT_RECVMAXSZ, &s) == NNG_ENOENT); + TEST_CHECK( + nng_listener_getopt_size(l, NNG_OPT_RECVMAXSZ, &s) == NNG_ENOENT); + + TEST_CHECK(nng_dialer_getopt_int(d, NNG_OPT_RAW, &i) == NNG_ENOENT); + TEST_CHECK(nng_listener_getopt_int(l, NNG_OPT_RAW, &i) == NNG_ENOENT); + + TEST_CHECK( + nng_dialer_getopt_ms(d, NNG_OPT_RECVTIMEO, &t) == NNG_ENOENT); + TEST_CHECK( + nng_listener_getopt_ms(l, NNG_OPT_SENDTIMEO, &t) == NNG_ENOENT); +} + +void +test_timeout_options(void) +{ + nng_socket s1; + nng_duration to; + size_t sz; + + char *cases[] = { + NNG_OPT_RECVTIMEO, + NNG_OPT_SENDTIMEO, + NNG_OPT_RECONNMAXT, + NNG_OPT_RECONNMINT, + NULL, + }; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + for (int i = 0; cases[i] != NULL; i++) { + bool b; + TEST_CASE(cases[i]); + + // Can't receive a duration into zero bytes. + sz = 0; + TEST_CHECK(nng_getopt(s1, cases[i], &to, &sz) == NNG_EINVAL); + + // Type mismatches + TEST_CHECK(nng_getopt_bool(s1, cases[i], &b) == NNG_EBADTYPE); + sz = 1; + TEST_CHECK(nng_getopt(s1, cases[i], &b, &sz) == NNG_EINVAL); + + // Can set a valid duration + TEST_CHECK(nng_setopt_ms(s1, cases[i], 1234) == 0); + TEST_CHECK(nng_getopt_ms(s1, cases[i], &to) == 0); + TEST_CHECK(to == 1234); + + to = 0; + sz = sizeof(to); + TEST_CHECK(nng_getopt(s1, cases[i], &to, &sz) == 0); + TEST_CHECK(to == 1234); + TEST_CHECK(sz == sizeof(to)); + + // Can't set a negative duration + TEST_CHECK(nng_setopt_ms(s1, cases[i], -5) == NNG_EINVAL); + + // Can't pass a buf too small for duration + sz = sizeof(to) - 1; + to = 1; + TEST_CHECK(nng_setopt(s1, cases[i], &to, sz) == NNG_EINVAL); + } + TEST_CHECK(nng_close(s1) == 0); +} + +void +test_size_options(void) +{ + nng_socket s1; + size_t val; + size_t sz; + char * opt; + + char *cases[] = { + NNG_OPT_RECVMAXSZ, + NULL, + }; + + TEST_CHECK(nng_pair1_open(&s1) == 0); + for (int i = 0; (opt = cases[i]) != NULL; i++) { + TEST_CASE(opt); + + // Can't receive a size into zero bytes. + sz = 0; + TEST_CHECK(nng_getopt(s1, opt, &val, &sz) == NNG_EINVAL); + + // Can set a valid duration + TEST_CHECK(nng_setopt_size(s1, opt, 1234) == 0); + TEST_CHECK(nng_getopt_size(s1, opt, &val) == 0); + TEST_CHECK(val == 1234); + + val = 0; + sz = sizeof(val); + TEST_CHECK(nng_getopt(s1, opt, &val, &sz) == 0); + TEST_CHECK(val == 1234); + TEST_CHECK(sz == sizeof(val)); + + // Can't pass a buf too small for size + sz = sizeof(val) - 1; + val = 1; + TEST_CHECK(nng_setopt(s1, opt, &val, sz) == NNG_EINVAL); + + // We limit the limit to 4GB. Clear it if you want to + // ship more than 4GB at a time. +#if defined(_WIN64) || defined(_LP64) + val = 0x10000u; + val <<= 30u; + TEST_CHECK(nng_setopt_size(s1, opt, val) == NNG_EINVAL); + TEST_CHECK(nng_getopt_size(s1, opt, &val) == 0); + TEST_CHECK(val == 1234); +#endif + } + TEST_CHECK(nng_close(s1) == 0); +} + +TEST_LIST = { + { "recv timeout", test_recv_timeout }, + { "recv non-block", test_recv_nonblock }, + { "send timeout", test_send_timeout }, + { "send non-block", test_send_nonblock }, + { "read only options", test_readonly_options }, + { "socket base", test_socket_base }, + { "socket name", test_socket_name }, + { "socket name oversize", test_socket_name_oversize }, + { "send recv", test_send_recv }, + { "connection refused", test_connection_refused }, + { "late connection", test_late_connection }, + { "address busy", test_address_busy }, + { "bad url", test_bad_url }, + { "url option", test_url_option }, + { "listener options", test_listener_options }, + { "dialer options", test_dialer_options }, + { "timeout options", test_timeout_options }, + { "size options", test_size_options }, + { "endpoint absent options", test_endpoint_absent_options }, + { "endpoint types", test_endpoint_types }, + + { NULL, NULL }, +}; diff --git a/tests/stubs.h b/tests/stubs.h index a8fa59e9..f178a9bc 100644 --- a/tests/stubs.h +++ b/tests/stubs.h @@ -72,7 +72,11 @@ fdready(int fd) case 1: return (true); default: +#ifdef CONVEY_H ConveyError("BAD POLL RETURN!"); +#elif defined(TEST_CHECK) + TEST_ASSERT(0); +#endif return (false); } } @@ -81,7 +85,9 @@ int nosocket(nng_socket *s) { (void) s; // not used +#ifdef CONVEY_H ConveySkip("Protocol unconfigured"); +#endif return (NNG_ENOTSUP); } @@ -94,14 +100,6 @@ test_htons(uint16_t in) return (in); } -#ifndef NNG_HAVE_REQ0 -#define nng_req0_open nosocket -#endif - -#ifndef NNG_HAVE_REP0 -#define nng_rep0_open nosocket -#endif - #ifndef NNG_HAVE_PUB0 #define nng_pub0_open nosocket #endif @@ -114,10 +112,6 @@ test_htons(uint16_t in) #define nng_pair0_open nosocket #endif -#ifndef NNG_HAVE_PAIR1 -#define nng_pair1_open nosocket -#endif - #ifndef NNG_HAVE_PUSH0 #define nng_push0_open nosocket #endif 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 diff --git a/tests/testutil.h b/tests/testutil.h new file mode 100644 index 00000000..97540f36 --- /dev/null +++ b/tests/testutil.h @@ -0,0 +1,49 @@ +// +// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech> +// +// 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. +// + +#ifndef TESTUTIL_H +#define TESTUTIL_H + +#include <stdbool.h> +#include <stdint.h> + +// The following headers are provided for test code convenience. +#include <nng/nng.h> + +#ifdef __cplusplus +extern "C" { +#endif + +// testutil_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 testutil_clock(void); + +// testutil_pollfd tests if the given file descriptor polls as readable. +extern bool testutil_pollfd(int); + +// testutil_htons is just htons portably. +extern uint16_t testutil_htons(uint16_t); + +// testutil_sleep sleeps the specified number of msec +extern void testutil_sleep(int); + +// testutil_next_port returns a new port number (presumably unique) +extern uint16_t testutil_next_port(void); + +// testutil_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 testutil_marry(nng_socket, nng_socket); + +#ifdef __cplusplus +}; +#endif + +#endif // TESTUTIL_H |
