aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt9
-rw-r--r--src/core/aio_test.c245
-rw-r--r--src/core/buf_size_test.c119
-rw-r--r--src/core/errors_test.c46
-rw-r--r--src/core/id_test.c275
-rw-r--r--src/core/reconnect_test.c167
-rw-r--r--src/core/sock_test.c685
-rw-r--r--src/core/url_test.c479
8 files changed, 2025 insertions, 0 deletions
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. <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/pair1/pair.h>
+#include <nng/supplemental/util/platform.h>
+
+#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. <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 <nng/nng.h>
+#include <nng/protocol/pair1/pair.h>
+#include <nng/supplemental/util/platform.h>
+
+#include <nng/compat/nanomsg/nn.h>
+
+#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. <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.
+//
+
+#include <errno.h>
+#include <string.h>
+
+#include <nng/nng.h>
+
+#include <acutest.h>
+#include <testutil.h>
+
+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. <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.
+//
+
+#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. <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/pipeline0/pull.h>
+#include <nng/protocol/pipeline0/push.h>
+#include <nng/supplemental/util/platform.h>
+
+#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. <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/pair1/pair.h>
+#include <nng/supplemental/util/platform.h>
+
+#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. <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 "acutest.h"
+
+#include <string.h>
+
+#include <nng/nng.h>
+
+#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