diff options
Diffstat (limited to 'src/test/test_util.c')
-rw-r--r-- | src/test/test_util.c | 711 |
1 files changed, 629 insertions, 82 deletions
diff --git a/src/test/test_util.c b/src/test/test_util.c index 65d9d2f878..225fb790f9 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -4,6 +4,7 @@ /* See LICENSE for licensing information */ #include "orconfig.h" +#define COMPAT_PRIVATE #define CONTROL_PRIVATE #define MEMPOOL_PRIVATE #define UTIL_PRIVATE @@ -11,8 +12,11 @@ #include "config.h" #include "control.h" #include "test.h" +#ifdef ENABLE_MEMPOOLS #include "mempool.h" +#endif /* ENABLE_MEMPOOLS */ #include "memarea.h" +#include "util_process.h" #ifdef _WIN32 #include <tchar.h> @@ -101,6 +105,107 @@ test_util_read_file_eof_zero_bytes(void *arg) test_util_read_until_eof_impl("tor_test_fifo_empty", 0, 10000); } +/* Test the basic expected behaviour for write_chunks_to_file. + * NOTE: This will need to be updated if we ever change the tempfile location + * or extension */ +static void +test_util_write_chunks_to_file(void *arg) +{ + char *fname = NULL; + char *tempname = NULL; + char *str = NULL; + int r; + struct stat st; + + /* These should be two different sizes to ensure the data is different + * between the data file and the temp file's 'known string' */ + int temp_str_len = 1024; + int data_str_len = 512; + char *data_str = tor_malloc(data_str_len); + char *temp_str = tor_malloc(temp_str_len); + + smartlist_t *chunks = smartlist_new(); + sized_chunk_t c = {data_str, data_str_len/2}; + sized_chunk_t c2 = {data_str + data_str_len/2, data_str_len/2}; + (void)arg; + + crypto_rand(temp_str, temp_str_len); + crypto_rand(data_str, data_str_len); + + // Ensure it can write multiple chunks + + smartlist_add(chunks, &c); + smartlist_add(chunks, &c2); + + /* + * Check if it writes using a tempfile + */ + fname = tor_strdup(get_fname("write_chunks_with_tempfile")); + tor_asprintf(&tempname, "%s.tmp", fname); + + // write a known string to a file where the tempfile will be + r = write_bytes_to_file(tempname, temp_str, temp_str_len, 1); + tt_int_op(r, ==, 0); + + // call write_chunks_to_file + r = write_chunks_to_file(fname, chunks, 1, 0); + tt_int_op(r, ==, 0); + + // assert the file has been written (expected size) + str = read_file_to_str(fname, RFTS_BIN, &st); + tt_assert(str != NULL); + tt_u64_op((uint64_t)st.st_size, ==, data_str_len); + test_mem_op(data_str, ==, str, data_str_len); + tor_free(str); + + // assert that the tempfile is removed (should not leave artifacts) + str = read_file_to_str(tempname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); + tt_assert(str == NULL); + + // Remove old testfile for second test + r = unlink(fname); + tt_int_op(r, ==, 0); + tor_free(fname); + tor_free(tempname); + + /* + * Check if it skips using a tempfile with flags + */ + fname = tor_strdup(get_fname("write_chunks_with_no_tempfile")); + tor_asprintf(&tempname, "%s.tmp", fname); + + // write a known string to a file where the tempfile will be + r = write_bytes_to_file(tempname, temp_str, temp_str_len, 1); + tt_int_op(r, ==, 0); + + // call write_chunks_to_file with no_tempfile = true + r = write_chunks_to_file(fname, chunks, 1, 1); + tt_int_op(r, ==, 0); + + // assert the file has been written (expected size) + str = read_file_to_str(fname, RFTS_BIN, &st); + tt_assert(str != NULL); + tt_u64_op((uint64_t)st.st_size, ==, data_str_len); + test_mem_op(data_str, ==, str, data_str_len); + tor_free(str); + + // assert the tempfile still contains the known string + str = read_file_to_str(tempname, RFTS_BIN, &st); + tt_assert(str != NULL); + tt_u64_op((uint64_t)st.st_size, ==, temp_str_len); + test_mem_op(temp_str, ==, str, temp_str_len); + + done: + unlink(fname); + unlink(tempname); + smartlist_free(chunks); + tor_free(fname); + tor_free(tempname); + tor_free(str); + tor_free(data_str); + tor_free(temp_str); +} + static void test_util_time(void) { @@ -242,7 +347,7 @@ test_util_time(void) tv.tv_sec = (time_t)1326296338; tv.tv_usec = 3060; - format_iso_time(timestr, tv.tv_sec); + format_iso_time(timestr, (time_t)tv.tv_sec); test_streq("2012-01-11 15:38:58", timestr); /* The output of format_local_iso_time will vary by timezone, and setting our timezone for testing purposes would be a nontrivial flaky pain. @@ -250,7 +355,7 @@ test_util_time(void) format_local_iso_time(timestr, tv.tv_sec); test_streq("2012-01-11 10:38:58", timestr); */ - format_iso_time_nospace(timestr, tv.tv_sec); + format_iso_time_nospace(timestr, (time_t)tv.tv_sec); test_streq("2012-01-11T15:38:58", timestr); test_eq(strlen(timestr), ISO_TIME_LEN); format_iso_time_nospace_usec(timestr, &tv); @@ -796,6 +901,64 @@ test_util_expand_filename(void) } #endif +/** Test tor_escape_str_for_pt_args(). */ +static void +test_util_escape_string_socks(void) +{ + char *escaped_string = NULL; + + /** Simple backslash escape. */ + escaped_string = tor_escape_str_for_pt_args("This is a backslash: \\",";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "This is a backslash: \\\\"); + tor_free(escaped_string); + + /** Simple semicolon escape. */ + escaped_string = tor_escape_str_for_pt_args("First rule:Do not use ;",";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "First rule:Do not use \\;"); + tor_free(escaped_string); + + /** Empty string. */ + escaped_string = tor_escape_str_for_pt_args("", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, ""); + tor_free(escaped_string); + + /** Escape all characters. */ + escaped_string = tor_escape_str_for_pt_args(";\\;\\", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "\\;\\\\\\;\\\\"); + tor_free(escaped_string); + + escaped_string = tor_escape_str_for_pt_args(";", ";\\"); + test_assert(escaped_string); + test_streq(escaped_string, "\\;"); + tor_free(escaped_string); + + done: + tor_free(escaped_string); +} + +static void +test_util_string_is_key_value(void *ptr) +{ + (void)ptr; + test_assert(string_is_key_value(LOG_WARN, "key=value")); + test_assert(string_is_key_value(LOG_WARN, "k=v")); + test_assert(string_is_key_value(LOG_WARN, "key=")); + test_assert(string_is_key_value(LOG_WARN, "x=")); + test_assert(string_is_key_value(LOG_WARN, "xx=")); + test_assert(!string_is_key_value(LOG_WARN, "=value")); + test_assert(!string_is_key_value(LOG_WARN, "=x")); + test_assert(!string_is_key_value(LOG_WARN, "=")); + + /* ??? */ + /* test_assert(!string_is_key_value(LOG_WARN, "===")); */ + done: + ; +} + /** Test basic string functionality. */ static void test_util_strmisc(void) @@ -867,6 +1030,8 @@ test_util_strmisc(void) test_eq(0L, tor_parse_long("10",-2,0,100,NULL,NULL)); test_eq(68284L, tor_parse_long("10abc",16,0,70000,NULL,NULL)); test_eq(68284L, tor_parse_long("10ABC",16,0,70000,NULL,NULL)); + test_eq(0, tor_parse_long("10ABC",-1,0,70000,&i,NULL)); + test_eq(i, 0); /* Test parse_ulong */ test_eq(0UL, tor_parse_ulong("",10,0,100,NULL,NULL)); @@ -878,6 +1043,8 @@ test_util_strmisc(void) test_eq(0UL, tor_parse_ulong("8",8,0,100,NULL,NULL)); test_eq(50UL, tor_parse_ulong("50",10,50,100,NULL,NULL)); test_eq(0UL, tor_parse_ulong("-50",10,-100,100,NULL,NULL)); + test_eq(0UL, tor_parse_ulong("50",-1,50,100,&i,NULL)); + test_eq(0, i); /* Test parse_uint64 */ test_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp)); @@ -890,6 +1057,9 @@ test_util_strmisc(void) test_assert(U64_LITERAL(0) == tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp)); test_eq(0, i); + test_assert(U64_LITERAL(0) == + tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp)); + test_eq(0, i); { /* Test parse_double */ @@ -923,7 +1093,7 @@ test_util_strmisc(void) test_eq(i, 0); test_eq(0UL, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL)); test_eq(i, 0); - test_eq(U64_LITERAL(0), tor_parse_uint64(TOOBIG, 10, + tt_u64_op(U64_LITERAL(0), ==, tor_parse_uint64(TOOBIG, 10, 0, UINT64_MAX, &i, NULL)); test_eq(i, 0); } @@ -1022,19 +1192,19 @@ test_util_strmisc(void) } /* Test str-foo functions */ - cp = tor_strdup("abcdef"); - test_assert(tor_strisnonupper(cp)); - cp[3] = 'D'; - test_assert(!tor_strisnonupper(cp)); - tor_strupper(cp); - test_streq(cp, "ABCDEF"); - tor_strlower(cp); - test_streq(cp, "abcdef"); - test_assert(tor_strisnonupper(cp)); - test_assert(tor_strisprint(cp)); - cp[3] = 3; - test_assert(!tor_strisprint(cp)); - tor_free(cp); + cp_tmp = tor_strdup("abcdef"); + test_assert(tor_strisnonupper(cp_tmp)); + cp_tmp[3] = 'D'; + test_assert(!tor_strisnonupper(cp_tmp)); + tor_strupper(cp_tmp); + test_streq(cp_tmp, "ABCDEF"); + tor_strlower(cp_tmp); + test_streq(cp_tmp, "abcdef"); + test_assert(tor_strisnonupper(cp_tmp)); + test_assert(tor_strisprint(cp_tmp)); + cp_tmp[3] = 3; + test_assert(!tor_strisprint(cp_tmp)); + tor_free(cp_tmp); /* Test memmem and memstr */ { @@ -1045,6 +1215,10 @@ test_util_strmisc(void) test_assert(!tor_memmem(haystack, 4, "cde", 3)); haystack = "ababcad"; test_eq_ptr(tor_memmem(haystack, 7, "abc", 3), haystack + 2); + test_eq_ptr(tor_memmem(haystack, 7, "ad", 2), haystack + 5); + test_eq_ptr(tor_memmem(haystack, 7, "cad", 3), haystack + 4); + test_assert(!tor_memmem(haystack, 7, "dadad", 5)); + test_assert(!tor_memmem(haystack, 7, "abcdefghij", 10)); /* memstr */ test_eq_ptr(tor_memstr(haystack, 7, "abc"), haystack + 2); test_eq_ptr(tor_memstr(haystack, 7, "cad"), haystack + 4); @@ -1117,21 +1291,21 @@ test_util_pow2(void) test_eq(tor_log2(UINT64_MAX), 63); /* Test round_to_power_of_2 */ - test_eq(round_to_power_of_2(120), 128); - test_eq(round_to_power_of_2(128), 128); - test_eq(round_to_power_of_2(130), 128); - test_eq(round_to_power_of_2(U64_LITERAL(40000000000000000)), - U64_LITERAL(1)<<55); - test_eq(round_to_power_of_2(U64_LITERAL(0xffffffffffffffff)), + tt_u64_op(round_to_power_of_2(120), ==, 128); + tt_u64_op(round_to_power_of_2(128), ==, 128); + tt_u64_op(round_to_power_of_2(130), ==, 128); + tt_u64_op(round_to_power_of_2(U64_LITERAL(40000000000000000)), ==, + U64_LITERAL(1)<<55); + tt_u64_op(round_to_power_of_2(U64_LITERAL(0xffffffffffffffff)), ==, U64_LITERAL(1)<<63); - test_eq(round_to_power_of_2(0), 1); - test_eq(round_to_power_of_2(1), 1); - test_eq(round_to_power_of_2(2), 2); - test_eq(round_to_power_of_2(3), 2); - test_eq(round_to_power_of_2(4), 4); - test_eq(round_to_power_of_2(5), 4); - test_eq(round_to_power_of_2(6), 4); - test_eq(round_to_power_of_2(7), 8); + tt_u64_op(round_to_power_of_2(0), ==, 1); + tt_u64_op(round_to_power_of_2(1), ==, 1); + tt_u64_op(round_to_power_of_2(2), ==, 2); + tt_u64_op(round_to_power_of_2(3), ==, 2); + tt_u64_op(round_to_power_of_2(4), ==, 4); + tt_u64_op(round_to_power_of_2(5), ==, 4); + tt_u64_op(round_to_power_of_2(6), ==, 4); + tt_u64_op(round_to_power_of_2(7), ==, 8); done: ; @@ -1410,14 +1584,14 @@ test_util_mmap(void) test_eq(mapping->size, strlen("Short file.")); test_streq(mapping->data, "Short file."); #ifdef _WIN32 - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; test_assert(unlink(fname1) == 0); #else /* make sure we can unlink. */ test_assert(unlink(fname1) == 0); test_streq(mapping->data, "Short file."); - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; #endif @@ -1438,7 +1612,7 @@ test_util_mmap(void) test_assert(mapping); test_eq(mapping->size, buflen); test_memeq(mapping->data, buf, buflen); - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; /* Now try a big aligned file. */ @@ -1447,7 +1621,7 @@ test_util_mmap(void) test_assert(mapping); test_eq(mapping->size, 16384); test_memeq(mapping->data, buf, 16384); - tor_munmap_file(mapping); + tt_int_op(0, ==, tor_munmap_file(mapping)); mapping = NULL; done: @@ -1460,8 +1634,7 @@ test_util_mmap(void) tor_free(fname3); tor_free(buf); - if (mapping) - tor_munmap_file(mapping); + tor_munmap_file(mapping); } /** Run unit tests for escaping/unescaping data for use by controllers. */ @@ -1729,6 +1902,8 @@ test_util_path_is_relative(void) ; } +#ifdef ENABLE_MEMPOOLS + /** Run unittests for memory pool allocator */ static void test_util_mempool(void) @@ -1787,6 +1962,8 @@ test_util_mempool(void) mp_pool_destroy(pool); } +#endif /* ENABLE_MEMPOOLS */ + /** Run unittests for memory area allocator */ static void test_util_memarea(void) @@ -2071,18 +2248,21 @@ test_util_asprintf(void *ptr) test_assert(cp); test_streq("simple string 100% safe", cp); test_eq(strlen(cp), r); + tor_free(cp); /* empty string */ r = tor_asprintf(&cp, "%s", ""); test_assert(cp); test_streq("", cp); test_eq(strlen(cp), r); + tor_free(cp); /* numbers (%i) */ r = tor_asprintf(&cp, "I like numbers-%2i, %i, etc.", -1, 2); test_assert(cp); test_streq("I like numbers--1, 2, etc.", cp); test_eq(strlen(cp), r); + /* don't free cp; next test uses it. */ /* numbers (%d) */ r = tor_asprintf(&cp2, "First=%d, Second=%d", 101, 202); @@ -2155,6 +2335,8 @@ test_util_listdir(void *ptr) done: tor_free(fname1); tor_free(fname2); + tor_free(fname3); + tor_free(dir1); tor_free(dirname); if (dir_contents) { SMARTLIST_FOREACH(dir_contents, char *, cp, tor_free(cp)); @@ -2223,6 +2405,7 @@ test_util_load_win_lib(void *ptr) } #endif +#ifndef _WIN32 static void clear_hex_errno(char *hex_errno) { @@ -2267,6 +2450,7 @@ test_util_exit_status(void *ptr) done: ; } +#endif #ifndef _WIN32 /** Check that fgets waits until a full line, and not return a partial line, on @@ -2355,6 +2539,19 @@ test_util_fgets_eagain(void *ptr) } #endif +#ifndef BUILDDIR +#define BUILDDIR "." +#endif + +#ifdef _WIN32 +#define notify_pending_waitpid_callbacks() STMT_NIL +#define TEST_CHILD "test-child.exe" +#define EOL "\r\n" +#else +#define TEST_CHILD (BUILDDIR "/src/test/test-child") +#define EOL "\n" +#endif + /** Helper function for testing tor_spawn_background */ static void run_util_spawn_background(const char *argv[], const char *expected_out, @@ -2374,19 +2571,28 @@ run_util_spawn_background(const char *argv[], const char *expected_out, status = tor_spawn_background(argv[0], argv, NULL, &process_handle); #endif + notify_pending_waitpid_callbacks(); + test_eq(expected_status, status); - if (status == PROCESS_STATUS_ERROR) + if (status == PROCESS_STATUS_ERROR) { + tt_ptr_op(process_handle, ==, NULL); return; + } test_assert(process_handle != NULL); test_eq(expected_status, process_handle->status); +#ifndef _WIN32 + notify_pending_waitpid_callbacks(); + tt_ptr_op(process_handle->waitpid_cb, !=, NULL); +#endif + #ifdef _WIN32 test_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE); test_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE); #else - test_assert(process_handle->stdout_pipe > 0); - test_assert(process_handle->stderr_pipe > 0); + test_assert(process_handle->stdout_pipe >= 0); + test_assert(process_handle->stderr_pipe >= 0); #endif /* Check stdout */ @@ -2397,12 +2603,19 @@ run_util_spawn_background(const char *argv[], const char *expected_out, test_eq(strlen(expected_out), pos); test_streq(expected_out, stdout_buf); + notify_pending_waitpid_callbacks(); + /* Check it terminated correctly */ retval = tor_get_exit_code(process_handle, 1, &exit_code); test_eq(PROCESS_EXIT_EXITED, retval); test_eq(expected_exit, exit_code); // TODO: Make test-child exit with something other than 0 +#ifndef _WIN32 + notify_pending_waitpid_callbacks(); + tt_ptr_op(process_handle->waitpid_cb, ==, NULL); +#endif + /* Check stderr */ pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, sizeof(stderr_buf) - 1); @@ -2411,6 +2624,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out, test_streq(expected_err, stderr_buf); test_eq(strlen(expected_err), pos); + notify_pending_waitpid_callbacks(); + done: if (process_handle) tor_process_handle_destroy(process_handle, 1); @@ -2420,29 +2635,20 @@ run_util_spawn_background(const char *argv[], const char *expected_out, static void test_util_spawn_background_ok(void *ptr) { -#ifdef _WIN32 - 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 + const char *argv[] = {TEST_CHILD, "--test", NULL}; + const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL; + const char *expected_err = "ERR"EOL; (void)ptr; run_util_spawn_background(argv, expected_out, expected_err, 0, - PROCESS_STATUS_RUNNING); + PROCESS_STATUS_RUNNING); } /** Check that failing to find the executable works as expected */ static void test_util_spawn_background_fail(void *ptr) { -#ifndef BUILDDIR -#define BUILDDIR "." -#endif const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL}; const char *expected_err = ""; char expected_out[1024]; @@ -2463,13 +2669,13 @@ test_util_spawn_background_fail(void *ptr) "ERR: Failed to spawn background process - code %s\n", code); run_util_spawn_background(argv, expected_out, expected_err, 255, - expected_status); + 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) +test_util_spawn_background_partial_read_impl(int exit_early) { const int expected_exit = 0; const int expected_status = PROCESS_STATUS_RUNNING; @@ -2479,22 +2685,22 @@ test_util_spawn_background_partial_read(void *ptr) process_handle_t *process_handle=NULL; int status; char stdout_buf[100], stderr_buf[100]; -#ifdef _WIN32 - 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", + + const char *argv[] = {TEST_CHILD, "--test", NULL}; + const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL, + "DONE" EOL, NULL }; - const char *expected_err = "ERR\n"; + const char *expected_err = "ERR" EOL; + +#ifndef _WIN32 int eof = 0; #endif int expected_out_ctr; - (void)ptr; + + if (exit_early) { + argv[1] = "--hang"; + expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL; + } /* Start the program */ #ifdef _WIN32 @@ -2530,6 +2736,12 @@ test_util_spawn_background_partial_read(void *ptr) expected_out_ctr++; } + if (exit_early) { + tor_process_handle_destroy(process_handle, 1); + process_handle = NULL; + goto done; + } + /* The process should have exited without writing more */ #ifdef _WIN32 pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, @@ -2567,15 +2779,84 @@ test_util_spawn_background_partial_read(void *ptr) tor_process_handle_destroy(process_handle, 1); } +static void +test_util_spawn_background_partial_read(void *arg) +{ + (void)arg; + test_util_spawn_background_partial_read_impl(0); +} + +static void +test_util_spawn_background_exit_early(void *arg) +{ + (void)arg; + test_util_spawn_background_partial_read_impl(1); +} + +static void +test_util_spawn_background_waitpid_notify(void *arg) +{ + int retval, exit_code; + process_handle_t *process_handle=NULL; + int status; + int ms_timer; + + const char *argv[] = {TEST_CHILD, "--fast", NULL}; + + (void) arg; + +#ifdef _WIN32 + status = tor_spawn_background(NULL, argv, NULL, &process_handle); +#else + status = tor_spawn_background(argv[0], argv, NULL, &process_handle); +#endif + + tt_int_op(status, ==, PROCESS_STATUS_RUNNING); + tt_ptr_op(process_handle, !=, NULL); + + /* We're not going to look at the stdout/stderr output this time. Instead, + * we're testing whether notify_pending_waitpid_calbacks() can report the + * process exit (on unix) and/or whether tor_get_exit_code() can notice it + * (on windows) */ + +#ifndef _WIN32 + ms_timer = 30*1000; + tt_ptr_op(process_handle->waitpid_cb, !=, NULL); + while (process_handle->waitpid_cb && ms_timer > 0) { + tor_sleep_msec(100); + ms_timer -= 100; + notify_pending_waitpid_callbacks(); + } + tt_int_op(ms_timer, >, 0); + tt_ptr_op(process_handle->waitpid_cb, ==, NULL); +#endif + + ms_timer = 30*1000; + while (((retval = tor_get_exit_code(process_handle, 0, &exit_code)) + == PROCESS_EXIT_RUNNING) && ms_timer > 0) { + tor_sleep_msec(100); + ms_timer -= 100; + } + tt_int_op(ms_timer, >, 0); + + tt_int_op(retval, ==, PROCESS_EXIT_EXITED); + + done: + tor_process_handle_destroy(process_handle, 1); +} + +#undef TEST_CHILD +#undef EOL + /** - * Test for format_hex_number_for_helper_exit_status() + * Test for format_hex_number_sigsafe() */ static void test_util_format_hex_number(void *ptr) { int i, len; - char buf[HEX_ERRNO_SIZE + 1]; + char buf[33]; const struct { const char *str; unsigned int x; @@ -2584,6 +2865,8 @@ test_util_format_hex_number(void *ptr) {"1", 1}, {"273A", 0x273a}, {"FFFF", 0xffff}, + {"7FFFFFFF", 0x7fffffff}, + {"FFFFFFFF", 0xffffffff}, #if UINT_MAX >= 0xffffffff {"31BC421D", 0x31bc421d}, {"FFFFFFFF", 0xffffffff}, @@ -2594,19 +2877,73 @@ test_util_format_hex_number(void *ptr) (void)ptr; for (i = 0; test_data[i].str != NULL; ++i) { - len = format_hex_number_for_helper_exit_status(test_data[i].x, - buf, HEX_ERRNO_SIZE); + len = format_hex_number_sigsafe(test_data[i].x, buf, sizeof(buf)); + test_neq(len, 0); + test_eq(len, strlen(buf)); + test_streq(buf, test_data[i].str); + } + + test_eq(4, format_hex_number_sigsafe(0xffff, buf, 5)); + test_streq(buf, "FFFF"); + test_eq(0, format_hex_number_sigsafe(0xffff, buf, 4)); + test_eq(0, format_hex_number_sigsafe(0, buf, 1)); + + done: + return; +} + +/** + * Test for format_hex_number_sigsafe() + */ + +static void +test_util_format_dec_number(void *ptr) +{ + int i, len; + char buf[33]; + const struct { + const char *str; + unsigned int x; + } test_data[] = { + {"0", 0}, + {"1", 1}, + {"1234", 1234}, + {"12345678", 12345678}, + {"99999999", 99999999}, + {"100000000", 100000000}, + {"4294967295", 4294967295u}, +#if UINT_MAX > 0xffffffff + {"18446744073709551615", 18446744073709551615u }, +#endif + {NULL, 0} + }; + + (void)ptr; + + for (i = 0; test_data[i].str != NULL; ++i) { + len = format_dec_number_sigsafe(test_data[i].x, buf, sizeof(buf)); test_neq(len, 0); - buf[len] = '\0'; + test_eq(len, strlen(buf)); + test_streq(buf, test_data[i].str); + + len = format_dec_number_sigsafe(test_data[i].x, buf, + (int)(strlen(test_data[i].str) + 1)); + test_eq(len, strlen(buf)); test_streq(buf, test_data[i].str); } + test_eq(4, format_dec_number_sigsafe(7331, buf, 5)); + test_streq(buf, "7331"); + test_eq(0, format_dec_number_sigsafe(7331, buf, 4)); + test_eq(1, format_dec_number_sigsafe(0, buf, 2)); + test_eq(0, format_dec_number_sigsafe(0, buf, 1)); + done: return; } /** - * Test that we can properly format q Windows command line + * Test that we can properly format a Windows command line */ static void test_util_join_win_cmdline(void *ptr) @@ -2817,7 +3154,7 @@ test_util_eat_whitespace(void *ptr) (void)ptr; /* Try one leading ws */ - strcpy(str, "fuubaar"); + strlcpy(str, "fuubaar", sizeof(str)); for (i = 0; i < sizeof(ws); ++i) { str[0] = ws[i]; test_eq_ptr(str + 1, eat_whitespace(str)); @@ -2832,14 +3169,14 @@ test_util_eat_whitespace(void *ptr) test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Empty string */ - strcpy(str, ""); + strlcpy(str, "", sizeof(str)); test_eq_ptr(str, eat_whitespace(str)); test_eq_ptr(str, eat_whitespace_eos(str, str)); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str)); /* Only ws */ - strcpy(str, " \t\r\n"); + strlcpy(str, " \t\r\n", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + strlen(str) - 1, @@ -2847,7 +3184,7 @@ test_util_eat_whitespace(void *ptr) test_eq_ptr(str + strlen(str) - 1, eat_whitespace_eos_no_nl(str, str + strlen(str))); - strcpy(str, " \t\r "); + strlcpy(str, " \t\r ", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); @@ -2856,7 +3193,7 @@ test_util_eat_whitespace(void *ptr) eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Multiple ws */ - strcpy(str, "fuubaar"); + strlcpy(str, "fuubaar", sizeof(str)); for (i = 0; i < sizeof(ws); ++i) str[i] = ws[i]; test_eq_ptr(str + sizeof(ws), eat_whitespace(str)); @@ -2866,28 +3203,28 @@ test_util_eat_whitespace(void *ptr) eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat comment */ - strcpy(str, "# Comment \n No Comment"); + strlcpy(str, "# Comment \n No Comment", sizeof(str)); test_streq("No Comment", eat_whitespace(str)); test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat comment & ws mix */ - strcpy(str, " # \t Comment \n\t\nNo Comment"); + strlcpy(str, " # \t Comment \n\t\nNo Comment", sizeof(str)); test_streq("No Comment", eat_whitespace(str)); test_streq("No Comment", eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + 1, eat_whitespace_no_nl(str)); test_eq_ptr(str + 1, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Eat entire comment */ - strcpy(str, "#Comment"); + strlcpy(str, "#Comment", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str, eat_whitespace_no_nl(str)); test_eq_ptr(str, eat_whitespace_eos_no_nl(str, str + strlen(str))); /* Blank line, then comment */ - strcpy(str, " \t\n # Comment"); + strlcpy(str, " \t\n # Comment", sizeof(str)); test_eq_ptr(str + strlen(str), eat_whitespace(str)); test_eq_ptr(str + strlen(str), eat_whitespace_eos(str, str + strlen(str))); test_eq_ptr(str + 2, eat_whitespace_no_nl(str)); @@ -2913,6 +3250,8 @@ smartlist_new_from_text_lines(const char *lines) last_line = smartlist_pop_last(sl); if (last_line != NULL && *last_line != '\0') { smartlist_add(sl, last_line); + } else { + tor_free(last_line); } return sl; @@ -3212,12 +3551,204 @@ test_util_mathlog(void *arg) ; } +static void +test_util_round_to_next_multiple_of(void *arg) +{ + (void)arg; + + test_assert(round_uint64_to_next_multiple_of(0,1) == 0); + test_assert(round_uint64_to_next_multiple_of(0,7) == 0); + + test_assert(round_uint64_to_next_multiple_of(99,1) == 99); + test_assert(round_uint64_to_next_multiple_of(99,7) == 105); + test_assert(round_uint64_to_next_multiple_of(99,9) == 99); + + done: + ; +} + +static void +test_util_strclear(void *arg) +{ + static const char *vals[] = { "", "a", "abcdef", "abcdefgh", NULL }; + int i; + char *v = NULL; + (void)arg; + + for (i = 0; vals[i]; ++i) { + size_t n; + v = tor_strdup(vals[i]); + n = strlen(v); + tor_strclear(v); + tt_assert(tor_mem_is_zero(v, n+1)); + tor_free(v); + } + done: + tor_free(v); +} + #define UTIL_LEGACY(name) \ { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name } #define UTIL_TEST(name, flags) \ { #name, test_util_ ## name, flags, NULL, NULL } +#ifdef FD_CLOEXEC +#define CAN_CHECK_CLOEXEC +static int +fd_is_cloexec(tor_socket_t fd) +{ + int flags = fcntl(fd, F_GETFD, 0); + return (flags & FD_CLOEXEC) == FD_CLOEXEC; +} +#endif + +#ifndef _WIN32 +#define CAN_CHECK_NONBLOCK +static int +fd_is_nonblocking(tor_socket_t fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + return (flags & O_NONBLOCK) == O_NONBLOCK; +} +#endif + +static void +test_util_socket(void *arg) +{ + tor_socket_t fd1 = TOR_INVALID_SOCKET; + tor_socket_t fd2 = TOR_INVALID_SOCKET; + tor_socket_t fd3 = TOR_INVALID_SOCKET; + tor_socket_t fd4 = TOR_INVALID_SOCKET; + int n = get_n_open_sockets(); + + TT_BLATHER(("Starting with %d open sockets.", n)); + + (void)arg; + + fd1 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 0); + fd2 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 0, 1); + tt_assert(SOCKET_OK(fd1)); + tt_assert(SOCKET_OK(fd2)); + tt_int_op(get_n_open_sockets(), ==, n + 2); + //fd3 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 0); + //fd4 = tor_open_socket_with_extensions(AF_INET, SOCK_STREAM, 0, 1, 1); + fd3 = tor_open_socket(AF_INET, SOCK_STREAM, 0); + fd4 = tor_open_socket_nonblocking(AF_INET, SOCK_STREAM, 0); + tt_assert(SOCKET_OK(fd3)); + tt_assert(SOCKET_OK(fd4)); + tt_int_op(get_n_open_sockets(), ==, n + 4); + +#ifdef CAN_CHECK_CLOEXEC + tt_int_op(fd_is_cloexec(fd1), ==, 0); + tt_int_op(fd_is_cloexec(fd2), ==, 0); + tt_int_op(fd_is_cloexec(fd3), ==, 1); + tt_int_op(fd_is_cloexec(fd4), ==, 1); +#endif +#ifdef CAN_CHECK_NONBLOCK + tt_int_op(fd_is_nonblocking(fd1), ==, 0); + tt_int_op(fd_is_nonblocking(fd2), ==, 1); + tt_int_op(fd_is_nonblocking(fd3), ==, 0); + tt_int_op(fd_is_nonblocking(fd4), ==, 1); +#endif + + tor_close_socket(fd1); + tor_close_socket(fd2); + fd1 = fd2 = TOR_INVALID_SOCKET; + tt_int_op(get_n_open_sockets(), ==, n + 2); + tor_close_socket(fd3); + tor_close_socket(fd4); + fd3 = fd4 = TOR_INVALID_SOCKET; + tt_int_op(get_n_open_sockets(), ==, n); + + done: + if (SOCKET_OK(fd1)) + tor_close_socket(fd1); + if (SOCKET_OK(fd2)) + tor_close_socket(fd2); + if (SOCKET_OK(fd3)) + tor_close_socket(fd3); + if (SOCKET_OK(fd4)) + tor_close_socket(fd4); +} + +static void * +socketpair_test_setup(const struct testcase_t *testcase) +{ + return testcase->setup_data; +} +static int +socketpair_test_cleanup(const struct testcase_t *testcase, void *ptr) +{ + (void)testcase; + (void)ptr; + return 1; +} + +static const struct testcase_setup_t socketpair_setup = { + socketpair_test_setup, socketpair_test_cleanup +}; + +/* Test for socketpair and ersatz_socketpair(). We test them both, since + * the latter is a tolerably good way to exersize tor_accept_socket(). */ +static void +test_util_socketpair(void *arg) +{ + const int ersatz = !strcmp(arg, "1"); + int (*const tor_socketpair_fn)(int, int, int, tor_socket_t[2]) = + ersatz ? tor_ersatz_socketpair : tor_socketpair; + int n = get_n_open_sockets(); + tor_socket_t fds[2] = {TOR_INVALID_SOCKET, TOR_INVALID_SOCKET}; + const int family = AF_UNIX; + + tt_int_op(0, ==, tor_socketpair_fn(family, SOCK_STREAM, 0, fds)); + tt_assert(SOCKET_OK(fds[0])); + tt_assert(SOCKET_OK(fds[1])); + tt_int_op(get_n_open_sockets(), ==, n + 2); +#ifdef CAN_CHECK_CLOEXEC + tt_int_op(fd_is_cloexec(fds[0]), ==, 1); + tt_int_op(fd_is_cloexec(fds[1]), ==, 1); +#endif +#ifdef CAN_CHECK_NONBLOCK + tt_int_op(fd_is_nonblocking(fds[0]), ==, 0); + tt_int_op(fd_is_nonblocking(fds[1]), ==, 0); +#endif + + done: + if (SOCKET_OK(fds[0])) + tor_close_socket(fds[0]); + if (SOCKET_OK(fds[1])) + tor_close_socket(fds[1]); +} + +static void +test_util_max_mem(void *arg) +{ + size_t memory1, memory2; + int r, r2; + (void) arg; + + r = get_total_system_memory(&memory1); + r2 = get_total_system_memory(&memory2); + tt_int_op(r, ==, r2); + tt_uint_op(memory2, ==, memory1); + + TT_BLATHER(("System memory: "U64_FORMAT, U64_PRINTF_ARG(memory1))); + + if (r==0) { + /* You have at least a megabyte. */ + tt_uint_op(memory1, >, (1<<20)); + } else { + /* You do not have a petabyte. */ +#if SIZEOF_SIZE_T == SIZEOF_UINT64_T + tt_u64_op(memory1, <, (U64_LITERAL(1)<<50)); +#endif + } + + done: + ; +} + struct testcase_t util_tests[] = { UTIL_LEGACY(time), UTIL_TEST(parse_http_time, 0), @@ -3228,11 +3759,15 @@ struct testcase_t util_tests[] = { #ifndef _WIN32 UTIL_LEGACY(expand_filename), #endif + UTIL_LEGACY(escape_string_socks), + UTIL_LEGACY(string_is_key_value), UTIL_LEGACY(strmisc), UTIL_LEGACY(pow2), UTIL_LEGACY(gzip), UTIL_LEGACY(datadir), +#ifdef ENABLE_MEMPOOLS UTIL_LEGACY(mempool), +#endif UTIL_LEGACY(memarea), UTIL_LEGACY(control_formats), UTIL_LEGACY(mmap), @@ -3241,6 +3776,8 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(path_is_relative), UTIL_LEGACY(strtok), UTIL_LEGACY(di_ops), + UTIL_TEST(round_to_next_multiple_of, 0), + UTIL_TEST(strclear, 0), UTIL_TEST(find_str_at_start_of_line, 0), UTIL_TEST(string_is_C_identifier, 0), UTIL_TEST(asprintf, 0), @@ -3249,14 +3786,17 @@ struct testcase_t util_tests[] = { #ifdef _WIN32 UTIL_TEST(load_win_lib, 0), #endif - UTIL_TEST(exit_status, 0), #ifndef _WIN32 + UTIL_TEST(exit_status, 0), 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(spawn_background_exit_early, 0), + UTIL_TEST(spawn_background_waitpid_notify, 0), UTIL_TEST(format_hex_number, 0), + UTIL_TEST(format_dec_number, 0), UTIL_TEST(join_win_cmdline, 0), UTIL_TEST(split_lines, 0), UTIL_TEST(n_bits_set, 0), @@ -3268,8 +3808,15 @@ struct testcase_t util_tests[] = { UTIL_TEST(read_file_eof_tiny_limit, 0), UTIL_TEST(read_file_eof_two_loops, 0), UTIL_TEST(read_file_eof_zero_bytes, 0), + UTIL_TEST(write_chunks_to_file, 0), UTIL_TEST(mathlog, 0), UTIL_TEST(weak_random, 0), + UTIL_TEST(socket, TT_FORK), + { "socketpair", test_util_socketpair, TT_FORK, &socketpair_setup, + (void*)"0" }, + { "socketpair_ersatz", test_util_socketpair, TT_FORK, + &socketpair_setup, (void*)"1" }, + UTIL_TEST(max_mem, 0), END_OF_TESTCASES }; |