summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/Makefile.am17
-rw-r--r--src/test/test-child.c40
-rw-r--r--src/test/test.c787
-rw-r--r--src/test/test.h3
-rw-r--r--src/test/test_addr.c4
-rw-r--r--src/test/test_crypto.c24
-rw-r--r--src/test/test_dir.c18
-rw-r--r--src/test/test_microdesc.c233
-rw-r--r--src/test/test_util.c474
-rw-r--r--src/test/tinytest.c17
-rw-r--r--src/test/tinytest_demo.c14
-rw-r--r--src/test/tinytest_macros.h39
12 files changed, 1587 insertions, 83 deletions
diff --git a/src/test/Makefile.am b/src/test/Makefile.am
index 904719d94b..852715079d 100644
--- a/src/test/Makefile.am
+++ b/src/test/Makefile.am
@@ -1,6 +1,6 @@
TESTS = test
-noinst_PROGRAMS = test
+noinst_PROGRAMS = test test-child
AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
@@ -12,12 +12,13 @@ AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
# matters a lot there, and is quite hard to debug if you forget to do it.
test_SOURCES = \
- test_data.c \
test.c \
test_addr.c \
+ test_containers.c \
test_crypto.c \
+ test_data.c \
test_dir.c \
- test_containers.c \
+ test_microdesc.c \
test_util.c \
tinytest.c
@@ -25,6 +26,12 @@ test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
test_LDADD = ../or/libtor.a ../common/libor.a ../common/libor-crypto.a \
../common/libor-event.a \
- @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+ @TOR_ZLIB_LIBS@ -lm @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@
+
+noinst_HEADERS = \
+ tinytest.h \
+ tinytest_macros.h \
+ test.h
+
-noinst_HEADERS = tinytest.h tinytest_macros.h test.h
diff --git a/src/test/test-child.c b/src/test/test-child.c
new file mode 100644
index 0000000000..1b9c5e3d57
--- /dev/null
+++ b/src/test/test-child.c
@@ -0,0 +1,40 @@
+#include <stdio.h>
+#include "orconfig.h"
+#ifdef MS_WINDOWS
+#define WINDOWS_LEAN_AND_MEAN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+/** Trivial test program which prints out its command line arguments so we can
+ * check if tor_spawn_background() works */
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ fprintf(stdout, "OUT\n");
+ fprintf(stderr, "ERR\n");
+ for (i = 1; i < argc; i++)
+ fprintf(stdout, "%s\n", argv[i]);
+ fprintf(stdout, "SLEEPING\n");
+ /* We need to flush stdout so that test_util_spawn_background_partial_read()
+ succeed. Otherwise ReadFile() will get the entire output in one */
+ // XXX: Can we make stdio flush on newline?
+ fflush(stdout);
+#ifdef MS_WINDOWS
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ fprintf(stdout, "DONE\n");
+#ifdef MS_WINDOWS
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+
+ return 0;
+}
+
diff --git a/src/test/test.c b/src/test/test.c
index b5b744eba7..04e7e02863 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -119,30 +119,46 @@ get_fname(const char *name)
return buf;
}
-/** Remove all files stored under the temporary directory, and the directory
- * itself. Called by atexit(). */
+/* Remove a directory and all of its subdirectories */
static void
-remove_directory(void)
+rm_rf(const char *dir)
{
+ struct stat st;
smartlist_t *elements;
- if (getpid() != temp_dir_setup_in_pid) {
- /* Only clean out the tempdir when the main process is exiting. */
- return;
- }
- elements = tor_listdir(temp_dir);
+
+ elements = tor_listdir(dir);
if (elements) {
SMARTLIST_FOREACH(elements, const char *, cp,
{
- size_t len = strlen(cp)+strlen(temp_dir)+16;
- char *tmp = tor_malloc(len);
- tor_snprintf(tmp, len, "%s"PATH_SEPARATOR"%s", temp_dir, cp);
- unlink(tmp);
+ char *tmp = NULL;
+ tor_asprintf(&tmp, "%s"PATH_SEPARATOR"%s", dir, cp);
+ if (0 == stat(tmp,&st) && (st.st_mode & S_IFDIR)) {
+ rm_rf(tmp);
+ } else {
+ if (unlink(tmp)) {
+ fprintf(stderr, "Error removing %s: %s\n", tmp, strerror(errno));
+ }
+ }
tor_free(tmp);
});
SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
smartlist_free(elements);
}
- rmdir(temp_dir);
+ if (rmdir(dir))
+ fprintf(stderr, "Error removing directory %s: %s\n", dir, strerror(errno));
+}
+
+/** Remove all files stored under the temporary directory, and the directory
+ * itself. Called by atexit(). */
+static void
+remove_directory(void)
+{
+ if (getpid() != temp_dir_setup_in_pid) {
+ /* Only clean out the tempdir when the main process is exiting. */
+ return;
+ }
+
+ rm_rf(temp_dir);
}
/** Define this if unit tests spend too much time generating public keys*/
@@ -187,6 +203,437 @@ free_pregenerated_keys(void)
}
}
+typedef struct socks_test_data_t {
+ socks_request_t *req;
+ buf_t *buf;
+} socks_test_data_t;
+
+static void *
+socks_test_setup(const struct testcase_t *testcase)
+{
+ socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
+ (void)testcase;
+ data->buf = buf_new_with_capacity(256);
+ data->req = socks_request_new();
+ config_register_addressmaps(get_options());
+ return data;
+}
+static int
+socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ socks_test_data_t *data = ptr;
+ (void)testcase;
+ buf_free(data->buf);
+ socks_request_free(data->req);
+ tor_free(data);
+ return 1;
+}
+
+const struct testcase_setup_t socks_setup = {
+ socks_test_setup, socks_test_cleanup
+};
+
+#define SOCKS_TEST_INIT() \
+ socks_test_data_t *testdata = ptr; \
+ buf_t *buf = testdata->buf; \
+ socks_request_t *socks = testdata->req;
+#define ADD_DATA(buf, s) \
+ write_to_buf(s, sizeof(s)-1, buf)
+
+static void
+socks_request_clear(socks_request_t *socks)
+{
+ tor_free(socks->username);
+ tor_free(socks->password);
+ memset(socks, 0, sizeof(socks_request_t));
+}
+
+/** Perform unsupported SOCKS 4 commands */
+static void
+test_socks_4_unsupported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == -1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+
+ done:
+ ;
+}
+
+/** Perform supported SOCKS 4 commands */
+static void
+test_socks_4_supported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
+ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+ test_streq("2.2.2.3", socks->address);
+ test_eq(4370, socks->port);
+ test_assert(socks->got_auth == 0);
+ test_assert(! socks->username);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
+ ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+ test_streq("2.2.2.4", socks->address);
+ test_eq(4370, socks->port);
+ test_assert(socks->got_auth == 1);
+ test_assert(socks->username);
+ test_eq(2, socks->usernamelen);
+ test_memeq("me", socks->username, 2);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
+ ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(4, socks->socks_version);
+ test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+ test_streq("torproject.org", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+
+ done:
+ ;
+}
+
+/** Perform unsupported SOCKS 5 commands */
+static void
+test_socks_5_unsupported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send unsupported BIND [02] command */
+ ADD_DATA(buf, "\x05\x02\x00\x01");
+
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(0, buf_datalen(buf));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), -1);
+ /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+ buf_clear(buf);
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
+ ADD_DATA(buf, "\x05\x03\x00\x01\x02");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), -1);
+ /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+ done:
+ ;
+}
+
+/** Perform supported SOCKS 5 commands */
+static void
+test_socks_5_supported_commands(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 0);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 1);
+ test_streq("2.2.2.2", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
+ test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks), 1);
+
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("torproject.org", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("torproject.org", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+ socks_request_clear(socks);
+
+ /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
+ test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+ test_streq("2.2.2.5", socks->address);
+
+ test_eq(0, buf_datalen(buf));
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication */
+static void
+test_socks_5_no_authenticate(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /*SOCKS 5 No Authentication */
+ ADD_DATA(buf,"\x05\x01\x00");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_NO_AUTH, socks->reply[1]);
+
+ test_eq(0, buf_datalen(buf));
+
+ /*SOCKS 5 Send username/password anyway - pretend to be broken */
+ ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(2, socks->passwordlen);
+
+ test_memeq("\x01\x01", socks->username, 2);
+ test_memeq("\x01\x01", socks->password, 2);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication */
+static void
+test_socks_5_authenticate(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Negotiate username/password authentication */
+ ADD_DATA(buf, "\x05\x01\x02");
+
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_USER_PASS, socks->reply[1]);
+ test_eq(5, socks->socks_version);
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 5 Send username/password */
+ ADD_DATA(buf, "\x01\x02me\x08mypasswd");
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(8, socks->passwordlen);
+
+ test_memeq("me", socks->username, 2);
+ test_memeq("mypasswd", socks->password, 8);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication and send data all in one go */
+static void
+test_socks_5_authenticate_with_data(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Negotiate username/password authentication */
+ ADD_DATA(buf, "\x05\x01\x02");
+
+ test_assert(!fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks));
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(SOCKS_USER_PASS, socks->reply[1]);
+ test_eq(5, socks->socks_version);
+
+ test_eq(0, buf_datalen(buf));
+
+ /* SOCKS 5 Send username/password */
+ /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+ ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+ test_assert(fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks) == 1);
+ test_eq(5, socks->socks_version);
+ test_eq(2, socks->replylen);
+ test_eq(5, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ test_streq("2.2.2.2", socks->address);
+ test_eq(4369, socks->port);
+
+ test_eq(2, socks->usernamelen);
+ test_eq(3, socks->passwordlen);
+ test_memeq("me", socks->username, 2);
+ test_memeq("you", socks->password, 3);
+
+ done:
+ ;
+}
+
+/** Perform SOCKS 5 authentication before method negotiated */
+static void
+test_socks_5_auth_before_negotiation(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* SOCKS 5 Send username/password */
+ ADD_DATA(buf, "\x01\x02me\x02me");
+ test_assert(fetch_from_buf_socks(buf, socks,
+ get_options()->TestSocks,
+ get_options()->SafeSocks) == -1);
+ test_eq(0, socks->socks_version);
+ test_eq(0, socks->replylen);
+ test_eq(0, socks->reply[0]);
+ test_eq(0, socks->reply[1]);
+
+ done:
+ ;
+}
+
+static void
+test_buffer_copy(void *arg)
+{
+ generic_buffer_t *buf=NULL, *buf2=NULL;
+ const char *s;
+ size_t len;
+ char b[256];
+ int i;
+ (void)arg;
+
+ buf = generic_buffer_new();
+ tt_assert(buf);
+
+ /* Copy an empty buffer. */
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_assert(buf2);
+ tt_int_op(0, ==, generic_buffer_len(buf2));
+
+ /* Now try with a short buffer. */
+ s = "And now comes an act of enormous enormance!";
+ len = strlen(s);
+ generic_buffer_add(buf, s, len);
+ tt_int_op(len, ==, generic_buffer_len(buf));
+ /* Add junk to buf2 so we can test replacing.*/
+ generic_buffer_add(buf2, "BLARG", 5);
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(len, ==, generic_buffer_len(buf2));
+ generic_buffer_get(buf2, b, len);
+ test_mem_op(b, ==, s, len);
+ /* Now free buf2 and retry so we can test allocating */
+ generic_buffer_free(buf2);
+ buf2 = NULL;
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(len, ==, generic_buffer_len(buf2));
+ generic_buffer_get(buf2, b, len);
+ test_mem_op(b, ==, s, len);
+ /* Clear buf for next test */
+ generic_buffer_get(buf, b, len);
+ tt_int_op(generic_buffer_len(buf),==,0);
+
+ /* Okay, now let's try a bigger buffer. */
+ s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
+ "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
+ "fugiat quo voluptas nulla pariatur?";
+ len = strlen(s);
+ for (i = 0; i < 256; ++i) {
+ b[0]=i;
+ generic_buffer_add(buf, b, 1);
+ generic_buffer_add(buf, s, len);
+ }
+ tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
+ for (i = 0; i < 256; ++i) {
+ generic_buffer_get(buf2, b, len+1);
+ tt_int_op((unsigned char)b[0],==,i);
+ test_mem_op(b+1, ==, s, len);
+ }
+
+ done:
+ if (buf)
+ generic_buffer_free(buf);
+ if (buf2)
+ generic_buffer_free(buf2);
+}
+
/** Run unit tests for buffers.c */
static void
test_buffers(void)
@@ -566,6 +1013,7 @@ test_policy_summary_helper(const char *policy_str,
smartlist_t *policy = smartlist_create();
char *summary = NULL;
int r;
+ short_policy_t *short_policy = NULL;
line.key = (char*)"foo";
line.value = (char *)policy_str;
@@ -578,10 +1026,14 @@ test_policy_summary_helper(const char *policy_str,
test_assert(summary != NULL);
test_streq(summary, expected_summary);
+ short_policy = parse_short_policy(summary);
+ tt_assert(short_policy);
+
done:
tor_free(summary);
if (policy)
addr_policy_list_free(policy);
+ short_policy_free(short_policy);
}
/** Run unit tests for generating summary lines of exit policies */
@@ -611,12 +1063,15 @@ test_policies(void)
smartlist_add(policy, p);
+ tor_addr_from_ipv4h(&tar, 0x01020304u);
test_assert(ADDR_POLICY_ACCEPTED ==
- compare_addr_to_addr_policy(0x01020304u, 2, policy));
+ compare_tor_addr_to_addr_policy(&tar, 2, policy));
+ tor_addr_make_unspec(&tar);
test_assert(ADDR_POLICY_PROBABLY_ACCEPTED ==
- compare_addr_to_addr_policy(0, 2, policy));
+ compare_tor_addr_to_addr_policy(&tar, 2, policy));
+ tor_addr_from_ipv4h(&tar, 0xc0a80102);
test_assert(ADDR_POLICY_REJECTED ==
- compare_addr_to_addr_policy(0xc0a80102, 2, policy));
+ compare_tor_addr_to_addr_policy(&tar, 2, policy));
test_assert(0 == policies_parse_exit_policy(NULL, &policy2, 1, NULL, 1));
test_assert(policy2);
@@ -1026,8 +1481,73 @@ static void
test_geoip(void)
{
int i, j;
- time_t now = time(NULL);
+ time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
char *s = NULL;
+ const char *bridge_stats_1 =
+ "bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "bridge-ips zz=24,xy=8\n",
+ *dirreq_stats_1 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips ab=8\n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs ab=8\n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *dirreq_stats_2 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips \n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs \n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=0,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *dirreq_stats_3 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips \n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs \n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *dirreq_stats_4 =
+ "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "dirreq-v3-ips \n"
+ "dirreq-v2-ips \n"
+ "dirreq-v3-reqs \n"
+ "dirreq-v2-reqs \n"
+ "dirreq-v3-resp ok=8,not-enough-sigs=0,unavailable=0,not-found=0,"
+ "not-modified=0,busy=0\n"
+ "dirreq-v2-resp ok=0,unavailable=0,not-found=0,not-modified=0,"
+ "busy=0\n"
+ "dirreq-v3-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v2-direct-dl complete=0,timeout=0,running=0\n"
+ "dirreq-v3-tunneled-dl complete=0,timeout=0,running=4\n"
+ "dirreq-v2-tunneled-dl complete=0,timeout=0,running=0\n",
+ *entry_stats_1 =
+ "entry-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "entry-ips ab=8\n",
+ *entry_stats_2 =
+ "entry-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "entry-ips \n";
/* Populate the DB a bit. Add these in order, since we can't do the final
* 'sort' step. These aren't very good IP addresses, but they're perfectly
@@ -1053,8 +1573,8 @@ test_geoip(void)
test_streq("??", NAMEFOR(2000));
#undef NAMEFOR
- get_options()->BridgeRelay = 1;
- get_options()->BridgeRecordUsageByCountry = 1;
+ get_options_mutable()->BridgeRelay = 1;
+ get_options_mutable()->BridgeRecordUsageByCountry = 1;
/* Put 9 observations in AB... */
for (i=32; i < 40; ++i)
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, i, now-7200);
@@ -1077,6 +1597,114 @@ test_geoip(void)
test_assert(s);
test_streq("zz=24,xy=8", s);
+ /* Start testing bridge statistics by making sure that we don't output
+ * bridge stats without initializing them. */
+ s = geoip_format_bridge_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats and generate the bridge-stats history string out of
+ * the connecting clients added above. */
+ geoip_bridge_stats_init(now);
+ s = geoip_format_bridge_stats(now + 86400);
+ test_streq(bridge_stats_1, s);
+ tor_free(s);
+
+ /* Stop collecting bridge stats and make sure we don't write a history
+ * string anymore. */
+ geoip_bridge_stats_term();
+ s = geoip_format_bridge_stats(now + 86400);
+ test_assert(!s);
+
+ /* Stop being a bridge and start being a directory mirror that gathers
+ * directory request statistics. */
+ geoip_bridge_stats_term();
+ get_options_mutable()->BridgeRelay = 0;
+ get_options_mutable()->BridgeRecordUsageByCountry = 0;
+ get_options_mutable()->DirReqStatistics = 1;
+
+ /* Start testing dirreq statistics by making sure that we don't collect
+ * dirreq stats without initializing them. */
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, note one connecting client, and generate the
+ * dirreq-stats history string. */
+ geoip_dirreq_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_1, s);
+ tor_free(s);
+
+ /* Stop collecting stats, add another connecting client, and ensure we
+ * don't generate a history string. */
+ geoip_dirreq_stats_term();
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 101, now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add a connecting client, reset stats, and make sure
+ * that we get an all empty history string. */
+ geoip_dirreq_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, 100, now);
+ geoip_reset_dirreq_stats(now);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_2, s);
+ tor_free(s);
+
+ /* Note a successful network status response and make sure that it
+ * appears in the history string. */
+ geoip_note_ns_response(GEOIP_CLIENT_NETWORKSTATUS, GEOIP_SUCCESS);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_3, s);
+ tor_free(s);
+
+ /* Start a tunneled directory request. */
+ geoip_start_dirreq((uint64_t) 1, 1024, GEOIP_CLIENT_NETWORKSTATUS,
+ DIRREQ_TUNNELED);
+ s = geoip_format_dirreq_stats(now + 86400);
+ test_streq(dirreq_stats_4, s);
+
+ /* Stop collecting directory request statistics and start gathering
+ * entry stats. */
+ geoip_dirreq_stats_term();
+ get_options_mutable()->DirReqStatistics = 0;
+ get_options_mutable()->EntryStatistics = 1;
+
+ /* Start testing entry statistics by making sure that we don't collect
+ * anything without initializing entry stats. */
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, note one connecting client, and generate the
+ * entry-stats history string. */
+ geoip_entry_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_streq(entry_stats_1, s);
+ tor_free(s);
+
+ /* Stop collecting stats, add another connecting client, and ensure we
+ * don't generate a history string. */
+ geoip_entry_stats_term();
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 101, now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add a connecting client, reset stats, and make sure
+ * that we get an all empty history string. */
+ geoip_entry_stats_init(now);
+ geoip_note_client_seen(GEOIP_CLIENT_CONNECT, 100, now);
+ geoip_reset_entry_stats(now);
+ s = geoip_format_entry_stats(now + 86400);
+ test_streq(entry_stats_2, s);
+ tor_free(s);
+
+ /* Stop collecting entry statistics. */
+ geoip_entry_stats_term();
+ get_options_mutable()->EntryStatistics = 0;
+
done:
tor_free(s);
}
@@ -1089,7 +1717,8 @@ test_stats(void)
char *s = NULL;
int i;
- /* We shouldn't collect exit stats without initializing them. */
+ /* Start with testing exit port statistics; we shouldn't collect exit
+ * stats without initializing them. */
rep_hist_note_exit_stream_opened(80);
rep_hist_note_exit_bytes(80, 100, 10000);
s = rep_hist_format_exit_stats(now + 86400);
@@ -1134,7 +1763,7 @@ test_stats(void)
test_assert(!s);
/* Re-start stats, add some bytes, reset stats, and see what history we
- * get when observing no streams or bytes at all. */
+ * get when observing no streams or bytes at all. */
rep_hist_exit_stats_init(now);
rep_hist_note_exit_stream_opened(80);
rep_hist_note_exit_bytes(80, 100, 10000);
@@ -1144,6 +1773,96 @@ test_stats(void)
"exit-kibibytes-written other=0\n"
"exit-kibibytes-read other=0\n"
"exit-streams-opened other=0\n", s);
+ tor_free(s);
+
+ /* Continue with testing connection statistics; we shouldn't collect
+ * conn stats without initializing them. */
+ rep_hist_note_or_conn_bytes(1, 20, 400, now);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, note bytes, and generate history string. */
+ rep_hist_conn_stats_init(now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_streq("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,1,0\n", s);
+ tor_free(s);
+
+ /* Stop collecting stats, add some bytes, and ensure we don't generate
+ * a history string. */
+ rep_hist_conn_stats_term();
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add some bytes, reset stats, and see what history we
+ * get when observing no bytes at all. */
+ rep_hist_conn_stats_init(now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now);
+ rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10);
+ rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
+ rep_hist_reset_conn_stats(now);
+ s = rep_hist_format_conn_stats(now + 86400);
+ test_streq("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n", s);
+ tor_free(s);
+
+ /* Continue with testing buffer statistics; we shouldn't collect buffer
+ * stats without initializing them. */
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_assert(!s);
+
+ /* Initialize stats, add statistics for a single circuit, and generate
+ * the history string. */
+ rep_hist_buffer_stats_init(now);
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "cell-processed-cells 20,0,0,0,0,0,0,0,0,0\n"
+ "cell-queued-cells 2.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,"
+ "0.00,0.00\n"
+ "cell-time-in-queue 2,0,0,0,0,0,0,0,0,0\n"
+ "cell-circuits-per-decile 1\n", s);
+ tor_free(s);
+
+ /* Add nineteen more circuit statistics to the one that's already in the
+ * history to see that the math works correctly. */
+ for (i = 21; i < 30; i++)
+ rep_hist_add_buffer_stats(2.0, 2.0, i);
+ for (i = 20; i < 30; i++)
+ rep_hist_add_buffer_stats(3.5, 3.5, i);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "cell-processed-cells 29,28,27,26,25,24,23,22,21,20\n"
+ "cell-queued-cells 2.75,2.75,2.75,2.75,2.75,2.75,2.75,2.75,"
+ "2.75,2.75\n"
+ "cell-time-in-queue 3,3,3,3,3,3,3,3,3,3\n"
+ "cell-circuits-per-decile 2\n", s);
+ tor_free(s);
+
+ /* Stop collecting stats, add statistics for one circuit, and ensure we
+ * don't generate a history string. */
+ rep_hist_buffer_stats_term();
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_assert(!s);
+
+ /* Re-start stats, add statistics for one circuit, reset stats, and make
+ * sure that the history has all zeros. */
+ rep_hist_buffer_stats_init(now);
+ rep_hist_add_buffer_stats(2.0, 2.0, 20);
+ rep_hist_reset_buffer_stats(now);
+ s = rep_hist_format_buffer_stats(now + 86400);
+ test_streq("cell-stats-end 2010-08-12 13:27:30 (86400 s)\n"
+ "cell-processed-cells 0,0,0,0,0,0,0,0,0,0\n"
+ "cell-queued-cells 0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,"
+ "0.00,0.00\n"
+ "cell-time-in-queue 0,0,0,0,0,0,0,0,0,0\n"
+ "cell-circuits-per-decile 0\n", s);
done:
tor_free(s);
@@ -1186,6 +1905,7 @@ const struct testcase_setup_t legacy_setup = {
static struct testcase_t test_array[] = {
ENT(buffers),
+ { "buffer_copy", test_buffer_copy, 0, NULL, NULL },
ENT(onion_handshake),
ENT(circuit_timeout),
ENT(policies),
@@ -1198,19 +1918,39 @@ static struct testcase_t test_array[] = {
END_OF_TESTCASES
};
+#define SOCKSENT(name) \
+ { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
+
+static struct testcase_t socks_tests[] = {
+ SOCKSENT(4_unsupported_commands),
+ SOCKSENT(4_supported_commands),
+
+ SOCKSENT(5_unsupported_commands),
+ SOCKSENT(5_supported_commands),
+ SOCKSENT(5_no_authenticate),
+ SOCKSENT(5_auth_before_negotiation),
+ SOCKSENT(5_authenticate),
+ SOCKSENT(5_authenticate_with_data),
+
+ END_OF_TESTCASES
+};
+
extern struct testcase_t addr_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t container_tests[];
extern struct testcase_t util_tests[];
extern struct testcase_t dir_tests[];
+extern struct testcase_t microdesc_tests[];
static struct testgroup_t testgroups[] = {
{ "", test_array },
+ { "socks/", socks_tests },
{ "addr/", addr_tests },
{ "crypto/", crypto_tests },
{ "container/", container_tests },
{ "util/", util_tests },
{ "dir/", dir_tests },
+ { "dir/md/", microdesc_tests },
END_OF_GROUPS
};
@@ -1259,7 +1999,10 @@ main(int c, const char **v)
}
options->command = CMD_RUN_UNITTESTS;
- crypto_global_init(0, NULL, NULL);
+ if (crypto_global_init(0, NULL, NULL)) {
+ printf("Can't initialize crypto subsystem; exiting.\n");
+ return 1;
+ }
rep_hist_init();
network_init();
setup_directory();
diff --git a/src/test/test.h b/src/test/test.h
index f7ae46ce6d..a053a7ac43 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -45,7 +45,8 @@
_print = tor_malloc(printlen); \
base16_encode(_print, printlen, _value, \
(len)); }, \
- { tor_free(_print); } \
+ { tor_free(_print); }, \
+ TT_EXIT_TEST_FUNCTION \
);
#define test_memeq(expr1, expr2, len) test_mem_op((expr1), ==, (expr2), len)
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index 1dab0e0112..ec0c508978 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -74,7 +74,9 @@ test_addr_basic(void)
cp += 2; \
if (i != 15) *cp++ = ':'; \
} \
- }, { tor_free(_print); } \
+ }, \
+ { tor_free(_print); }, \
+ TT_EXIT_TEST_FUNCTION \
); \
STMT_END
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 121af279c7..dca0d3a28f 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -630,7 +630,7 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 4095);
- tor_assert(encrypted_size > 0); /* This is obviously true, since 4111 is
+ tt_assert(encrypted_size > 0); /* This is obviously true, since 4111 is
* greater than 0, but its truth is not
* obvious to all analysis tools. */
cipher = crypto_create_init_cipher(key1, 0);
@@ -639,7 +639,7 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 4095);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain, decrypted1, 4095);
/* Encrypt a second time (with a new random initialization vector). */
cipher = crypto_create_init_cipher(key1, 1);
@@ -648,14 +648,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 4095);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted2, 4095,
encrypted2, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 4095);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain, decrypted2, 4095);
test_memneq(encrypted1, encrypted2, encrypted_size);
/* Decrypt with the wrong key. */
@@ -680,14 +680,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 1);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 1,
encrypted1, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 1);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_1, decrypted1, 1);
/* Special length case: 15. */
cipher = crypto_create_init_cipher(key1, 1);
@@ -696,14 +696,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 15);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 15,
encrypted1, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 15);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_15, decrypted1, 15);
/* Special length case: 16. */
cipher = crypto_create_init_cipher(key1, 1);
@@ -712,14 +712,14 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 16);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 16,
encrypted1, encrypted_size);
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(decrypted_size, 16);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_16, decrypted1, 16);
/* Special length case: 17. */
cipher = crypto_create_init_cipher(key1, 1);
@@ -728,12 +728,12 @@ test_crypto_aes_iv(void)
crypto_free_cipher_env(cipher);
cipher = NULL;
test_eq(encrypted_size, 16 + 17);
- tor_assert(encrypted_size > 0);
+ tt_assert(encrypted_size > 0);
cipher = crypto_create_init_cipher(key1, 0);
decrypted_size = crypto_cipher_decrypt_with_iv(cipher, decrypted1, 17,
encrypted1, encrypted_size);
test_eq(decrypted_size, 17);
- tor_assert(decrypted_size > 0);
+ tt_assert(decrypted_size > 0);
test_memeq(plain_17, decrypted1, 17);
done:
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 8fd94289a9..04ca29a9fb 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -298,7 +298,7 @@ test_dir_versions(void)
#define tt_versionstatus_op(vs1, op, vs2) \
tt_assert_test_type(vs1,vs2,#vs1" "#op" "#vs2,version_status_t, \
- (_val1 op _val2),"%d")
+ (_val1 op _val2),"%d",TT_EXIT_TEST_FUNCTION)
#define test_v_i_o(val, ver, lst) \
tt_versionstatus_op(val, ==, tor_version_is_obsolete(ver, lst))
@@ -803,7 +803,7 @@ test_dir_v3_networkstatus(void)
rs->or_port = 443;
rs->dir_port = 8000;
/* all flags but running cleared */
- rs->is_running = 1;
+ rs->is_flagged_running = 1;
smartlist_add(vote->routerstatus_list, vrs);
test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@ -818,7 +818,7 @@ test_dir_v3_networkstatus(void)
rs->addr = 0x99009901;
rs->or_port = 443;
rs->dir_port = 0;
- rs->is_exit = rs->is_stable = rs->is_fast = rs->is_running =
+ rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running =
rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
smartlist_add(vote->routerstatus_list, vrs);
test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@ -835,7 +835,8 @@ test_dir_v3_networkstatus(void)
rs->or_port = 400;
rs->dir_port = 9999;
rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
- rs->is_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1;
+ rs->is_flagged_running = rs->is_valid = rs->is_v2_dir =
+ rs->is_possible_guard = 1;
smartlist_add(vote->routerstatus_list, vrs);
test_assert(router_add_to_routerlist(generate_ri_from_rs(vrs), &msg,0,0)>=0);
@@ -1075,7 +1076,8 @@ test_dir_v3_networkstatus(void)
test_assert(!rs->is_fast);
test_assert(!rs->is_possible_guard);
test_assert(!rs->is_stable);
- test_assert(rs->is_running); /* If it wasn't running it wouldn't be here */
+ /* (If it wasn't running it wouldn't be here) */
+ test_assert(rs->is_flagged_running);
test_assert(!rs->is_v2_dir);
test_assert(!rs->is_valid);
test_assert(!rs->is_named);
@@ -1097,7 +1099,7 @@ test_dir_v3_networkstatus(void)
test_assert(rs->is_fast);
test_assert(rs->is_possible_guard);
test_assert(rs->is_stable);
- test_assert(rs->is_running);
+ test_assert(rs->is_flagged_running);
test_assert(rs->is_v2_dir);
test_assert(rs->is_valid);
test_assert(!rs->is_named);
@@ -1167,10 +1169,10 @@ test_dir_v3_networkstatus(void)
/* Extract a detached signature from con3. */
detached_text1 = get_detached_sigs(con3, con_md3);
- tor_assert(detached_text1);
+ tt_assert(detached_text1);
/* Try to parse it. */
dsig1 = networkstatus_parse_detached_signatures(detached_text1, NULL);
- tor_assert(dsig1);
+ tt_assert(dsig1);
/* Are parsed values as expected? */
test_eq(dsig1->valid_after, con3->valid_after);
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
new file mode 100644
index 0000000000..b807265c84
--- /dev/null
+++ b/src/test/test_microdesc.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2010-2011, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "or.h"
+
+#include "config.h"
+#include "microdesc.h"
+
+#include "test.h"
+
+#ifdef MS_WINDOWS
+/* For mkdir() */
+#include <direct.h>
+#else
+#include <dirent.h>
+#endif
+
+static const char test_md1[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMjlHH/daN43cSVRaHBwgUfnszzAhg98EvivJ9Qxfv51mvQUxPjQ07es\n"
+ "gV/3n8fyh3Kqr/ehi9jxkdgSRfSnmF7giaHL1SLZ29kA7KtST+pBvmTpDtHa3ykX\n"
+ "Xorc7hJvIyTZoc1HU+5XSynj3gsBE5IGK1ZRzrNS688LnuZMVp1tAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+static const char test_md2[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMIixIowh2DyPmDNMDwBX2DHcYcqdcH1zdIQJZkyV6c6rQHnvbcaDoSg\n"
+ "jgFSLJKpnGmh71FVRqep+yVB0zI1JY43kuEnXry2HbZCD9UDo3d3n7t015X5S7ON\n"
+ "bSSYtQGPwOr6Epf96IF6DoQxy4iDnPUAlejuhAG51s1y6/rZQ3zxAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n";
+
+static const char test_md3[] =
+ "@last-listed 2009-06-22\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMH3340d4ENNGrqx7UxT+lB7x6DNUKOdPEOn4teceE11xlMyZ9TPv41c\n"
+ "qj2fRZzfxlc88G/tmiaHshmdtEpklZ740OFqaaJVj4LjPMKFNE+J7Xc1142BE9Ci\n"
+ "KgsbjGYe2RY261aADRWLetJ8T9QDMm+JngL4288hc8pq1uB/3TAbAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "p accept 1-700,800-1000\n"
+ "family nodeX nodeY nodeZ\n";
+
+static void
+test_md_cache(void *data)
+{
+ or_options_t *options = NULL;
+ microdesc_cache_t *mc = NULL ;
+ smartlist_t *added = NULL, *wanted = NULL;
+ microdesc_t *md1, *md2, *md3;
+ char d1[DIGEST256_LEN], d2[DIGEST256_LEN], d3[DIGEST256_LEN];
+ const char *test_md3_noannotation = strchr(test_md3, '\n')+1;
+ time_t time1, time2, time3;
+ char *fn = NULL, *s = NULL;
+ (void)data;
+
+ options = get_options_mutable();
+ tt_assert(options);
+
+ time1 = time(NULL);
+ time2 = time(NULL) - 2*24*60*60;
+ time3 = time(NULL) - 15*24*60*60;
+
+ /* Possibly, turn this into a test setup/cleanup pair */
+ tor_free(options->DataDirectory);
+ options->DataDirectory = tor_strdup(get_fname("md_datadir_test"));
+#ifdef MS_WINDOWS
+ tt_int_op(0, ==, mkdir(options->DataDirectory));
+#else
+ tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
+#endif
+
+ tt_assert(!strcmpstart(test_md3_noannotation, "onion-key"));
+
+ crypto_digest256(d1, test_md1, strlen(test_md1), DIGEST_SHA256);
+ crypto_digest256(d2, test_md2, strlen(test_md1), DIGEST_SHA256);
+ crypto_digest256(d3, test_md3_noannotation, strlen(test_md3_noannotation),
+ DIGEST_SHA256);
+
+ mc = get_microdesc_cache();
+
+ added = microdescs_add_to_cache(mc, test_md1, NULL, SAVED_NOWHERE, 0,
+ time1, NULL);
+ tt_int_op(1, ==, smartlist_len(added));
+ md1 = smartlist_get(added, 0);
+ smartlist_free(added);
+ added = NULL;
+
+ wanted = smartlist_create();
+ added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
+ time2, wanted);
+ /* Should fail, since we didn't list test_md2's digest in wanted */
+ tt_int_op(0, ==, smartlist_len(added));
+ smartlist_free(added);
+ added = NULL;
+
+ smartlist_add(wanted, tor_memdup(d2, DIGEST256_LEN));
+ smartlist_add(wanted, tor_memdup(d3, DIGEST256_LEN));
+ added = microdescs_add_to_cache(mc, test_md2, NULL, SAVED_NOWHERE, 0,
+ time2, wanted);
+ /* Now it can work. md2 should have been added */
+ tt_int_op(1, ==, smartlist_len(added));
+ md2 = smartlist_get(added, 0);
+ /* And it should have gotten removed from 'wanted' */
+ tt_int_op(smartlist_len(wanted), ==, 1);
+ test_mem_op(smartlist_get(wanted, 0), ==, d3, DIGEST256_LEN);
+ smartlist_free(added);
+ added = NULL;
+
+ added = microdescs_add_to_cache(mc, test_md3, NULL,
+ SAVED_NOWHERE, 0, -1, NULL);
+ /* Must fail, since SAVED_NOWHERE precludes annotations */
+ tt_int_op(0, ==, smartlist_len(added));
+ smartlist_free(added);
+ added = NULL;
+
+ added = microdescs_add_to_cache(mc, test_md3_noannotation, NULL,
+ SAVED_NOWHERE, 0, time3, NULL);
+ /* Now it can work */
+ tt_int_op(1, ==, smartlist_len(added));
+ md3 = smartlist_get(added, 0);
+ smartlist_free(added);
+ added = NULL;
+
+ /* Okay. We added 1...3. Let's poke them to see how they look, and make
+ * sure they're really in the journal. */
+ tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
+ tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
+ tt_ptr_op(md3, ==, microdesc_cache_lookup_by_digest256(mc, d3));
+
+ tt_int_op(md1->last_listed, ==, time1);
+ tt_int_op(md2->last_listed, ==, time2);
+ tt_int_op(md3->last_listed, ==, time3);
+
+ tt_int_op(md1->saved_location, ==, SAVED_IN_JOURNAL);
+ tt_int_op(md2->saved_location, ==, SAVED_IN_JOURNAL);
+ tt_int_op(md3->saved_location, ==, SAVED_IN_JOURNAL);
+
+ tt_int_op(md1->bodylen, ==, strlen(test_md1));
+ tt_int_op(md2->bodylen, ==, strlen(test_md2));
+ tt_int_op(md3->bodylen, ==, strlen(test_md3_noannotation));
+ test_mem_op(md1->body, ==, test_md1, strlen(test_md1));
+ test_mem_op(md2->body, ==, test_md2, strlen(test_md2));
+ test_mem_op(md3->body, ==, test_md3_noannotation,
+ strlen(test_md3_noannotation));
+
+ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs.new",
+ options->DataDirectory);
+ s = read_file_to_str(fn, RFTS_BIN, NULL);
+ tt_assert(s);
+ test_mem_op(md1->body, ==, s + md1->off, md1->bodylen);
+ test_mem_op(md2->body, ==, s + md2->off, md2->bodylen);
+ test_mem_op(md3->body, ==, s + md3->off, md3->bodylen);
+
+ tt_ptr_op(md1->family, ==, NULL);
+ tt_ptr_op(md3->family, !=, NULL);
+ tt_int_op(smartlist_len(md3->family), ==, 3);
+ tt_str_op(smartlist_get(md3->family, 0), ==, "nodeX");
+
+ /* Now rebuild the cache! */
+ tt_int_op(microdesc_cache_rebuild(mc, 1), ==, 0);
+
+ tt_int_op(md1->saved_location, ==, SAVED_IN_CACHE);
+ tt_int_op(md2->saved_location, ==, SAVED_IN_CACHE);
+ tt_int_op(md3->saved_location, ==, SAVED_IN_CACHE);
+
+ /* The journal should be empty now */
+ tor_free(s);
+ s = read_file_to_str(fn, RFTS_BIN, NULL);
+ tt_str_op(s, ==, "");
+ tor_free(s);
+ tor_free(fn);
+
+ /* read the cache. */
+ tor_asprintf(&fn, "%s"PATH_SEPARATOR"cached-microdescs",
+ options->DataDirectory);
+ s = read_file_to_str(fn, RFTS_BIN, NULL);
+ test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
+ test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
+ test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));
+
+ /* Okay, now we are going to forget about the cache entirely, and reload it
+ * from the disk. */
+ microdesc_free_all();
+ mc = get_microdesc_cache();
+ md1 = microdesc_cache_lookup_by_digest256(mc, d1);
+ md2 = microdesc_cache_lookup_by_digest256(mc, d2);
+ md3 = microdesc_cache_lookup_by_digest256(mc, d3);
+ test_assert(md1);
+ test_assert(md2);
+ test_assert(md3);
+ test_mem_op(md1->body, ==, s + md1->off, strlen(test_md1));
+ test_mem_op(md2->body, ==, s + md2->off, strlen(test_md2));
+ test_mem_op(md3->body, ==, s + md3->off, strlen(test_md3_noannotation));
+
+ tt_int_op(md1->last_listed, ==, time1);
+ tt_int_op(md2->last_listed, ==, time2);
+ tt_int_op(md3->last_listed, ==, time3);
+
+ /* Okay, now we are going to clear out everything older than a week old.
+ * In practice, that means md3 */
+ microdesc_cache_clean(mc, time(NULL)-7*24*60*60, 1/*force*/);
+ tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
+ tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
+ tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));
+ md3 = NULL; /* it's history now! */
+
+ /* rebuild again, make sure it stays gone. */
+ microdesc_cache_rebuild(mc, 1);
+ tt_ptr_op(md1, ==, microdesc_cache_lookup_by_digest256(mc, d1));
+ tt_ptr_op(md2, ==, microdesc_cache_lookup_by_digest256(mc, d2));
+ tt_ptr_op(NULL, ==, microdesc_cache_lookup_by_digest256(mc, d3));
+
+ done:
+ if (options)
+ tor_free(options->DataDirectory);
+ microdesc_free_all();
+
+ smartlist_free(added);
+ if (wanted)
+ SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
+ smartlist_free(wanted);
+ tor_free(s);
+ tor_free(fn);
+}
+
+struct testcase_t microdesc_tests[] = {
+ { "cache", test_md_cache, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 23cd059cf7..f9672c100b 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -6,6 +6,7 @@
#include "orconfig.h"
#define CONTROL_PRIVATE
#define MEMPOOL_PRIVATE
+#define UTIL_PRIVATE
#include "or.h"
#include "config.h"
#include "control.h"
@@ -375,7 +376,7 @@ test_util_strmisc(void)
/* Test memmem and memstr */
{
const char *haystack = "abcde";
- tor_assert(!tor_memmem(haystack, 5, "ef", 2));
+ tt_assert(!tor_memmem(haystack, 5, "ef", 2));
test_eq_ptr(tor_memmem(haystack, 5, "cd", 2), haystack + 2);
test_eq_ptr(tor_memmem(haystack, 5, "cde", 3), haystack + 2);
haystack = "ababcad";
@@ -639,26 +640,26 @@ test_util_gzip(void)
tor_strdup("String with low redundancy that won't be compressed much.");
test_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
ZLIB_METHOD));
- tor_assert(len1>16);
+ tt_assert(len1>16);
/* when we allow an incomplete string, we should succeed.*/
- tor_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
+ tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
ZLIB_METHOD, 0, LOG_INFO));
buf3[len2]='\0';
- tor_assert(len2 > 5);
- tor_assert(!strcmpstart(buf1, buf3));
+ tt_assert(len2 > 5);
+ tt_assert(!strcmpstart(buf1, buf3));
/* when we demand a complete string, this must fail. */
tor_free(buf3);
- tor_assert(tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
+ tt_assert(tor_gzip_uncompress(&buf3, &len2, buf2, len1-16,
ZLIB_METHOD, 1, LOG_INFO));
- tor_assert(!buf3);
+ tt_assert(!buf3);
/* Now, try streaming compression. */
tor_free(buf1);
tor_free(buf2);
tor_free(buf3);
state = tor_zlib_new(1, ZLIB_METHOD);
- tor_assert(state);
+ tt_assert(state);
cp1 = buf1 = tor_malloc(1024);
len1 = 1024;
ccp2 = "ABCDEFGHIJABCDEFGHIJ";
@@ -675,7 +676,7 @@ test_util_gzip(void)
test_eq(len2, 0);
test_assert(cp1 > cp2); /* Make sure we really added something. */
- tor_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1,
+ tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf1, 1024-len1,
ZLIB_METHOD, 1, LOG_WARN));
test_streq(buf3, "ABCDEFGHIJABCDEFGHIJ"); /*Make sure it compressed right.*/
@@ -832,6 +833,18 @@ test_util_sscanf(void)
test_eq(u2, 3u);
test_eq(u3, 99u);
+ /* %x should work. */
+ r = tor_sscanf("1234 02aBcdEf", "%x %x", &u1, &u2);
+ test_eq(r, 2);
+ test_eq(u1, 0x1234);
+ test_eq(u2, 0x2ABCDEF);
+ /* Width works on %x */
+ r = tor_sscanf("f00dcafe444", "%4x%4x%u", &u1, &u2, &u3);
+ test_eq(r, 3);
+ test_eq(u1, 0xf00d);
+ test_eq(u2, 0xcafe);
+ test_eq(u3, 444);
+
r = tor_sscanf("99% fresh", "%3u%% fresh", &u1); /* percents are scannable.*/
test_eq(r, 1);
test_eq(u1, 99);
@@ -1239,6 +1252,440 @@ test_util_load_win_lib(void *ptr)
#endif
static void
+clear_hex_errno(char *hex_errno)
+{
+ memset(hex_errno, '\0', HEX_ERRNO_SIZE + 1);
+}
+
+static void
+test_util_exit_status(void *ptr)
+{
+ /* Leave an extra byte for a \0 so we can do string comparison */
+ char hex_errno[HEX_ERRNO_SIZE + 1];
+
+ (void)ptr;
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0, 0, hex_errno);
+ tt_str_op(hex_errno, ==, " 0/0\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0, 0x7FFFFFFF, hex_errno);
+ tt_str_op(hex_errno, ==, " 0/7FFFFFFF\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0xFF, -0x80000000, hex_errno);
+ tt_str_op(hex_errno, ==, "FF/-80000000\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0x7F, 0, hex_errno);
+ tt_str_op(hex_errno, ==, " 7F/0\n");
+
+ clear_hex_errno(hex_errno);
+ format_helper_exit_status(0x08, -0x242, hex_errno);
+ tt_str_op(hex_errno, ==, " 8/-242\n");
+
+ done:
+ ;
+}
+
+#ifndef MS_WINDOWS
+/** Check that fgets waits until a full line, and not return a partial line, on
+ * a EAGAIN with a non-blocking pipe */
+static void
+test_util_fgets_eagain(void *ptr)
+{
+ int test_pipe[2] = {-1, -1};
+ int retval;
+ ssize_t retlen;
+ char *retptr;
+ FILE *test_stream = NULL;
+ char buf[10];
+
+ (void)ptr;
+
+ /* Set up a pipe to test on */
+ retval = pipe(test_pipe);
+ tt_int_op(retval, >=, 0);
+
+ /* Set up the read-end to be non-blocking */
+ retval = fcntl(test_pipe[0], F_SETFL, O_NONBLOCK);
+ tt_int_op(retval, >=, 0);
+
+ /* Open it as a stdio stream */
+ test_stream = fdopen(test_pipe[0], "r");
+ tt_ptr_op(test_stream, !=, NULL);
+
+ /* Send in a partial line */
+ retlen = write(test_pipe[1], "A", 1);
+ tt_int_op(retlen, ==, 1);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_want(retptr == NULL);
+ tt_int_op(errno, ==, EAGAIN);
+
+ /* Send in the rest */
+ retlen = write(test_pipe[1], "B\n", 2);
+ tt_int_op(retlen, ==, 2);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "AB\n");
+
+ /* Send in a full line */
+ retlen = write(test_pipe[1], "CD\n", 3);
+ tt_int_op(retlen, ==, 3);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "CD\n");
+
+ /* Send in a partial line */
+ retlen = write(test_pipe[1], "E", 1);
+ tt_int_op(retlen, ==, 1);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, NULL);
+ tt_int_op(errno, ==, EAGAIN);
+
+ /* Send in the rest */
+ retlen = write(test_pipe[1], "F\n", 2);
+ tt_int_op(retlen, ==, 2);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "EF\n");
+
+ /* Send in a full line and close */
+ retlen = write(test_pipe[1], "GH", 2);
+ tt_int_op(retlen, ==, 2);
+ retval = close(test_pipe[1]);
+ test_pipe[1] = -1;
+ tt_int_op(retval, ==, 0);
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, buf);
+ tt_str_op(buf, ==, "GH");
+
+ /* Check for EOF */
+ retptr = fgets(buf, sizeof(buf), test_stream);
+ tt_ptr_op(retptr, ==, NULL);
+ tt_int_op(feof(test_stream), >, 0);
+
+ done:
+ if (test_stream != NULL)
+ fclose(test_stream);
+ if (test_pipe[0] != -1)
+ close(test_pipe[0]);
+ if (test_pipe[1] != -1)
+ close(test_pipe[1]);
+}
+#endif
+
+/** Helper function for testing tor_spawn_background */
+static void
+run_util_spawn_background(const char *argv[], const char *expected_out,
+ const char *expected_err, int expected_exit,
+ int expected_status)
+{
+ int retval, exit_code;
+ ssize_t pos;
+ process_handle_t process_handle;
+ char stdout_buf[100], stderr_buf[100];
+
+ /* Start the program */
+#ifdef MS_WINDOWS
+ tor_spawn_background(NULL, argv, &process_handle);
+#else
+ tor_spawn_background(argv[0], argv, &process_handle);
+#endif
+
+ tt_int_op(process_handle.status, ==, expected_status);
+
+ /* If the process failed to start, don't bother continuing */
+ if (process_handle.status == PROCESS_STATUS_ERROR)
+ return;
+
+ tt_int_op(process_handle.stdout_pipe, >, 0);
+ tt_int_op(process_handle.stderr_pipe, >, 0);
+
+ /* Check stdout */
+ pos = tor_read_all_from_process_stdout(&process_handle, stdout_buf,
+ sizeof(stdout_buf) - 1);
+ tt_assert(pos >= 0);
+ stdout_buf[pos] = '\0';
+ tt_str_op(stdout_buf, ==, expected_out);
+ tt_int_op(pos, ==, strlen(expected_out));
+
+ /* Check it terminated correctly */
+ retval = tor_get_exit_code(process_handle, 1, &exit_code);
+ tt_int_op(retval, ==, PROCESS_EXIT_EXITED);
+ tt_int_op(exit_code, ==, expected_exit);
+ // TODO: Make test-child exit with something other than 0
+
+ /* Check stderr */
+ pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf,
+ sizeof(stderr_buf) - 1);
+ tt_assert(pos >= 0);
+ stderr_buf[pos] = '\0';
+ tt_str_op(stderr_buf, ==, expected_err);
+ tt_int_op(pos, ==, strlen(expected_err));
+
+ done:
+ ;
+}
+
+/** Check that we can launch a process and read the output */
+static void
+test_util_spawn_background_ok(void *ptr)
+{
+#ifdef MS_WINDOWS
+ const char *argv[] = {"test-child.exe", "--test", NULL};
+ const char *expected_out = "OUT\r\n--test\r\nSLEEPING\r\nDONE\r\n";
+ const char *expected_err = "ERR\r\n";
+#else
+ const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
+ const char *expected_out = "OUT\n--test\nSLEEPING\nDONE\n";
+ const char *expected_err = "ERR\n";
+#endif
+
+ (void)ptr;
+
+ run_util_spawn_background(argv, expected_out, expected_err, 0,
+ PROCESS_STATUS_RUNNING);
+}
+
+/** Check that failing to find the executable works as expected */
+static void
+test_util_spawn_background_fail(void *ptr)
+{
+#ifdef MS_WINDOWS
+ const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
+ const char *expected_out = "ERR: Failed to spawn background process "
+ "- code 9/2\n";
+ const char *expected_err = "";
+ const int expected_status = PROCESS_STATUS_ERROR;
+#else
+ const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
+ const char *expected_out = "ERR: Failed to spawn background process "
+ "- code 9/2\n";
+ const char *expected_err = "";
+ /* TODO: Once we can signal failure to exec, set this to be
+ * PROCESS_STATUS_ERROR */
+ const int expected_status = PROCESS_STATUS_RUNNING;
+#endif
+
+ (void)ptr;
+
+ run_util_spawn_background(argv, expected_out, expected_err, 255,
+ expected_status);
+}
+
+/** Test that reading from a handle returns a partial read rather than
+ * blocking */
+static void
+test_util_spawn_background_partial_read(void *ptr)
+{
+ const int expected_exit = 0;
+ const int expected_status = PROCESS_STATUS_RUNNING;
+
+ int retval, exit_code;
+ ssize_t pos = -1;
+ process_handle_t process_handle;
+ char stdout_buf[100], stderr_buf[100];
+#ifdef MS_WINDOWS
+ const char *argv[] = {"test-child.exe", "--test", NULL};
+ const char *expected_out[] = { "OUT\r\n--test\r\nSLEEPING\r\n",
+ "DONE\r\n",
+ NULL };
+ const char *expected_err = "ERR\r\n";
+#else
+ const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
+ const char *expected_out[] = { "OUT\n--test\nSLEEPING\n",
+ "DONE\n",
+ NULL };
+ const char *expected_err = "ERR\n";
+ int eof = 0;
+#endif
+ int expected_out_ctr;
+ (void)ptr;
+
+ /* Start the program */
+#ifdef MS_WINDOWS
+ tor_spawn_background(NULL, argv, &process_handle);
+#else
+ tor_spawn_background(argv[0], argv, &process_handle);
+#endif
+ tt_int_op(process_handle.status, ==, expected_status);
+
+ /* Check stdout */
+ for (expected_out_ctr =0; expected_out[expected_out_ctr] != NULL;) {
+#ifdef MS_WINDOWS
+ pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf,
+ sizeof(stdout_buf) - 1, NULL);
+#else
+ /* Check that we didn't read the end of file last time */
+ tt_assert(!eof);
+ pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf,
+ sizeof(stdout_buf) - 1, NULL, &eof);
+#endif
+ log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
+
+ /* We would have blocked, keep on trying */
+ if (0 == pos)
+ continue;
+
+ tt_int_op(pos, >, 0);
+ stdout_buf[pos] = '\0';
+ tt_str_op(stdout_buf, ==, expected_out[expected_out_ctr]);
+ tt_int_op(pos, ==, strlen(expected_out[expected_out_ctr]));
+ expected_out_ctr++;
+ }
+
+ /* The process should have exited without writing more */
+#ifdef MS_WINDOWS
+ pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf,
+ sizeof(stdout_buf) - 1,
+ &process_handle);
+ tt_int_op(pos, ==, 0);
+#else
+ if (!eof) {
+ /* We should have got all the data, but maybe not the EOF flag */
+ pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf,
+ sizeof(stdout_buf) - 1,
+ &process_handle, &eof);
+ tt_int_op(pos, ==, 0);
+ tt_assert(eof);
+ }
+ /* Otherwise, we got the EOF on the last read */
+#endif
+
+ /* Check it terminated correctly */
+ retval = tor_get_exit_code(process_handle, 1, &exit_code);
+ tt_int_op(retval, ==, PROCESS_EXIT_EXITED);
+ tt_int_op(exit_code, ==, expected_exit);
+
+ // TODO: Make test-child exit with something other than 0
+
+ /* Check stderr */
+ pos = tor_read_all_from_process_stderr(&process_handle, stderr_buf,
+ sizeof(stderr_buf) - 1);
+ tt_assert(pos >= 0);
+ stderr_buf[pos] = '\0';
+ tt_str_op(stderr_buf, ==, expected_err);
+ tt_int_op(pos, ==, strlen(expected_err));
+
+ done:
+ ;
+}
+
+/**
+ * Test that we can properly format q Windows command line
+ */
+static void
+test_util_join_win_cmdline(void *ptr)
+{
+ /* Based on some test cases from "Parsing C++ Command-Line Arguments" in
+ * MSDN but we don't exercise all quoting rules because tor_join_win_cmdline
+ * will try to only generate simple cases for the child process to parse;
+ * i.e. we never embed quoted strings in arguments. */
+
+ const char *argvs[][4] = {
+ {"a", "bb", "CCC", NULL}, // Normal
+ {NULL, NULL, NULL, NULL}, // Empty argument list
+ {"", NULL, NULL, NULL}, // Empty argument
+ {"\"a", "b\"b", "CCC\"", NULL}, // Quotes
+ {"a\tbc", "dd dd", "E", NULL}, // Whitespace
+ {"a\\\\\\b", "de fg", "H", NULL}, // Backslashes
+ {"a\\\"b", "\\c", "D\\", NULL}, // Backslashes before quote
+ {"a\\\\b c", "d", "E", NULL}, // Backslashes not before quote
+ {} // Terminator
+ };
+
+ const char *cmdlines[] = {
+ "a bb CCC",
+ "",
+ "\"\"",
+ "\\\"a b\\\"b CCC\\\"",
+ "\"a\tbc\" \"dd dd\" E",
+ "a\\\\\\b \"de fg\" H",
+ "a\\\\\\\"b \\c D\\",
+ "\"a\\\\b c\" d E",
+ NULL // Terminator
+ };
+
+ int i;
+ char *joined_argv;
+
+ (void)ptr;
+
+ for (i=0; cmdlines[i]!=NULL; i++) {
+ log_info(LD_GENERAL, "Joining argvs[%d], expecting <%s>", i, cmdlines[i]);
+ joined_argv = tor_join_win_cmdline(argvs[i]);
+ tt_str_op(joined_argv, ==, cmdlines[i]);
+ tor_free(joined_argv);
+ }
+
+ done:
+ ;
+}
+
+#define MAX_SPLIT_LINE_COUNT 3
+struct split_lines_test_t {
+ const char *orig_line; // Line to be split (may contain \0's)
+ int orig_length; // Length of orig_line
+ const char *split_line[MAX_SPLIT_LINE_COUNT]; // Split lines
+};
+
+/**
+ * Test that we properly split a buffer into lines
+ */
+static void
+test_util_split_lines(void *ptr)
+{
+ /* Test cases. orig_line of last test case must be NULL.
+ * The last element of split_line[i] must be NULL. */
+ struct split_lines_test_t tests[] = {
+ {"", 0, {NULL}},
+ {"foo", 3, {"foo", NULL}},
+ {"\n\rfoo\n\rbar\r\n", 12, {"foo", "bar", NULL}},
+ {"fo o\r\nb\tar", 10, {"fo o", "b.ar", NULL}},
+ {"\x0f""f\0o\0\n\x01""b\0r\0\r", 12, {".f.o.", ".b.r.", NULL}},
+ {NULL, 0, {}}
+ };
+
+ int i, j;
+ char *orig_line;
+ smartlist_t *sl;
+
+ (void)ptr;
+
+ for (i=0; tests[i].orig_line; i++) {
+ sl = smartlist_create();
+ /* Allocate space for string and trailing NULL */
+ orig_line = tor_memdup(tests[i].orig_line, tests[i].orig_length + 1);
+ tor_split_lines(sl, orig_line, tests[i].orig_length);
+
+ j = 0;
+ log_info(LD_GENERAL, "Splitting test %d of length %d",
+ i, tests[i].orig_length);
+ SMARTLIST_FOREACH(sl, const char *, line,
+ {
+ /* Check we have not got too many lines */
+ tt_int_op(j, <, MAX_SPLIT_LINE_COUNT);
+ /* Check that there actually should be a line here */
+ tt_assert(tests[i].split_line[j] != NULL);
+ log_info(LD_GENERAL, "Line %d of test %d, should be <%s>",
+ j, i, tests[i].split_line[j]);
+ /* Check that the line is as expected */
+ tt_str_op(tests[i].split_line[j], ==, line);
+ j++;
+ });
+ /* Check that we didn't miss some lines */
+ tt_assert(tests[i].split_line[j] == NULL);
+ tor_free(orig_line);
+ smartlist_free(sl);
+ }
+
+ done:
+ ;
+}
+
+static void
test_util_di_ops(void)
{
#define LT -1
@@ -1319,6 +1766,15 @@ struct testcase_t util_tests[] = {
#ifdef MS_WINDOWS
UTIL_TEST(load_win_lib, 0),
#endif
+ UTIL_TEST(exit_status, 0),
+#ifndef MS_WINDOWS
+ UTIL_TEST(fgets_eagain, TT_SKIP),
+#endif
+ UTIL_TEST(spawn_background_ok, 0),
+ UTIL_TEST(spawn_background_fail, 0),
+ UTIL_TEST(spawn_background_partial_read, 0),
+ UTIL_TEST(join_win_cmdline, 0),
+ UTIL_TEST(split_lines, 0),
END_OF_TESTCASES
};
diff --git a/src/test/tinytest.c b/src/test/tinytest.c
index 11ffc2fe56..8caa4f5453 100644
--- a/src/test/tinytest.c
+++ b/src/test/tinytest.c
@@ -28,7 +28,11 @@
#include <string.h>
#include <assert.h>
-#ifdef WIN32
+#ifdef TINYTEST_LOCAL
+#include "tinytest_local.h"
+#endif
+
+#ifdef _WIN32
#include <windows.h>
#else
#include <sys/types.h>
@@ -40,9 +44,6 @@
#define __attribute__(x)
#endif
-#ifdef TINYTEST_LOCAL
-#include "tinytest_local.h"
-#endif
#include "tinytest.h"
#include "tinytest_macros.h"
@@ -64,7 +65,7 @@ const char *cur_test_prefix = NULL; /**< prefix of the current test group */
/** Name of the current test, if we haven't logged is yet. Used for --quiet */
const char *cur_test_name = NULL;
-#ifdef WIN32
+#ifdef _WIN32
/** Pointer to argv[0] for win32. */
static const char *commandname = NULL;
#endif
@@ -103,7 +104,7 @@ static enum outcome
_testcase_run_forked(const struct testgroup_t *group,
const struct testcase_t *testcase)
{
-#ifdef WIN32
+#ifdef _WIN32
/* Fork? On Win32? How primitive! We'll do what the smart kids do:
we'll invoke our own exe (whose name we recall from the command
line) with a command line that tells it to run just the test we
@@ -174,6 +175,7 @@ _testcase_run_forked(const struct testgroup_t *group,
exit(1);
}
exit(0);
+ return FAIL; /* unreachable */
} else {
/* parent */
int status, r;
@@ -239,6 +241,7 @@ testcase_run_one(const struct testgroup_t *group,
if (opt_forked) {
exit(outcome==OK ? 0 : (outcome==SKIP?MAGIC_EXITCODE : 1));
+ return 1; /* unreachable */
} else {
return (int)outcome;
}
@@ -287,7 +290,7 @@ tinytest_main(int c, const char **v, struct testgroup_t *groups)
{
int i, j, n=0;
-#ifdef WIN32
+#ifdef _WIN32
commandname = v[0];
#endif
for (i=1; i<c; ++i) {
diff --git a/src/test/tinytest_demo.c b/src/test/tinytest_demo.c
index 4d2f588435..98cb773d1a 100644
--- a/src/test/tinytest_demo.c
+++ b/src/test/tinytest_demo.c
@@ -1,4 +1,4 @@
-/* tinytest_demo.c -- Copyright 2009 Nick Mathewson
+/* tinytest_demo.c -- Copyright 2009-2010 Nick Mathewson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -53,7 +53,7 @@ test_strcmp(void *data)
}
/* Pretty often, calling tt_abort_msg to indicate failure is more
- heavy-weight than you want. Instead, just say: */
+ heavy-weight than you want. Instead, just say: */
tt_assert(strcmp("testcase", "testcase") == 0);
/* Occasionally, you don't want to stop the current testcase just
@@ -91,7 +91,7 @@ test_strcmp(void *data)
/* First you declare a type to hold the environment info, and functions to
set it up and tear it down. */
struct data_buffer {
- /* We're just going to have couple of character buffer. Using
+ /* We're just going to have couple of character buffer. Using
setup/teardown functions is probably overkill for this case.
You could also do file descriptors, complicated handles, temporary
@@ -164,7 +164,7 @@ test_memcpy(void *ptr)
/* ============================================================ */
-/* Now we need to make sure that our tests get invoked. First, you take
+/* Now we need to make sure that our tests get invoked. First, you take
a bunch of related tests and put them into an array of struct testcase_t.
*/
@@ -189,15 +189,15 @@ struct testgroup_t groups[] = {
/* Every group has a 'prefix', and an array of tests. That's it. */
{ "demo/", demo_tests },
- END_OF_GROUPS
+ END_OF_GROUPS
};
int
main(int c, const char **v)
{
- /* Finally, just call tinytest_main(). It lets you specify verbose
- or quiet output with --verbose and --quiet. You can list
+ /* Finally, just call tinytest_main(). It lets you specify verbose
+ or quiet output with --verbose and --quiet. You can list
specific tests:
tinytest-demo demo/memcpy
diff --git a/src/test/tinytest_macros.h b/src/test/tinytest_macros.h
index a7fa64a824..032393ccf7 100644
--- a/src/test/tinytest_macros.h
+++ b/src/test/tinytest_macros.h
@@ -90,10 +90,10 @@
TT_STMT_BEGIN \
if (!(b)) { \
_tinytest_set_test_failed(); \
- TT_GRIPE((msg)); \
+ TT_GRIPE(("%s",msg)); \
fail; \
} else { \
- TT_BLATHER((msg)); \
+ TT_BLATHER(("%s",msg)); \
} \
TT_STMT_END
@@ -111,7 +111,7 @@
#define tt_assert(b) tt_assert_msg((b), "assert("#b")")
#define tt_assert_test_fmt_type(a,b,str_test,type,test,printf_type,printf_fmt, \
- setup_block,cleanup_block) \
+ setup_block,cleanup_block,die_on_fail) \
TT_STMT_BEGIN \
type _val1 = (type)(a); \
type _val2 = (type)(b); \
@@ -135,33 +135,50 @@
cleanup_block; \
if (!_tt_status) { \
_tinytest_set_test_failed(); \
- TT_EXIT_TEST_FUNCTION; \
+ die_on_fail ; \
} \
} \
TT_STMT_END
-#define tt_assert_test_type(a,b,str_test,type,test,fmt) \
+#define tt_assert_test_type(a,b,str_test,type,test,fmt,die_on_fail) \
tt_assert_test_fmt_type(a,b,str_test,type,test,type,fmt, \
- {_print=_value;},{})
+ {_print=_value;},{},die_on_fail)
/* Helper: assert that a op b, when cast to type. Format the values with
* printf format fmt on failure. */
#define tt_assert_op_type(a,op,b,type,fmt) \
- tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt)
+ tt_assert_test_type(a,b,#a" "#op" "#b,type,(_val1 op _val2),fmt, \
+ TT_EXIT_TEST_FUNCTION)
#define tt_int_op(a,op,b) \
- tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld")
+ tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2), \
+ "%ld",TT_EXIT_TEST_FUNCTION)
#define tt_uint_op(a,op,b) \
tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \
- (_val1 op _val2),"%lu")
+ (_val1 op _val2),"%lu",TT_EXIT_TEST_FUNCTION)
#define tt_ptr_op(a,op,b) \
tt_assert_test_type(a,b,#a" "#op" "#b,void*, \
- (_val1 op _val2),"%p")
+ (_val1 op _val2),"%p",TT_EXIT_TEST_FUNCTION)
#define tt_str_op(a,op,b) \
tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \
- (strcmp(_val1,_val2) op 0),"<%s>")
+ (strcmp(_val1,_val2) op 0),"<%s>",TT_EXIT_TEST_FUNCTION)
+
+#define tt_want_int_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,long,(_val1 op _val2),"%ld",(void)0)
+
+#define tt_want_uint_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,unsigned long, \
+ (_val1 op _val2),"%lu",(void)0)
+
+#define tt_want_ptr_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,void*, \
+ (_val1 op _val2),"%p",(void)0)
+
+#define tt_want_str_op(a,op,b) \
+ tt_assert_test_type(a,b,#a" "#op" "#b,const char *, \
+ (strcmp(_val1,_val2) op 0),"<%s>",(void)0)
#endif