From b826bfc171d90f8bde7bd672c0ac14201b8b2742 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Mon, 16 Nov 2020 20:44:29 -0800 Subject: Work for test refactoring. There are a few major areas in this change. * CMake options are now located in a common cmake/NNGOptions.cmake file. This should make it easier for folks to figure out what the options are, and how they are used. * Tests are now scoped with their directory name, which should avoid possible name collisions with test names. * A number of tests have been either moved or incorporated into the newer testutil/acutest framework. We are moving away from my old c-convey framework to something easier to debug. * We use CMake directories a bit more extensively leading to a much cleaner CMake structure. It's not complete, but a big step in the right direction, and a preview of future work. * Tests are now run with verbose flags, so we get more test results in the CI/CD logs. --- src/core/CMakeLists.txt | 9 + src/core/aio_test.c | 245 +++++++++++++++++ src/core/buf_size_test.c | 119 ++++++++ src/core/errors_test.c | 46 ++++ src/core/id_test.c | 275 +++++++++++++++++++ src/core/reconnect_test.c | 167 +++++++++++ src/core/sock_test.c | 685 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/url_test.c | 479 ++++++++++++++++++++++++++++++++ 8 files changed, 2025 insertions(+) create mode 100644 src/core/aio_test.c create mode 100644 src/core/buf_size_test.c create mode 100644 src/core/errors_test.c create mode 100644 src/core/id_test.c create mode 100644 src/core/reconnect_test.c create mode 100644 src/core/sock_test.c create mode 100644 src/core/url_test.c (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9ecc46f8..2c954938 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -8,6 +8,7 @@ # # Core. +nng_directory(core) nng_check_sym(strlcpy string.h NNG_HAVE_STRLCPY) nng_check_sym(strnlen string.h NNG_HAVE_STRNLEN) @@ -75,3 +76,11 @@ nng_sources( url.c url.h ) + +nng_test(aio_test) +nng_test(buf_size_test) +nng_test(errors_test) +nng_test(id_test) +nng_test(reconnect_test) +nng_test(sock_test) +nng_test(url_test) diff --git a/src/core/aio_test.c b/src/core/aio_test.c new file mode 100644 index 00000000..787b6149 --- /dev/null +++ b/src/core/aio_test.c @@ -0,0 +1,245 @@ +// +// Copyright 2020 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// +// 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 + +#include +#include +#include + +#include "acutest.h" +#include "testutil.h" + +static void +cb_done(void *p) +{ + (*(int *) p)++; +} + +static void +sleep_done(void *arg) +{ + *(nng_time *) arg = nng_clock(); +} + +static void +cancel(nng_aio *aio, void *arg, int rv) +{ + *(int *) arg = rv; + nng_aio_finish(aio, rv); +} + +void +test_sleep(void) +{ + nng_time start; + nng_time end = 0; + nng_aio *aio; + + TEST_NNG_PASS(nng_aio_alloc(&aio, sleep_done, &end)); + start = nng_clock(); + nng_sleep_aio(200, aio); + nng_aio_wait(aio); + TEST_NNG_PASS(nng_aio_result(aio)); + 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(aio); +} + +void +test_sleep_timeout(void) +{ + nng_time start; + nng_time end = 0; + nng_aio *aio; + + TEST_CHECK(nng_aio_alloc(&aio, sleep_done, &end) == 0); + nng_aio_set_timeout(aio, 100); + start = nng_clock(); + nng_sleep_aio(2000, aio); + nng_aio_wait(aio); + TEST_NNG_FAIL(nng_aio_result(aio), 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(aio); +} + +void +test_insane_nio(void) +{ + nng_aio *aio; + nng_iov iov; + + TEST_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + TEST_NNG_FAIL(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_NNG_PASS(nng_aio_alloc(&aio, NULL, NULL)); + TEST_CHECK(nng_aio_begin(aio) == true); + nng_aio_defer(aio, cancel, &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, cb_done, &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 * tx_aio; + nng_aio * rx_aio; + int tx_done = 0; + int rx_done = 0; + nng_msg * m; + char * addr = "inproc://traffic"; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_PASS(nng_pair1_open(&s2)); + + TEST_NNG_PASS(nng_listen(s1, addr, NULL, 0)); + TEST_NNG_PASS(nng_dial(s2, addr, NULL, 0)); + + TEST_NNG_PASS(nng_aio_alloc(&rx_aio, cb_done, &rx_done)); + TEST_NNG_PASS(nng_aio_alloc(&tx_aio, cb_done, &tx_done)); + + nng_aio_set_timeout(rx_aio, 1000); + nng_aio_set_timeout(tx_aio, 1000); + + TEST_NNG_PASS(nng_msg_alloc(&m, 0)); + TEST_NNG_PASS(nng_msg_append(m, "hello", strlen("hello"))); + + nng_recv_aio(s2, rx_aio); + + nng_aio_set_msg(tx_aio, m); + nng_send_aio(s1, tx_aio); + + nng_aio_wait(tx_aio); + nng_aio_wait(rx_aio); + + TEST_NNG_PASS(nng_aio_result(rx_aio)); + TEST_NNG_PASS(nng_aio_result(tx_aio)); + + TEST_CHECK((m = nng_aio_get_msg(rx_aio)) != 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(rx_done == 1); + TEST_CHECK(tx_done == 1); + + nng_aio_free(rx_aio); + nng_aio_free(tx_aio); + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +} + +void +test_explicit_timeout(void) +{ + nng_socket s; + nng_aio * a; + int done = 0; + + TEST_NNG_PASS(nng_pair1_open(&s)); + TEST_NNG_PASS(nng_aio_alloc(&a, cb_done, &done)); + nng_aio_set_timeout(a, 40); + nng_recv_aio(s, a); + nng_aio_wait(a); + TEST_CHECK(done == 1); + TEST_NNG_FAIL(nng_aio_result(a), NNG_ETIMEDOUT); + nng_aio_free(a); + TEST_NNG_PASS(nng_close(s)); +} + +void +test_inherited_timeout(void) +{ + nng_socket s; + nng_aio * a; + int done = 0; + + TEST_NNG_PASS(nng_pair1_open(&s)); + TEST_NNG_PASS(nng_aio_alloc(&a, cb_done, &done)); + TEST_NNG_PASS(nng_socket_set_ms(s, NNG_OPT_RECVTIMEO, 40)); + nng_recv_aio(s, a); + nng_aio_wait(a); + TEST_CHECK(done == 1); + TEST_NNG_FAIL(nng_aio_result(a), NNG_ETIMEDOUT); + nng_aio_free(a); + TEST_NNG_PASS(nng_close(s)); +} + +void +test_zero_timeout(void) +{ + nng_socket s; + nng_aio * a; + int done = 0; + + TEST_NNG_PASS(nng_pair1_open(&s)); + TEST_NNG_PASS(nng_aio_alloc(&a, cb_done, &done)); + nng_aio_set_timeout(a, NNG_DURATION_ZERO); + nng_recv_aio(s, a); + nng_aio_wait(a); + TEST_CHECK(done == 1); + TEST_NNG_FAIL(nng_aio_result(a), NNG_ETIMEDOUT); + nng_aio_free(a); + TEST_NNG_PASS(nng_close(s)); +} + +TEST_LIST = { + { "sleep", test_sleep }, + { "sleep timeout", test_sleep_timeout }, + { "insane nio", test_insane_nio }, + { "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/src/core/buf_size_test.c b/src/core/buf_size_test.c new file mode 100644 index 00000000..172cb1f0 --- /dev/null +++ b/src/core/buf_size_test.c @@ -0,0 +1,119 @@ +// +// Copyright 2020 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// +// 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 +#include +#include + +#include + +#include "acutest.h" +#include "testutil.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_NNG_FAIL(nng_socket_get(s1, opt, &val, &sz), NNG_EINVAL); + + // Can set a valid size + TEST_NNG_PASS(nng_socket_set_int(s1, opt, 1234)); + TEST_NNG_PASS(nng_socket_get_int(s1, opt, &val)); + TEST_CHECK(val == 1234); + + val = 0; + sz = sizeof(val); + TEST_NNG_PASS(nng_socket_get(s1, opt, &val, &sz)); + TEST_CHECK(val == 1234); + TEST_CHECK(sz == sizeof(val)); + + // Can't set a negative size + TEST_NNG_FAIL(nng_socket_set_int(s1, opt, -5), NNG_EINVAL); + + // Can't pass a buf too small for size + sz = sizeof(val) - 1; + val = 1; + TEST_NNG_FAIL(nng_socket_set(s1, opt, &val, sz), NNG_EINVAL); + // Buffer sizes are limited to sane levels + TEST_NNG_FAIL( + nng_socket_set_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 nno = legacy[i]; + + TEST_CASE(opt); + + sz = sizeof(cnt); + TEST_NNG_PASS(nng_socket_set_int(s1, opt, 10)); + TEST_CHECK( + nn_getsockopt(os, NN_SOL_SOCKET, nno, &cnt, &sz) == 0); + TEST_CHECK(cnt == 10240); // 1k multiple + + cnt = 1; + TEST_CHECK( + nn_setsockopt(os, NN_SOL_SOCKET, nno, &cnt, sz) == 0); + TEST_CHECK( + nn_getsockopt(os, NN_SOL_SOCKET, nno, &cnt, &sz) == 0); + TEST_CHECK(cnt == 1024); // round up! + TEST_NNG_PASS(nng_socket_get_int(s1, opt, &cnt)); + TEST_CHECK(cnt == 1); + + TEST_CHECK( + nn_setsockopt(os, NN_SOL_SOCKET, nno, &cnt, 100) == -1); + TEST_CHECK(nn_errno() == EINVAL); + } + TEST_NNG_PASS(nng_close(s1)); +} + +TEST_LIST = { + { "buffer options", test_buffer_options }, + { "buffer legacy", test_buffer_legacy }, + { NULL, NULL }, +}; diff --git a/src/core/errors_test.c b/src/core/errors_test.c new file mode 100644 index 00000000..4e50467c --- /dev/null +++ b/src/core/errors_test.c @@ -0,0 +1,46 @@ +// +// Copyright 2020 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include +#include + +#include + +#include +#include + +static void +test_known_errors(void) +{ + + TEST_STREQUAL(nng_strerror(NNG_ECLOSED), "Object closed"); + TEST_STREQUAL(nng_strerror(NNG_ETIMEDOUT), "Timed out"); +} + +static void +test_unknown_errors(void) +{ + for (unsigned i = 1; i < 0x1000000; i = i * 2 + 100) { + TEST_CHECK(nng_strerror(i) != NULL); + } +} + +static void +test_system_errors(void) +{ + TEST_STREQUAL(nng_strerror(NNG_ESYSERR + ENOENT), strerror(ENOENT)); + TEST_STREQUAL(nng_strerror(NNG_ESYSERR + EINVAL), strerror(EINVAL)); +} + +TEST_LIST = { + { "known errors", test_known_errors }, + { "unknown errors", test_unknown_errors }, + { "system errors", test_system_errors }, + { NULL, NULL }, +}; diff --git a/src/core/id_test.c b/src/core/id_test.c new file mode 100644 index 00000000..8312b5cc --- /dev/null +++ b/src/core/id_test.c @@ -0,0 +1,275 @@ +// +// Copyright 2020 Staysail Systems, Inc. +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include "acutest.h" +#include "testutil.h" + +#include "idhash.h" + +void +test_basic(void) +{ + nni_id_map m; + char * five = "five"; + char * four = "four"; + + nni_id_map_init(&m, 0, 0, false); + + // insert it + TEST_NNG_PASS(nni_id_set(&m, 5, five)); + // retrieve it + TEST_CHECK(nni_id_get(&m, 5) == five); + + // change it + TEST_NNG_PASS(nni_id_set(&m, 5, four)); + TEST_CHECK(nni_id_get(&m, 5) == four); + + // delete + TEST_NNG_PASS(nni_id_remove(&m, 5)); + + nni_id_map_fini(&m); +} + +void +test_random(void) +{ + int i; + uint32_t id; + for (i = 0; i < 2; i++) { + nni_id_map m; + nni_id_map_init(&m, 0, 0, true); + TEST_NNG_PASS(nni_id_alloc(&m, &id, &id)); + nni_id_map_fini(&m); + TEST_CHECK(id != 0); + if (id != 1) { + break; + } + // one chance in 4 billion, but try again + } + + TEST_CHECK(id != 1); + TEST_CHECK(i < 2); +} + +void +test_collision(void) +{ + nni_id_map m; + char * five = "five"; + char * four = "four"; + + nni_id_map_init(&m, 0, 0, false); + + // Carefully crafted -- 13 % 8 == 5. + TEST_NNG_PASS(nni_id_set(&m, 5, five)); + TEST_NNG_PASS(nni_id_set(&m, 13, four)); + TEST_CHECK(nni_id_get(&m, 5) == five); + TEST_CHECK(nni_id_get(&m, 13) == four); + + // Delete the intermediate + TEST_NNG_PASS(nni_id_remove(&m, 5)); + TEST_CHECK(nni_id_get(&m, 13) == four); + + nni_id_map_fini(&m); +} + +void +test_empty(void) +{ + nni_id_map m; + nni_id_map_init(&m, 0, 0, false); + + TEST_CHECK(nni_id_get(&m, 42) == NULL); + TEST_NNG_FAIL(nni_id_remove(&m, 42), NNG_ENOENT); + TEST_NNG_FAIL(nni_id_remove(&m, 1), NNG_ENOENT); + nni_id_map_fini(&m); +} + +void +test_not_found(void) +{ + nni_id_map m; + uint32_t id; + nni_id_map_init(&m, 0, 0, false); + + TEST_NNG_PASS(nni_id_alloc(&m, &id, &id)); + TEST_NNG_FAIL(nni_id_remove(&m, 42), NNG_ENOENT); + TEST_NNG_FAIL(nni_id_remove(&m, 2), NNG_ENOENT); + TEST_NNG_PASS(nni_id_remove(&m, id)); + nni_id_map_fini(&m); +} + +void +test_resize(void) +{ + nni_id_map m; + int rv; + int i; + int expect[1024]; + + for (i = 0; i < 1024; i++) { + expect[i] = i; + } + + nni_id_map_init(&m, 0, 0, false); + + for (i = 0; i < 1024; i++) { + if ((rv = nni_id_set(&m, i, &expect[i])) != 0) { + TEST_NNG_PASS(rv); + } + } + + for (i = 0; i < 1024; i++) { + if ((rv = nni_id_remove(&m, i)) != 0) { + TEST_NNG_PASS(rv); + } + } + nni_id_map_fini(&m); +} + +void +test_dynamic(void) +{ + nni_id_map m; + int expect[5]; + uint32_t id; + + nni_id_map_init(&m, 10, 13, false); + + // We can fill the table. + TEST_NNG_PASS(nni_id_alloc(&m, &id, &expect[0])); + TEST_CHECK(id == 10); + TEST_NNG_PASS(nni_id_alloc(&m, &id, &expect[1])); + TEST_CHECK(id == 11); + TEST_NNG_PASS(nni_id_alloc(&m, &id, &expect[2])); + TEST_CHECK(id == 12); + TEST_NNG_PASS(nni_id_alloc(&m, &id, &expect[3])); + TEST_CHECK(id == 13); + + // Adding another fails. + TEST_NNG_FAIL(nni_id_alloc(&m, &id, &expect[4]), NNG_ENOMEM); + + // Delete one. + TEST_NNG_PASS(nni_id_remove(&m, 11)); + + // And now we can allocate one. + TEST_NNG_PASS(nni_id_alloc(&m, &id, &expect[4])); + TEST_CHECK(id == 11); + nni_id_map_fini(&m); +} + +void +test_set_out_of_range(void) +{ + nni_id_map m; + int x; + uint32_t id; + + nni_id_map_init(&m, 10, 13, false); + + // We can insert outside the range forcibly. + TEST_NNG_PASS(nni_id_set(&m, 1, &x)); + TEST_NNG_PASS(nni_id_set(&m, 100, &x)); + TEST_NNG_PASS(nni_id_alloc(&m, &id, &x)); + TEST_CHECK(id == 10); + nni_id_map_fini(&m); +} + +#define STRESS_LOAD 50000 +#define NUM_VALUES 1000 + +void +test_stress(void) +{ + void * values[NUM_VALUES]; + nni_id_map m; + size_t i; + int rv; + void * x; + int v; + + nni_id_map_init(&m, 0, 0, false); + for (i = 0; i < NUM_VALUES; i++) { + values[i] = NULL; + } + + for (i = 0; i < STRESS_LOAD; i++) { + v = rand() % NUM_VALUES; // Keep it constrained + + switch (rand() & 3) { + case 0: + x = &values[rand() % NUM_VALUES]; + values[v] = x; + if ((rv = nni_id_set(&m, v, x)) != 0) { + TEST_NNG_PASS(rv); + goto out; + } + break; + + case 1: + rv = nni_id_remove(&m, v); + if (values[v] == NULL) { + if (rv != NNG_ENOENT) { + TEST_NNG_FAIL(rv, NNG_ENOENT); + goto out; + } + } else { + values[v] = NULL; + if (rv != 0) { + TEST_NNG_PASS(rv); + goto out; + } + } + break; + case 2: + x = nni_id_get(&m, v); + if (x != values[v]) { + TEST_CHECK(x == values[v]); + goto out; + } + break; + } + } +out: + TEST_CHECK(i == STRESS_LOAD); + + // Post stress check. + for (i = 0; i < NUM_VALUES; i++) { + x = nni_id_get(&m, i); + if (x != values[i]) { + TEST_CHECK(x == values[i]); + break; + } + + // We only use the test macros if we know they are going + // to fail. Otherwise there will be too many errors reported. + rv = nni_id_remove(&m, i); + if ((x == NULL) && (rv != NNG_ENOENT)) { + TEST_NNG_FAIL(rv, NNG_ENOENT); + } else if ((x != NULL) && (rv != 0)) { + TEST_NNG_PASS(rv); + } + } + TEST_CHECK(i == NUM_VALUES); + + nni_id_map_fini(&m); +} + +TEST_LIST = { + { "basic", test_basic }, + { "random", test_random }, + { "collision", test_collision }, + { "empty", test_empty }, + { "not found", test_not_found }, + { "resize", test_resize }, + { "dynamic", test_dynamic }, + { "set out of range", test_set_out_of_range }, + { "stress", test_stress }, + { NULL, NULL }, +}; diff --git a/src/core/reconnect_test.c b/src/core/reconnect_test.c new file mode 100644 index 00000000..669261c9 --- /dev/null +++ b/src/core/reconnect_test.c @@ -0,0 +1,167 @@ +// +// Copyright 2020 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// +// 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 + +#include +#include +#include +#include + +#include "acutest.h" +#include "testutil.h" + +void +test_dial_before_listen(void) +{ + nng_socket push; + nng_socket pull; + char addr[64]; + + testutil_scratch_addr("inproc", sizeof(addr), addr); + + TEST_NNG_PASS(nng_push0_open(&push)); + TEST_NNG_PASS(nng_pull0_open(&pull)); + + TEST_NNG_PASS(nng_socket_set_ms(pull, NNG_OPT_RECONNMINT, 10)); + TEST_NNG_PASS(nng_socket_set_ms(pull, NNG_OPT_RECONNMAXT, 10)); + + TEST_NNG_PASS(nng_dial(pull, addr, NULL, NNG_FLAG_NONBLOCK)); + testutil_sleep(100); + TEST_NNG_PASS(nng_listen(push, addr, NULL, 0)); + + TEST_NNG_SEND_STR(push, "hello"); + TEST_NNG_RECV_STR(pull, "hello"); + + TEST_NNG_PASS(nng_close(push)); + TEST_NNG_PASS(nng_close(pull)); +} + +void +test_reconnect(void) +{ + nng_socket push; + nng_socket pull; + nng_listener l; + char addr[64]; + + testutil_scratch_addr("inproc", sizeof(addr), addr); + + TEST_NNG_PASS(nng_push0_open(&push)); + TEST_NNG_PASS(nng_pull0_open(&pull)); + + TEST_NNG_PASS(nng_socket_set_ms(pull, NNG_OPT_RECONNMINT, 10)); + TEST_NNG_PASS(nng_socket_set_ms(pull, NNG_OPT_RECONNMAXT, 10)); + + TEST_NNG_PASS(nng_dial(pull, addr, NULL, NNG_FLAG_NONBLOCK)); + testutil_sleep(100); + TEST_NNG_PASS(nng_listen(push, addr, &l, 0)); + + TEST_NNG_SEND_STR(push, "hello"); + TEST_NNG_RECV_STR(pull, "hello"); + + // Close the listener + TEST_NNG_PASS(nng_listener_close(l)); + + TEST_NNG_PASS(nng_listen(push, addr, &l, 0)); + TEST_NNG_SEND_STR(push, "again"); + TEST_NNG_RECV_STR(pull, "again"); + + TEST_NNG_PASS(nng_close(push)); + TEST_NNG_PASS(nng_close(pull)); +} + +void +test_reconnect_pipe(void) +{ + nng_socket push; + nng_socket pull; + nng_listener l; + nng_msg * msg; + char addr[64]; + + testutil_scratch_addr("inproc", sizeof(addr), addr); + + TEST_NNG_PASS(nng_push0_open(&push)); + TEST_NNG_PASS(nng_pull0_open(&pull)); + + TEST_NNG_PASS(nng_socket_set_ms(pull, NNG_OPT_RECONNMINT, 10)); + TEST_NNG_PASS(nng_socket_set_ms(pull, NNG_OPT_RECONNMAXT, 10)); + + TEST_NNG_PASS(nng_dial(pull, addr, NULL, NNG_FLAG_NONBLOCK)); + testutil_sleep(100); + TEST_NNG_PASS(nng_listen(push, addr, &l, 0)); + + TEST_NNG_SEND_STR(push, "hello"); + + TEST_NNG_PASS(nng_recvmsg(pull, &msg, 0)); + TEST_CHECK(msg != NULL); + TEST_CHECK(nng_msg_len(msg) == 6); + TEST_CHECK(strcmp(nng_msg_body(msg), "hello") == 0); + nng_pipe_close(nng_msg_get_pipe(msg)); + nng_msg_free(msg); + + // We have to wait a bit, because while we closed the pipe on the + // receiver, the receiver might not have got the update. If we + // send too soon, then the message gets routed to the sender pipe + // that is about to close. + testutil_sleep(100); + + // Reconnect should happen more ore less immediately. + TEST_NNG_SEND_STR(push, "again"); + TEST_NNG_RECV_STR(pull, "again"); + + TEST_NNG_PASS(nng_close(push)); + TEST_NNG_PASS(nng_close(pull)); +} + +void +test_reconnect_back_off_zero(void) +{ + nng_socket push; + nng_socket pull; + nng_time start; + char addr[64]; + testutil_scratch_addr("inproc", sizeof(addr), addr); + + TEST_NNG_PASS(nng_push0_open(&push)); + TEST_NNG_PASS(nng_pull0_open(&pull)); + + // redial every 10 ms. + TEST_NNG_PASS(nng_socket_set_ms(push, NNG_OPT_RECONNMAXT, 0)); + TEST_NNG_PASS(nng_socket_set_ms(push, NNG_OPT_RECONNMINT, 10)); + TEST_NNG_PASS(nng_dial(push, addr, NULL, NNG_FLAG_NONBLOCK)); + + // Start up the dialer first. It should keep retrying every 10 ms. + + // Wait 500 milliseconds. This gives a chance for an exponential + // back-off to increase to a longer time. It should by this point + // be well over 100 and probably closer to 200 ms. + nng_msleep(500); + + start = nng_clock(); + TEST_NNG_PASS(nng_listen(pull, addr, NULL, 0)); + + TEST_NNG_SEND_STR(push, "hello"); + TEST_NNG_RECV_STR(pull, "hello"); + + TEST_CHECK(nng_clock() - start < 100); + + TEST_NNG_PASS(nng_close(push)); + TEST_NNG_PASS(nng_close(pull)); +} + +TEST_LIST = { + { "dial before listen", test_dial_before_listen }, + { "reconnect", test_reconnect }, + { "reconnect back-off zero", test_reconnect_back_off_zero }, + { "reconnect pipe", test_reconnect_pipe }, + { NULL, NULL }, +}; \ No newline at end of file diff --git a/src/core/sock_test.c b/src/core/sock_test.c new file mode 100644 index 00000000..ab557632 --- /dev/null +++ b/src/core/sock_test.c @@ -0,0 +1,685 @@ +// +// Copyright 2020 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// +// 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 + +#include +#include +#include + +#include "acutest.h" +#include "testutil.h" + +void +test_recv_timeout(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg = NULL; + + TEST_NNG_PASS(nng_pair_open(&s1)); + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 10)); + now = testutil_clock(); + TEST_NNG_FAIL(nng_recvmsg(s1, &msg, 0), NNG_ETIMEDOUT); + TEST_CHECK(msg == NULL); + TEST_CHECK(testutil_clock() >= (now + 9)); + TEST_CHECK(testutil_clock() < (now + 500)); + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_recv_nonblock(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg = NULL; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, 10)); + now = testutil_clock(); + TEST_NNG_FAIL(nng_recvmsg(s1, &msg, NNG_FLAG_NONBLOCK), NNG_EAGAIN); + TEST_CHECK(msg == NULL); + TEST_CHECK(testutil_clock() < (now + 500)); + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_send_timeout(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg; + + TEST_NNG_PASS(nng_msg_alloc(&msg, 0)); + TEST_NNG_PASS(nng_pair_open(&s1)); + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 100)); + now = testutil_clock(); + TEST_NNG_FAIL(nng_sendmsg(s1, msg, 0), NNG_ETIMEDOUT); + TEST_CHECK(testutil_clock() >= (now + 9)); + TEST_CHECK(testutil_clock() < (now + 500)); + nng_msg_free(msg); + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_send_nonblock(void) +{ + nng_socket s1; + uint64_t now; + nng_msg * msg; + + TEST_NNG_PASS(nng_msg_alloc(&msg, 0)); + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, 500)); + now = testutil_clock(); + TEST_NNG_FAIL(nng_sendmsg(s1, msg, NNG_FLAG_NONBLOCK), NNG_EAGAIN); + TEST_CHECK(testutil_clock() < (now + 100)); + TEST_NNG_PASS(nng_close(s1)); + nng_msg_free(msg); +} + +void +test_readonly_options(void) +{ + nng_socket s1; + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_FAIL( + nng_socket_set_int(s1, NNG_OPT_RECVFD, 0), NNG_EREADONLY); + TEST_NNG_FAIL( + nng_socket_set_int(s1, NNG_OPT_SENDFD, 0), NNG_EREADONLY); + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_socket_base(void) +{ + nng_socket s1 = NNG_SOCKET_INITIALIZER; + + TEST_CHECK(nng_socket_id(s1) < 0); + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_CHECK(nng_socket_id(s1) > 0); + + // Cannot set bogus options + TEST_NNG_FAIL(nng_socket_set_bool(s1, "BAD_OPT", false), NNG_ENOTSUP); + + TEST_NNG_PASS(nng_close(s1)); +} + +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_NNG_PASS(nng_pair_open(&s1)); + TEST_NNG_PASS(nng_socket_get(s1, NNG_OPT_SOCKNAME, name, &sz)); + 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_NNG_PASS(nng_socket_set(s1, NNG_OPT_SOCKNAME, "hello", 6)); + sz = sizeof(name); + TEST_NNG_PASS(nng_socket_get(s1, NNG_OPT_SOCKNAME, name, &sz)); + TEST_CHECK(sz == 6); + TEST_STREQUAL(name, "hello"); + + memset(name, 'A', 64); + name[64] = '\0'; + + // strings must be NULL terminated + TEST_NNG_FAIL( + nng_socket_set(s1, NNG_OPT_SOCKNAME, name, 5), NNG_EINVAL); + + TEST_NNG_PASS(nng_socket_get_string(s1, NNG_OPT_SOCKNAME, &str)); + TEST_ASSERT(str != NULL); + TEST_CHECK(strlen(str) == 5); + TEST_STREQUAL(str, "hello"); + nng_strfree(str); + + TEST_NNG_PASS(nng_close(s1)); +} + +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_NNG_PASS(nng_pair_open(&s1)); + + TEST_NNG_FAIL( + nng_socket_set(s1, NNG_OPT_SOCKNAME, name, sz), NNG_EINVAL); + name[sz - 1] = '\0'; + TEST_NNG_FAIL( + nng_socket_set(s1, NNG_OPT_SOCKNAME, name, sz), NNG_EINVAL); + + strcpy(name, "hello"); + TEST_NNG_PASS(nng_socket_set(s1, NNG_OPT_SOCKNAME, name, sz)); + sz = sizeof(name); + memset(name, 'B', sz); + TEST_NNG_PASS(nng_getopt(s1, NNG_OPT_SOCKNAME, name, &sz)); + TEST_CHECK(sz == 6); + TEST_STREQUAL(name, "hello"); + TEST_NNG_PASS(nng_close(s1)); +} + +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_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_PASS(nng_pair1_open(&s2)); + + TEST_NNG_PASS(nng_socket_set_int(s1, NNG_OPT_RECVBUF, 1)); + TEST_NNG_PASS(nng_socket_get_int(s1, NNG_OPT_RECVBUF, &len)); + TEST_CHECK(len == 1); + + TEST_NNG_PASS(nng_socket_set_int(s1, NNG_OPT_SENDBUF, 1)); + TEST_NNG_PASS(nng_socket_set_int(s2, NNG_OPT_SENDBUF, 1)); + + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, to)); + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, to)); + TEST_NNG_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, to)); + TEST_NNG_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, to)); + + TEST_NNG_PASS(nng_listen(s1, a, NULL, 0)); + TEST_NNG_PASS(nng_dial(s2, a, NULL, 0)); + + TEST_NNG_PASS(nng_send(s1, "abc", 4, 0)); + TEST_NNG_PASS(nng_recv(s2, &buf, &sz, NNG_FLAG_ALLOC)); + TEST_CHECK(buf != NULL); + TEST_CHECK(sz == 4); + TEST_CHECK(memcmp(buf, "abc", 4) == 0); + nng_free(buf, sz); + + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +} + +void +test_send_recv_zero_length(void) +{ + nng_socket s1; + nng_socket s2; + int len; + size_t sz; + nng_duration to = 3000; // 3 seconds + char * buf; + char * a = "inproc://send-recv-zero-length"; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_PASS(nng_pair1_open(&s2)); + + TEST_NNG_PASS(nng_socket_set_int(s1, NNG_OPT_RECVBUF, 1)); + TEST_NNG_PASS(nng_getopt_int(s1, NNG_OPT_RECVBUF, &len)); + TEST_CHECK(len == 1); + + TEST_NNG_PASS(nng_socket_set_int(s1, NNG_OPT_SENDBUF, 1)); + TEST_NNG_PASS(nng_socket_set_int(s2, NNG_OPT_SENDBUF, 1)); + + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_SENDTIMEO, to)); + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_RECVTIMEO, to)); + TEST_NNG_PASS(nng_socket_set_ms(s2, NNG_OPT_SENDTIMEO, to)); + TEST_NNG_PASS(nng_socket_set_ms(s2, NNG_OPT_RECVTIMEO, to)); + + TEST_NNG_PASS(nng_listen(s1, a, NULL, 0)); + TEST_NNG_PASS(nng_dial(s2, a, NULL, 0)); + + TEST_NNG_PASS(nng_send(s1, "", 0, 0)); + TEST_NNG_PASS(nng_recv(s2, &buf, &sz, NNG_FLAG_ALLOC)); + TEST_CHECK(buf == NULL); + TEST_CHECK(sz == 0); + nng_free(buf, sz); + + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +} + +void +test_connection_refused(void) +{ + nng_socket s1; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_FAIL(nng_dial(s1, "inproc://no", NULL, 0), NNG_ECONNREFUSED); + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_late_connection(void) +{ + char * buf; + size_t sz; + nng_socket s1; + nng_socket s2; + char * a = "inproc://asy"; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_PASS(nng_pair1_open(&s2)); + + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_RECONNMINT, 10)); + TEST_NNG_PASS(nng_socket_set_ms(s1, NNG_OPT_RECONNMAXT, 10)); + + TEST_NNG_PASS(nng_dial(s1, a, NULL, NNG_FLAG_NONBLOCK)); + TEST_NNG_PASS(nng_listen(s2, a, NULL, 0)); + nng_msleep(100); + TEST_NNG_PASS(nng_send(s1, "abc", 4, 0)); + TEST_NNG_PASS(nng_recv(s2, &buf, &sz, NNG_FLAG_ALLOC)); + TEST_CHECK(sz == 4); + TEST_CHECK(memcmp(buf, "abc", 4) == 0); + nng_free(buf, sz); + + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +} + +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_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_PASS(nng_pair1_open(&s2)); + + TEST_CHECK(nng_listener_id(l) < 0); + TEST_NNG_PASS(nng_listen(s1, a, &l, 0)); + TEST_CHECK(nng_listener_id(l) > 0); + + // Cannot start another one. + TEST_NNG_FAIL(nng_listen(s1, a, NULL, 0), NNG_EADDRINUSE); + + // We can't restart it -- it's already running + TEST_NNG_FAIL(nng_listener_start(l, 0), NNG_ESTATE); + + // We can connect to it. + TEST_CHECK(nng_dialer_id(d) < 0); + TEST_NNG_PASS(nng_dial(s2, a, &d, 0)); + TEST_CHECK(nng_dialer_id(d) > 0); + + TEST_NNG_PASS(nng_close(s1)); + TEST_NNG_PASS(nng_close(s2)); +} + +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_NNG_PASS(nng_pair1_open(&s1)); + + TEST_CHECK(nng_dialer_id(d) < 0); + TEST_NNG_PASS(nng_dialer_create(&d, s1, a)); + TEST_CHECK(nng_dialer_id(d) > 0); + + // Forge a listener + l2.id = nng_dialer_id(d); + TEST_NNG_FAIL(nng_listener_get_bool(l2, NNG_OPT_RAW, &b), NNG_ENOENT); + TEST_NNG_FAIL(nng_listener_close(l2), NNG_ENOENT); + TEST_NNG_PASS(nng_dialer_close(d)); + + TEST_CHECK(nng_listener_id(l) < 0); + TEST_NNG_PASS(nng_listener_create(&l, s1, a)); + TEST_CHECK(nng_listener_id(l) > 0); + + // Forge a dialer + d2.id = nng_listener_id(l); + TEST_NNG_FAIL(nng_dialer_get_bool(d2, NNG_OPT_RAW, &b), NNG_ENOENT); + TEST_NNG_FAIL(nng_dialer_close(d2), NNG_ENOENT); + TEST_NNG_PASS(nng_listener_close(l)); + + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_bad_url(void) +{ + nng_socket s1; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + TEST_NNG_FAIL(nng_dial(s1, "bogus://1", NULL, 0), NNG_ENOTSUP); + TEST_NNG_FAIL(nng_listen(s1, "bogus://2", NULL, 0), NNG_ENOTSUP); + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_url_option(void) +{ + nng_socket s1; + char url[NNG_MAXADDRLEN]; + nng_listener l; + nng_dialer d; + size_t sz; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + + // Listener + TEST_NNG_PASS(nng_listener_create(&l, s1, "inproc://url1")); + memset(url, 0, sizeof(url)); + sz = sizeof(url); + TEST_NNG_PASS(nng_listener_get(l, NNG_OPT_URL, url, &sz)); + TEST_STREQUAL(url, "inproc://url1"); + TEST_NNG_FAIL( + nng_listener_set(l, NNG_OPT_URL, url, sz), NNG_EREADONLY); + sz = sizeof(url); + + // Dialer + TEST_NNG_PASS(nng_dialer_create(&d, s1, "inproc://url2")); + TEST_NNG_PASS(nng_dialer_get(d, NNG_OPT_URL, url, &sz)); + TEST_STREQUAL(url, "inproc://url2"); + TEST_NNG_FAIL(nng_dialer_set(d, NNG_OPT_URL, url, sz), NNG_EREADONLY); + + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_listener_options(void) +{ + nng_socket s1; + nng_listener l; + size_t sz; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + +#ifndef NNG_ELIDE_DEPRECATED + // Create a listener with the specified options + TEST_NNG_PASS(nng_socket_set_size(s1, NNG_OPT_RECVMAXSZ, 543)); + TEST_NNG_PASS(nng_listener_create(&l, s1, "inproc://listener_opts")); + TEST_NNG_PASS(nng_listener_get_size(l, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 543); + + // Verify endpoint overrides + TEST_NNG_PASS(nng_listener_set_size(l, NNG_OPT_RECVMAXSZ, 678)); + TEST_NNG_PASS(nng_listener_get_size(l, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 678); + TEST_NNG_PASS(nng_socket_get_size(s1, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 543); + + // And socket overrides again + TEST_NNG_PASS(nng_socket_set_size(s1, NNG_OPT_RECVMAXSZ, 911)); + TEST_NNG_PASS(nng_listener_get_size(l, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 911); +#else + TEST_NNG_PASS(nng_listener_create(&l, s1, "inproc://listener_opts")); + TEST_NNG_PASS(nng_listener_set_size(l, NNG_OPT_RECVMAXSZ, 678)); + TEST_NNG_PASS(nng_listener_get_size(l, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 678); +#endif + + // Cannot set invalid options + TEST_NNG_FAIL(nng_listener_set_size(l, "BAD_OPT", 1), NNG_ENOTSUP); + TEST_NNG_FAIL( + nng_listener_set_bool(l, NNG_OPT_RECVMAXSZ, true), NNG_EBADTYPE); + TEST_NNG_FAIL( + nng_listener_set(l, NNG_OPT_RECVMAXSZ, &sz, 1), NNG_EINVAL); + + // Cannot set inappropriate options + TEST_NNG_FAIL( + nng_listener_set_string(l, NNG_OPT_SOCKNAME, "1"), NNG_ENOTSUP); + + TEST_NNG_FAIL( + nng_listener_set_bool(l, NNG_OPT_RAW, true), NNG_ENOTSUP); + TEST_NNG_FAIL( + nng_listener_set_ms(l, NNG_OPT_RECONNMINT, 1), NNG_ENOTSUP); + TEST_NNG_FAIL(nng_listener_set_string(l, NNG_OPT_SOCKNAME, "bogus"), + NNG_ENOTSUP); + + // Read only options + TEST_NNG_FAIL(nng_listener_set_string(l, NNG_OPT_URL, "inproc://junk"), + NNG_EREADONLY); + + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_dialer_options(void) +{ + nng_socket s1; + nng_dialer d; + size_t sz; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + +#ifndef NNG_ELIDE_DEPRECATED + // NOTE: This test will fail if eliding deprecated behavior. + // Create a dialer with the specified options + TEST_NNG_PASS(nng_socket_set_size(s1, NNG_OPT_RECVMAXSZ, 543)); + TEST_NNG_PASS(nng_dialer_create(&d, s1, "inproc://dialer_opts")); + TEST_NNG_PASS(nng_dialer_get_size(d, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 543); + + // Verify endpoint overrides + TEST_NNG_PASS(nng_dialer_set_size(d, NNG_OPT_RECVMAXSZ, 678)); + TEST_NNG_PASS(nng_dialer_get_size(d, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 678); + TEST_NNG_PASS(nng_socket_get_size(s1, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 543); + + // And socket overrides again + TEST_NNG_PASS(nng_socket_set_size(s1, NNG_OPT_RECVMAXSZ, 911)); + TEST_NNG_PASS(nng_dialer_get_size(d, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 911); +#else + TEST_NNG_PASS(nng_dialer_create(&d, s1, "inproc://dialer_opts")); + TEST_NNG_PASS(nng_dialer_set_size(d, NNG_OPT_RECVMAXSZ, 678)); + TEST_NNG_PASS(nng_dialer_get_size(d, NNG_OPT_RECVMAXSZ, &sz)); + TEST_CHECK(sz == 678); +#endif + + // Cannot set invalid options + TEST_NNG_FAIL(nng_dialer_set_size(d, "BAD_OPT", 1), NNG_ENOTSUP); + TEST_NNG_FAIL( + nng_dialer_set_bool(d, NNG_OPT_RECVMAXSZ, true), NNG_EBADTYPE); + TEST_NNG_FAIL( + nng_dialer_set(d, NNG_OPT_RECVMAXSZ, &sz, 1), NNG_EINVAL); + + // Cannot set inappropriate options + TEST_NNG_FAIL( + nng_dialer_set_string(d, NNG_OPT_SOCKNAME, "1"), NNG_ENOTSUP); + TEST_NNG_FAIL(nng_dialer_set_bool(d, NNG_OPT_RAW, true), NNG_ENOTSUP); + TEST_NNG_FAIL(nng_dialer_set_ms(d, NNG_OPT_SENDTIMEO, 1), NNG_ENOTSUP); + TEST_NNG_FAIL( + nng_dialer_set_string(d, NNG_OPT_SOCKNAME, "bogus"), NNG_ENOTSUP); + + // Read only options + TEST_NNG_FAIL(nng_dialer_set_string(d, NNG_OPT_URL, "inproc://junk"), + NNG_EREADONLY); + + TEST_NNG_PASS(nng_close(s1)); +} + +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_NNG_FAIL( + nng_dialer_set_size(d, NNG_OPT_RECVMAXSZ, 10), NNG_ENOENT); + TEST_NNG_FAIL( + nng_listener_set_size(l, NNG_OPT_RECVMAXSZ, 10), NNG_ENOENT); + + TEST_NNG_FAIL(nng_dialer_get_bool(d, NNG_OPT_RAW, &b), NNG_ENOENT); + TEST_NNG_FAIL(nng_listener_get_bool(l, NNG_OPT_RAW, &b), NNG_ENOENT); + + TEST_NNG_FAIL( + nng_dialer_get_size(d, NNG_OPT_RECVMAXSZ, &s), NNG_ENOENT); + TEST_NNG_FAIL( + nng_listener_get_size(l, NNG_OPT_RECVMAXSZ, &s), NNG_ENOENT); + + TEST_NNG_FAIL(nng_dialer_get_int(d, NNG_OPT_RAW, &i), NNG_ENOENT); + TEST_NNG_FAIL(nng_listener_get_int(l, NNG_OPT_RAW, &i), NNG_ENOENT); + + TEST_NNG_FAIL(nng_dialer_get_ms(d, NNG_OPT_RECVTIMEO, &t), NNG_ENOENT); + TEST_NNG_FAIL( + nng_listener_get_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_NNG_PASS(nng_pair1_open(&s1)); + 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_NNG_FAIL( + nng_socket_get(s1, cases[i], &to, &sz), NNG_EINVAL); + + // Type mismatches + TEST_NNG_FAIL( + nng_socket_get_bool(s1, cases[i], &b), NNG_EBADTYPE); + sz = 1; + TEST_NNG_FAIL( + nng_socket_get(s1, cases[i], &b, &sz), NNG_EINVAL); + + // Can set a valid duration + TEST_NNG_PASS(nng_socket_set_ms(s1, cases[i], 1234)); + TEST_NNG_PASS(nng_socket_get_ms(s1, cases[i], &to)); + TEST_CHECK(to == 1234); + + to = 0; + sz = sizeof(to); + TEST_NNG_PASS(nng_socket_get(s1, cases[i], &to, &sz)); + TEST_CHECK(to == 1234); + TEST_CHECK(sz == sizeof(to)); + + // Can't set a negative duration + TEST_NNG_FAIL(nng_socket_set_ms(s1, cases[i], -5), NNG_EINVAL); + + // Can't pass a buf too small for duration + sz = sizeof(to) - 1; + to = 1; + TEST_NNG_FAIL( + nng_socket_set(s1, cases[i], &to, sz), NNG_EINVAL); + } + TEST_NNG_PASS(nng_close(s1)); +} + +void +test_size_options(void) +{ + nng_socket s1; + size_t val; + size_t sz; + char * opt; + + char *cases[] = { + NNG_OPT_RECVMAXSZ, + NULL, + }; + + TEST_NNG_PASS(nng_pair1_open(&s1)); + for (int i = 0; (opt = cases[i]) != NULL; i++) { + TEST_CASE(opt); + + // Can't receive a size into zero bytes. + sz = 0; + TEST_NNG_FAIL(nng_socket_get(s1, opt, &val, &sz), NNG_EINVAL); + + // Can set a valid duration + TEST_NNG_PASS(nng_socket_set_size(s1, opt, 1234)); + TEST_NNG_PASS(nng_socket_get_size(s1, opt, &val)); + TEST_CHECK(val == 1234); + + val = 0; + sz = sizeof(val); + TEST_NNG_PASS(nng_socket_get(s1, opt, &val, &sz)); + 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_NNG_FAIL(nng_socket_set(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_NNG_FAIL(nng_socket_set_size(s1, opt, val), NNG_EINVAL); + TEST_NNG_PASS(nng_socket_get_size(s1, opt, &val)); + TEST_CHECK(val == 1234); +#endif + } + TEST_NNG_PASS(nng_close(s1)); +} + +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 }, + { "send recv zero length", test_send_recv_zero_length }, + { "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/src/core/url_test.c b/src/core/url_test.c new file mode 100644 index 00000000..847b7df3 --- /dev/null +++ b/src/core/url_test.c @@ -0,0 +1,479 @@ +// +// Copyright 2018 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// +// 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 "acutest.h" + +#include + +#include + +#include "core/url.h" + +#include "testutil.h" + +void +test_url_host(void) +{ + nng_url *url; + + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com")); + TEST_ASSERT(url != NULL); + TEST_CHECK(strcmp(url->u_scheme, "http") == 0); + TEST_CHECK(strcmp(url->u_host, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_hostname, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_port, "80") == 0); + TEST_CHECK(strcmp(url->u_path, "") == 0); + TEST_CHECK(strcmp(url->u_requri, "") == 0); + TEST_CHECK(url->u_query == NULL); + TEST_CHECK(url->u_fragment == NULL); + TEST_CHECK(url->u_userinfo == NULL); + nng_url_free(url); +} + +void +test_url_host_port(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com:1234")); + TEST_ASSERT(url != NULL); + TEST_CHECK(strcmp(url->u_scheme, "http") == 0); + TEST_CHECK(strcmp(url->u_host, "www.google.com:1234") == 0); + TEST_CHECK(strcmp(url->u_hostname, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_port, "1234") == 0); + TEST_CHECK(strcmp(url->u_path, "") == 0); + TEST_CHECK(strcmp(url->u_requri, "") == 0); + TEST_CHECK(url->u_query == NULL); + TEST_CHECK(url->u_fragment == NULL); + TEST_CHECK(url->u_userinfo == NULL); + nng_url_free(url); +} + +void +test_url_host_port_path(void) +{ + nng_url *url; + + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.google.com:1234/somewhere")); + TEST_ASSERT(url != NULL); + TEST_CHECK(strcmp(url->u_scheme, "http") == 0); + TEST_CHECK(strcmp(url->u_host, "www.google.com:1234") == 0); + TEST_CHECK(strcmp(url->u_hostname, "www.google.com") == 0); + TEST_CHECK(strcmp(url->u_port, "1234") == 0); + TEST_CHECK(strcmp(url->u_path, "/somewhere") == 0); + TEST_CHECK(strcmp(url->u_requri, "/somewhere") == 0); + TEST_CHECK(url->u_userinfo == NULL); + TEST_CHECK(url->u_query == NULL); + TEST_CHECK(url->u_fragment == NULL); + nng_url_free(url); +} + +void +test_url_user_info(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse( + &url, "http://garrett@www.google.com:1234/somewhere")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_userinfo, "garrett"); + TEST_STREQUAL(url->u_host, "www.google.com:1234"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "1234"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_requri, "/somewhere"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + nng_url_free(url); +} + +void +test_url_path_query_param(void) +{ + nng_url *url; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.google.com/somewhere?result=yes")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_query, "result=yes"); + TEST_STREQUAL(url->u_requri, "/somewhere?result=yes"); + TEST_NULL(url->u_userinfo); + TEST_NULL(url->u_fragment); + nng_url_free(url); +} + +void +test_url_query_param_anchor(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, + "http://www.google.com/" + "somewhere?result=yes#chapter1")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_query, "result=yes"); + TEST_STREQUAL(url->u_fragment, "chapter1"); + TEST_STREQUAL(url->u_requri, "/somewhere?result=yes#chapter1"); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_path_anchor(void) +{ + nng_url *url; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.google.com/somewhere#chapter2")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/somewhere"); + TEST_STREQUAL(url->u_fragment, "chapter2"); + TEST_STREQUAL(url->u_requri, "/somewhere#chapter2"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_anchor(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com#chapter3")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_fragment, "chapter3"); + TEST_STREQUAL(url->u_requri, "#chapter3"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_query_param(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.google.com?color=red")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "www.google.com"); + TEST_STREQUAL(url->u_hostname, "www.google.com"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_query, "color=red"); + TEST_STREQUAL(url->u_requri, "?color=red"); + TEST_ASSERT(url != NULL); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_v6_host(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://[::1]")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "[::1]"); + TEST_STREQUAL(url->u_hostname, "::1"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_v6_host_port(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://[::1]:29")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "[::1]:29"); + TEST_STREQUAL(url->u_hostname, "::1"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "29"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_v6_host_port_path(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "http://[::1]:29/bottles")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_host, "[::1]:29"); + TEST_STREQUAL(url->u_hostname, "::1"); + TEST_STREQUAL(url->u_path, "/bottles"); + TEST_STREQUAL(url->u_port, "29"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_tcp_port(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "tcp://:9876/")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "tcp"); + TEST_STREQUAL(url->u_host, ":9876"); + TEST_STREQUAL(url->u_hostname, ""); + TEST_STREQUAL(url->u_path, "/"); + TEST_STREQUAL(url->u_port, "9876"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_bare_ws(void) +{ + nng_url *url; + + TEST_NNG_PASS(nng_url_parse(&url, "ws://")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "ws"); + TEST_STREQUAL(url->u_host, ""); + TEST_STREQUAL(url->u_hostname, ""); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "80"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_ws_wildcard(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "ws://*:12345/foobar")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "ws"); + TEST_STREQUAL(url->u_host, ":12345"); + TEST_STREQUAL(url->u_hostname, ""); + TEST_STREQUAL(url->u_path, "/foobar"); + TEST_STREQUAL(url->u_port, "12345"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_NULL(url->u_userinfo); + nng_url_free(url); +} + +void +test_url_ssh(void) +{ + nng_url *url; + TEST_NNG_PASS(nng_url_parse(&url, "ssh://user@host.example.com")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "ssh"); + TEST_STREQUAL(url->u_host, "host.example.com"); + TEST_STREQUAL(url->u_hostname, "host.example.com"); + TEST_STREQUAL(url->u_path, ""); + TEST_STREQUAL(url->u_port, "22"); + TEST_NULL(url->u_query); + TEST_NULL(url->u_fragment); + TEST_STREQUAL(url->u_userinfo, "user"); + nng_url_free(url); +} + +void +test_url_bad_scheme(void) +{ + nng_url *url = NULL; + TEST_NNG_FAIL(nng_url_parse(&url, "www.google.com"), NNG_EINVAL); + TEST_NULL(url); + TEST_NNG_FAIL(nng_url_parse(&url, "http:www.google.com"), NNG_EINVAL); + TEST_NULL(url); +} + +void +test_url_bad_ipv6(void) +{ + nng_url *url = NULL; + TEST_NNG_FAIL(nng_url_parse(&url, "http://[::1"), NNG_EINVAL); + TEST_NULL(url); + TEST_NNG_FAIL(nng_url_parse(&url, "http://[::1]bogus"), NNG_EINVAL); + TEST_NULL(url); +} + +void +test_url_canonify(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS(nng_url_parse( + &url, "hTTp://www.EXAMPLE.com/bogus/.%2e/%7egarrett")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.example.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/~garrett"); + nng_url_free(url); +} + +void +test_url_path_resolve(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.x.com//abc/def/./x/..///./../y")); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.x.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/abc/y"); + nng_url_free(url); +} + +void +test_url_query_info_pass(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS( + nng_url_parse(&url, "http://www.x.com/?/abc/def/./x/.././../y")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.x.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/"); + TEST_STREQUAL(url->u_query, "/abc/def/./x/.././../y"); + nng_url_free(url); +} + +void +test_url_bad_utf8(void) +{ + nng_url *url = NULL; + TEST_NNG_FAIL(nng_url_parse(&url, "http://x.com/x%80x"), NNG_EINVAL); + TEST_NULL(url); + TEST_NNG_FAIL(nng_url_parse(&url, "http://x.com/x%c0%81"), NNG_EINVAL); + TEST_NULL(url); +} + +void +test_url_good_utf8(void) +{ + nng_url *url = NULL; + TEST_NNG_PASS(nng_url_parse(&url, "http://www.x.com/%c2%a2_cents")); + TEST_ASSERT(url != NULL); + TEST_STREQUAL(url->u_scheme, "http"); + TEST_STREQUAL(url->u_hostname, "www.x.com"); + TEST_STREQUAL(url->u_port, "80"); + TEST_STREQUAL(url->u_path, "/\xc2\xa2_cents"); + nng_url_free(url); +} + +void +test_url_decode(void) +{ + uint8_t out[16]; + size_t len; + + out[3] = 'x'; + len = nni_url_decode(out, "abc", 3); + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "abc", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "x%00y", 3); // embedded NULL + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "x\x00y", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "%3987", 3); + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "987", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "78%39", 3); + TEST_CHECK(len == 3); + TEST_CHECK(memcmp(out, "789", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "", 5); + TEST_CHECK(len == 0); + TEST_CHECK(memcmp(out, "789", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "be", 2); + TEST_CHECK(len == 2); + TEST_CHECK(memcmp(out, "be9", 3) == 0); + TEST_CHECK(out[3] == 'x'); + + len = nni_url_decode(out, "78%39", 2); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "", 2); + TEST_CHECK(len == 0); + + len = nni_url_decode(out, "78%", 5); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "78%xy", 5); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "78%1$", 5); + TEST_CHECK(len == (size_t) -1); + + len = nni_url_decode(out, "%%20", 5); + TEST_CHECK(len == (size_t) -1); +} + +TEST_LIST = { + { "url host", test_url_host }, + { "url host port", test_url_host_port }, + { "url host port path", test_url_host_port_path }, + { "url user info", test_url_user_info }, + { "url path query param", test_url_path_query_param }, + { "url query param anchor", test_url_query_param_anchor }, + { "url path anchor", test_url_path_anchor }, + { "url anchor", test_url_anchor }, + { "url query param", test_url_query_param }, + { "url v6 host", test_url_v6_host }, + { "url v6 host port", test_url_v6_host_port }, + { "url v6 host port path", test_url_v6_host_port_path }, + { "url tcp port", test_url_tcp_port }, + { "url bare ws", test_url_bare_ws }, + { "url ws wildcard", test_url_ws_wildcard }, + { "url ssh", test_url_ssh }, + { "url bad scheme", test_url_bad_scheme }, + { "url bad v6", test_url_bad_ipv6 }, + { "url canonify", test_url_canonify }, + { "url path resolve", test_url_path_resolve }, + { "url query info pass", test_url_query_info_pass }, + { "url bad utf8", test_url_bad_utf8 }, + { "url good utf8", test_url_good_utf8 }, + { "url decode", test_url_decode }, + { NULL, NULL }, +}; \ No newline at end of file -- cgit v1.2.3-70-g09d2