More unit tests for process spawning

Try killing a running process; try noticing that a process has
exited without checking its output; verify that waitpid_cb (when
present) is set to NULL when you would expect it to be.
This commit is contained in:
Nick Mathewson 2014-04-30 12:50:00 -04:00
parent e2e588175e
commit e3833193af
2 changed files with 146 additions and 18 deletions

View File

@ -9,6 +9,13 @@
#else #else
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <string.h>
#ifdef _WIN32
#define SLEEP(sec) Sleep((sec)*1000)
#else
#define SLEEP(sec) sleep(sec)
#endif
/** Trivial test program which prints out its command line arguments so we can /** Trivial test program which prints out its command line arguments so we can
* check if tor_spawn_background() works */ * check if tor_spawn_background() works */
@ -16,27 +23,38 @@ int
main(int argc, char **argv) main(int argc, char **argv)
{ {
int i; int i;
int delay = 1;
int fast = 0;
if (argc > 1) {
if (!strcmp(argv[1], "--hang")) {
delay = 60;
} else if (!strcmp(argv[1], "--fast")) {
fast = 1;
delay = 0;
}
}
fprintf(stdout, "OUT\n"); fprintf(stdout, "OUT\n");
fprintf(stderr, "ERR\n"); fprintf(stderr, "ERR\n");
for (i = 1; i < argc; i++) for (i = 1; i < argc; i++)
fprintf(stdout, "%s\n", argv[i]); fprintf(stdout, "%s\n", argv[i]);
fprintf(stdout, "SLEEPING\n"); if (!fast)
fprintf(stdout, "SLEEPING\n");
/* We need to flush stdout so that test_util_spawn_background_partial_read() /* We need to flush stdout so that test_util_spawn_background_partial_read()
succeed. Otherwise ReadFile() will get the entire output in one */ succeed. Otherwise ReadFile() will get the entire output in one */
// XXX: Can we make stdio flush on newline? // XXX: Can we make stdio flush on newline?
fflush(stdout); fflush(stdout);
#ifdef _WIN32 if (!fast)
Sleep(1000); SLEEP(1);
#else
sleep(1);
#endif
fprintf(stdout, "DONE\n"); fprintf(stdout, "DONE\n");
#ifdef _WIN32 fflush(stdout);
Sleep(1000); if (fast)
#else return 0;
sleep(1);
#endif while (--delay) {
SLEEP(1);
}
return 0; return 0;
} }

View File

@ -14,6 +14,7 @@
#include "test.h" #include "test.h"
#include "mempool.h" #include "mempool.h"
#include "memarea.h" #include "memarea.h"
#include "util_process.h"
#ifdef _WIN32 #ifdef _WIN32
#include <tchar.h> #include <tchar.h>
@ -2529,6 +2530,10 @@ test_util_fgets_eagain(void *ptr)
} }
#endif #endif
#ifdef _WIN32
#define notify_pending_waitpid_callbacks() STMT_NIL
#endif
/** Helper function for testing tor_spawn_background */ /** Helper function for testing tor_spawn_background */
static void static void
run_util_spawn_background(const char *argv[], const char *expected_out, run_util_spawn_background(const char *argv[], const char *expected_out,
@ -2548,19 +2553,28 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
status = tor_spawn_background(argv[0], argv, NULL, &process_handle); status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
#endif #endif
notify_pending_waitpid_callbacks();
test_eq(expected_status, status); test_eq(expected_status, status);
if (status == PROCESS_STATUS_ERROR) if (status == PROCESS_STATUS_ERROR) {
tt_ptr_op(process_handle, ==, NULL);
return; return;
}
test_assert(process_handle != NULL); test_assert(process_handle != NULL);
test_eq(expected_status, process_handle->status); test_eq(expected_status, process_handle->status);
#ifndef _WIN32
notify_pending_waitpid_callbacks();
tt_ptr_op(process_handle->waitpid_cb, !=, NULL);
#endif
#ifdef _WIN32 #ifdef _WIN32
test_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE); test_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
test_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE); test_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
#else #else
test_assert(process_handle->stdout_pipe > 0); test_assert(process_handle->stdout_pipe >= 0);
test_assert(process_handle->stderr_pipe > 0); test_assert(process_handle->stderr_pipe >= 0);
#endif #endif
/* Check stdout */ /* Check stdout */
@ -2571,12 +2585,19 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
test_eq(strlen(expected_out), pos); test_eq(strlen(expected_out), pos);
test_streq(expected_out, stdout_buf); test_streq(expected_out, stdout_buf);
notify_pending_waitpid_callbacks();
/* Check it terminated correctly */ /* Check it terminated correctly */
retval = tor_get_exit_code(process_handle, 1, &exit_code); retval = tor_get_exit_code(process_handle, 1, &exit_code);
test_eq(PROCESS_EXIT_EXITED, retval); test_eq(PROCESS_EXIT_EXITED, retval);
test_eq(expected_exit, exit_code); test_eq(expected_exit, exit_code);
// TODO: Make test-child exit with something other than 0 // 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 */ /* Check stderr */
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
sizeof(stderr_buf) - 1); sizeof(stderr_buf) - 1);
@ -2585,6 +2606,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
test_streq(expected_err, stderr_buf); test_streq(expected_err, stderr_buf);
test_eq(strlen(expected_err), pos); test_eq(strlen(expected_err), pos);
notify_pending_waitpid_callbacks();
done: done:
if (process_handle) if (process_handle)
tor_process_handle_destroy(process_handle, 1); tor_process_handle_destroy(process_handle, 1);
@ -2607,7 +2630,7 @@ test_util_spawn_background_ok(void *ptr)
(void)ptr; (void)ptr;
run_util_spawn_background(argv, expected_out, expected_err, 0, 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 */ /** Check that failing to find the executable works as expected */
@ -2637,13 +2660,13 @@ test_util_spawn_background_fail(void *ptr)
"ERR: Failed to spawn background process - code %s\n", code); "ERR: Failed to spawn background process - code %s\n", code);
run_util_spawn_background(argv, expected_out, expected_err, 255, 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 /** Test that reading from a handle returns a partial read rather than
* blocking */ * blocking */
static void 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_exit = 0;
const int expected_status = PROCESS_STATUS_RUNNING; const int expected_status = PROCESS_STATUS_RUNNING;
@ -2668,7 +2691,16 @@ test_util_spawn_background_partial_read(void *ptr)
int eof = 0; int eof = 0;
#endif #endif
int expected_out_ctr; int expected_out_ctr;
(void)ptr;
if (exit_early) {
argv[1] = "--hang";
#ifdef _WIN32
expected_out[0] = "OUT\r\n--hang\r\nSLEEPING\r\n";
#else
expected_out[0] = "OUT\n--hang\nSLEEPING\n";
#endif
}
/* Start the program */ /* Start the program */
#ifdef _WIN32 #ifdef _WIN32
@ -2704,6 +2736,12 @@ test_util_spawn_background_partial_read(void *ptr)
expected_out_ctr++; 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 */ /* The process should have exited without writing more */
#ifdef _WIN32 #ifdef _WIN32
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf, pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
@ -2741,6 +2779,76 @@ test_util_spawn_background_partial_read(void *ptr)
tor_process_handle_destroy(process_handle, 1); 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;
#ifdef _WIN32
const char *argv[] = {"test-child.exe", "--fast", NULL};
#else
const char *argv[] = {BUILDDIR "/src/test/test-child", "--fast", NULL};
#endif
(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);
}
/** /**
* Test for format_hex_number_sigsafe() * Test for format_hex_number_sigsafe()
*/ */
@ -3682,6 +3790,8 @@ struct testcase_t util_tests[] = {
UTIL_TEST(spawn_background_ok, 0), UTIL_TEST(spawn_background_ok, 0),
UTIL_TEST(spawn_background_fail, 0), UTIL_TEST(spawn_background_fail, 0),
UTIL_TEST(spawn_background_partial_read, 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_hex_number, 0),
UTIL_TEST(format_dec_number, 0), UTIL_TEST(format_dec_number, 0),
UTIL_TEST(join_win_cmdline, 0), UTIL_TEST(join_win_cmdline, 0),