diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-01-23 22:49:57 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-01-23 22:49:57 -0800 |
| commit | 7df0822d3ed58ee73918cac576c0b07363e84425 (patch) | |
| tree | 5cae69dc0dc3e609260e0bd99bb8743c1c1a28cc /tests | |
| parent | 91a0b46b6a63f1c2345279b831a02c972e7b1781 (diff) | |
| download | nng-7df0822d3ed58ee73918cac576c0b07363e84425.tar.gz nng-7df0822d3ed58ee73918cac576c0b07363e84425.tar.bz2 nng-7df0822d3ed58ee73918cac576c0b07363e84425.zip | |
Added a bunch more compatibility stuff.
I implemented the reqrep compatibility test, which uncovered a few
semantic issues I had in the REQ/REP protocol, which I've fixed.
There are still missing things. and at least one portion of the req/rep
test suite cannot be enabled until I add tuning of the reconnect timeout,
which is currently way too long (1 sec) for the test suite to work.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/CMakeLists.txt | 18 | ||||
| -rw-r--r-- | tests/compat_reqrep.c | 194 | ||||
| -rw-r--r-- | tests/compat_testutil.h | 224 |
3 files changed, 436 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2b7a816e..52c9fdba 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -50,6 +50,21 @@ if (NNG_TESTS) math (EXPR TEST_PORT "${TEST_PORT}+10") endmacro (add_nng_test) + macro (add_nng_compat_test NAME TIMEOUT) + list (APPEND all_tests ${NAME}) + add_executable (${NAME} ${NAME}.c) + target_link_libraries (${NAME} ${PROJECT_NAME}_static) + target_link_libraries (${NAME} ${NNG_REQUIRED_LIBRARIES}) + target_compile_definitions(${NAME} PUBLIC -DNNG_STATIC_LIB) + if (CMAKE_THREAD_LIBS_INIT) + target_link_libraries (${NAME} "${CMAKE_THREAD_LIBS_INIT}") + endif() + + add_test (NAME ${NAME} COMMAND ${NAME} -v ${TEST_PORT}) + set_tests_properties (${NAME} PROPERTIES TIMEOUT ${TIMEOUT}) + math (EXPR TEST_PORT "${TEST_PORT}+10") + endmacro (add_nng_compat_test) + else () macro (add_nng_test NAME TIMEOUT) endmacro (add_nng_test) @@ -69,3 +84,6 @@ add_nng_test(pubsub 5) add_nng_test(sock 5) add_nng_test(survey 5) add_nng_test(tcp 5) + +# compatbility tests +add_nng_compat_test(compat_reqrep 5) diff --git a/tests/compat_reqrep.c b/tests/compat_reqrep.c new file mode 100644 index 00000000..a418b8d9 --- /dev/null +++ b/tests/compat_reqrep.c @@ -0,0 +1,194 @@ +/* + Copyright (c) 2012 Martin Sustrik All rights reserved. + + 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. +*/ + +#include "nng_compat.h" + +#include "compat_testutil.h" + +#define SOCKET_ADDRESS "inproc://test" + +extern void nng_usleep(uint64_t); + +int main () +{ + int rc; + int rep1; + int rep2; + int req1; + int req2; + int resend_ivl; + char buf [7]; + int timeo; + + /* Test req/rep with full socket types. */ + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + req1 = test_socket (AF_SP, NN_REQ); + test_connect (req1, SOCKET_ADDRESS); + req2 = test_socket (AF_SP, NN_REQ); + test_connect (req2, SOCKET_ADDRESS); + + /* Check invalid sequence of sends and recvs. */ + rc = nn_send (rep1, "ABC", 3, 0); + nn_assert (rc == -1 && nn_errno () == EFSM); + rc = nn_recv (req1, buf, sizeof (buf), 0); + nn_assert (rc == -1 && nn_errno () == EFSM); + + /* Check fair queueing the requests. */ + test_send (req2, "ABC"); + test_recv (rep1, "ABC"); + test_send (rep1, "ABC"); + test_recv (req2, "ABC"); + + test_send (req1, "ABC"); + test_recv (rep1, "ABC"); + test_send (rep1, "ABC"); + test_recv (req1, "ABC"); + + test_close (rep1); + test_close (req1); + test_close (req2); + + /* Check load-balancing of requests. */ + req1 = test_socket (AF_SP, NN_REQ); + test_bind (req1, SOCKET_ADDRESS); + + rep1 = test_socket (AF_SP, NN_REP); + test_connect (rep1, SOCKET_ADDRESS); + + nng_usleep(10000); // ensure rep1 binds before rep2 + + rep2 = test_socket (AF_SP, NN_REP); + test_connect (rep2, SOCKET_ADDRESS); + + timeo = 500; + test_setsockopt (req1, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo)); + test_setsockopt (rep1, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo)); + test_setsockopt (rep2, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo)); + + // Give time for connections to settle -- required for LB stuff. + nng_usleep(100000); + + test_send (req1, "ABC"); + test_recv (rep1, "ABC"); + test_send (rep1, "ABC"); + test_recv (req1, "ABC"); + + test_send (req1, "ABC"); + test_recv (rep2, "ABC"); + test_send (rep2, "ABC"); + test_recv (req1, "ABC"); + + test_close (rep2); + test_close (rep1); + test_close (req1); + + /* Test re-sending of the request. */ + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + req1 = test_socket (AF_SP, NN_REQ); + test_connect (req1, SOCKET_ADDRESS); + resend_ivl = 100; + rc = nn_setsockopt (req1, NN_REQ, NN_REQ_RESEND_IVL, + &resend_ivl, sizeof (resend_ivl)); + errno_assert (rc == 0); + + test_send (req1, "ABC"); + test_recv (rep1, "ABC"); + /* The following waits for request to be resent */ + test_recv (rep1, "ABC"); + + test_close (req1); + test_close (rep1); + + +#if 0 // The default reconnect interval is waaay to large for this to pass. + /* Check sending a request when the peer is not available. (It should + be sent immediatelly when the peer comes online rather than relying + on the resend algorithm. */ + req1 = test_socket (AF_SP, NN_REQ); + timeo = 10; + rc = nn_setsockopt (req1, NN_SOL_SOCKET, NN_RECONNECT_IVL, + &timeo, sizeof (timeo)); + test_send (req1, "ABC"); + test_connect (req1, SOCKET_ADDRESS); + + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + timeo = 500; + rc = nn_setsockopt (rep1, NN_SOL_SOCKET, NN_RCVTIMEO, + &timeo, sizeof (timeo)); + printf("RC = %d errno %d (%s)\n", rc, errno, strerror(errno)); + errno_assert (rc == 0); + test_recv (rep1, "ABC"); + + test_close (req1); + test_close (rep1); +#endif + + /* Check removing socket request sent to (It should + be sent immediatelly to other peer rather than relying + on the resend algorithm). */ + req1 = test_socket (AF_SP, NN_REQ); + test_bind (req1, SOCKET_ADDRESS); + rep1 = test_socket (AF_SP, NN_REP); + test_connect (rep1, SOCKET_ADDRESS); + rep2 = test_socket (AF_SP, NN_REP); + nng_usleep(10000); // give time for rep1 to connect + test_connect (rep2, SOCKET_ADDRESS); + + timeo = 500; // Was 200, but Windows occasionally fails at that rate. + test_setsockopt (rep1, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo)); + test_setsockopt (rep2, NN_SOL_SOCKET, NN_RCVTIMEO, &timeo, sizeof (timeo)); + + test_send (req1, "ABC"); + /* We got request through rep1 */ + test_recv (rep1, "ABC"); + /* But instead replying we simulate crash */ + test_close (rep1); + /* The rep2 should get request immediately */ + test_recv (rep2, "ABC"); + /* Let's check it's delivered well */ + test_send (rep2, "REPLY"); + test_recv (req1, "REPLY"); + + test_close (req1); + test_close (rep2); + + /* Test cancelling delayed request */ + + req1 = test_socket (AF_SP, NN_REQ); + test_connect (req1, SOCKET_ADDRESS); + test_send (req1, "ABC"); + test_send (req1, "DEF"); + + rep1 = test_socket (AF_SP, NN_REP); + test_bind (rep1, SOCKET_ADDRESS); + timeo = 100; + test_recv (rep1, "DEF"); + + test_close (req1); + test_close (rep1); + + return 0; +} + diff --git a/tests/compat_testutil.h b/tests/compat_testutil.h new file mode 100644 index 00000000..58020331 --- /dev/null +++ b/tests/compat_testutil.h @@ -0,0 +1,224 @@ +/* + Copyright (c) 2013 Insollo Entertainment, LLC. All rights reserved. + Copyright 2015 Garrett D'Amore <garrett@damore.org> + Copyright 2016 Franklin "Snaipe" Mathieu <franklinmathieu@gmail.com> + + 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. +*/ + +// Note: This file started life in nanomsg. We have copied it, and adjusted +// it for validating the compatibility features of nanomsg. As much as +// possible we want to run tests from the nanomsg test suite unmodified. + +#ifndef TESTUTIL_H_INCLUDED +#define TESTUTIL_H_INCLUDED + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#define nn_err_strerror nn_strerror +#define nn_err_abort abort +#define nn_assert assert +#define errno_assert assert +#define alloc_assert(x) assert(x != NULL) + +static int test_socket_impl (char *file, int line, int family, int protocol); +static int test_connect_impl (char *file, int line, int sock, char *address); +static int test_bind_impl (char *file, int line, int sock, char *address); +static void test_close_impl (char *file, int line, int sock); +static void test_send_impl (char *file, int line, int sock, char *data); +static void test_recv_impl (char *file, int line, int sock, char *data); +static void test_drop_impl (char *file, int line, int sock, int err); +static int test_setsockopt_impl (char *file, int line, int sock, int level, + int option, const void *optval, size_t optlen); + +#define test_socket(f, p) test_socket_impl (__FILE__, __LINE__, (f), (p)) +#define test_connect(s, a) test_connect_impl (__FILE__, __LINE__, (s), (a)) +#define test_bind(s, a) test_bind_impl (__FILE__, __LINE__, (s), (a)) +#define test_send(s, d) test_send_impl (__FILE__, __LINE__, (s), (d)) +#define test_recv(s, d) test_recv_impl (__FILE__, __LINE__, (s), (d)) +#define test_drop(s, e) test_drop_impl (__FILE__, __LINE__, (s), (e)) +#define test_close(s) test_close_impl (__FILE__, __LINE__, (s)) +#define test_setsockopt(s, l, o, v, z) test_setsockopt_impl (__FILE__, \ + __LINE__, (s), (l), (o), (v), (z)) + +static int test_socket_impl (char *file, int line, int family, + int protocol) +{ + int sock; + + sock = nn_socket (family, protocol); + if (sock == -1) { + fprintf (stderr, "Failed create socket: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + + return sock; +} + +static int test_connect_impl (char *file, int line, int sock, char *address) +{ + int rc; + + rc = nn_connect (sock, address); + if(rc < 0) { + fprintf (stderr, "Failed connect to \"%s\": %s [%d] (%s:%d)\n", + address, + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static int test_bind_impl (char *file, int line, int sock, char *address) +{ + int rc; + + rc = nn_bind (sock, address); + if(rc < 0) { + fprintf (stderr, "Failed bind to \"%s\": %s [%d] (%s:%d)\n", + address, + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static int test_setsockopt_impl (char *file, int line, + int sock, int level, int option, const void *optval, size_t optlen) +{ + int rc; + + rc = nn_setsockopt (sock, level, option, optval, optlen); + if(rc < 0) { + fprintf (stderr, "Failed set option \"%d\": %s [%d] (%s:%d)\n", + option, + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + return rc; +} + +static void test_close_impl (char *file, int line, int sock) +{ + int rc; + + rc = nn_close (sock); + if ((rc != 0) && (errno != EBADF && errno != ETERM)) { + fprintf (stderr, "Failed to close socket: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } +} + +static void test_send_impl (char *file, int line, + int sock, char *data) +{ + size_t data_len; + int rc; + + data_len = strlen (data); + + rc = nn_send (sock, data, data_len, 0); + if (rc < 0) { + fprintf (stderr, "Failed to send: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + if (rc != (int)data_len) { + fprintf (stderr, "Data to send is truncated: %d != %d (%s:%d)\n", + rc, (int) data_len, + file, line); + nn_err_abort (); + } +} + +static void test_recv_impl (char *file, int line, int sock, char *data) +{ + size_t data_len; + int rc; + char *buf; + + data_len = strlen (data); + /* We allocate plus one byte so that we are sure that message received + has correct length and not truncated */ + buf = malloc (data_len+1); + alloc_assert (buf); + + rc = nn_recv (sock, buf, data_len+1, 0); + if (rc < 0) { + fprintf (stderr, "Failed to recv: %s [%d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, file, line); + nn_err_abort (); + } + if (rc != (int)data_len) { + fprintf (stderr, "Received data has wrong length: %d != %d (%s:%d)\n", + rc, (int) data_len, + file, line); + nn_err_abort (); + } + if (memcmp (data, buf, data_len) != 0) { + /* We don't print the data as it may have binary garbage */ + fprintf (stderr, "Received data is wrong (%s:%d)\n", file, line); + nn_err_abort (); + } + + free (buf); +} + +static void test_drop_impl (char *file, int line, int sock, int err) +{ + int rc; + char buf[1024]; + + rc = nn_recv (sock, buf, sizeof (buf), 0); + if (rc < 0 && err != errno) { + fprintf (stderr, "Got wrong err to recv: %s [%d != %d] (%s:%d)\n", + nn_err_strerror (errno), + (int) errno, err, file, line); + nn_err_abort (); + } else if (rc >= 0) { + fprintf (stderr, "Did not drop message: [%d bytes] (%s:%d)\n", + rc, file, line); + nn_err_abort (); + } +} + +static int get_test_port (int argc, const char *argv[]) +{ + return atoi(argc < 2 ? "5555" : argv[1]); +} + +static void test_addr_from (char *out, const char *proto, + const char *ip, int port) +{ + sprintf(out, "%s://%s:%d", proto, ip, port); +} + +#endif |
