tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_process_slow.c (10207B)


      1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * \file test_process_slow.c
      6 * \brief Slow test cases for the Process API.
      7 */
      8 
      9 #define MAINLOOP_PRIVATE
     10 #include "orconfig.h"
     11 #include "core/or/or.h"
     12 #include "core/mainloop/mainloop.h"
     13 #include "lib/evloop/compat_libevent.h"
     14 #include "lib/process/process.h"
     15 #include "lib/process/waitpid.h"
     16 #include "test/test.h"
     17 
     18 #ifndef BUILDDIR
     19 #define BUILDDIR "."
     20 #endif
     21 
     22 #ifdef _WIN32
     23 #define TEST_PROCESS "test-process.exe"
     24 #else
     25 #define TEST_PROCESS BUILDDIR "/src/test/test-process"
     26 #endif /* defined(_WIN32) */
     27 
     28 /** Timer that ticks once a second and stop the event loop after 5 ticks. */
     29 static periodic_timer_t *main_loop_timeout_timer;
     30 
     31 /** How many times have our timer ticked? */
     32 static int timer_tick_count;
     33 
     34 struct process_data_t {
     35  smartlist_t *stdout_data;
     36  smartlist_t *stderr_data;
     37  smartlist_t *stdin_data;
     38  process_exit_code_t exit_code;
     39  bool did_exit;
     40 };
     41 
     42 typedef struct process_data_t process_data_t;
     43 
     44 static process_data_t *
     45 process_data_new(void)
     46 {
     47  process_data_t *process_data = tor_malloc_zero(sizeof(process_data_t));
     48  process_data->stdout_data = smartlist_new();
     49  process_data->stderr_data = smartlist_new();
     50  process_data->stdin_data = smartlist_new();
     51  return process_data;
     52 }
     53 
     54 static void
     55 process_data_free(process_data_t *process_data)
     56 {
     57  if (process_data == NULL)
     58    return;
     59 
     60  SMARTLIST_FOREACH(process_data->stdout_data, char *, x, tor_free(x));
     61  SMARTLIST_FOREACH(process_data->stderr_data, char *, x, tor_free(x));
     62  SMARTLIST_FOREACH(process_data->stdin_data, char *, x, tor_free(x));
     63 
     64  smartlist_free(process_data->stdout_data);
     65  smartlist_free(process_data->stderr_data);
     66  smartlist_free(process_data->stdin_data);
     67  tor_free(process_data);
     68 }
     69 
     70 static void
     71 process_stdout_callback(process_t *process, const char *data, size_t size)
     72 {
     73  tt_ptr_op(process, OP_NE, NULL);
     74  tt_ptr_op(data, OP_NE, NULL);
     75  tt_int_op(strlen(data), OP_EQ, size);
     76 
     77  process_data_t *process_data = process_get_data(process);
     78  smartlist_add(process_data->stdout_data, tor_strdup(data));
     79 
     80 done:
     81  return;
     82 }
     83 
     84 static void
     85 process_stderr_callback(process_t *process, const char *data, size_t size)
     86 {
     87  tt_ptr_op(process, OP_NE, NULL);
     88  tt_ptr_op(data, OP_NE, NULL);
     89  tt_int_op(strlen(data), OP_EQ, size);
     90 
     91  process_data_t *process_data = process_get_data(process);
     92  smartlist_add(process_data->stderr_data, tor_strdup(data));
     93 
     94 done:
     95  return;
     96 }
     97 
     98 static bool
     99 process_exit_callback(process_t *process, process_exit_code_t exit_code)
    100 {
    101  process_status_t status;
    102 
    103  tt_ptr_op(process, OP_NE, NULL);
    104 
    105  process_data_t *process_data = process_get_data(process);
    106  process_data->exit_code = exit_code;
    107  process_data->did_exit = true;
    108 
    109  /* Check if our process is still running? */
    110  status = process_get_status(process);
    111  tt_int_op(status, OP_EQ, PROCESS_STATUS_NOT_RUNNING);
    112 
    113 done:
    114  /* Do not free up our process_t. */
    115  return false;
    116 }
    117 
    118 #ifdef _WIN32
    119 static const char *
    120 get_win32_test_binary_path(void)
    121 {
    122  static char buffer[MAX_PATH];
    123 
    124  /* Get the absolute path of our binary: \path\to\test-slow.exe. */
    125  GetModuleFileNameA(GetModuleHandle(0), buffer, sizeof(buffer));
    126 
    127  /* Find our process name. */
    128  char *offset = strstr(buffer, "test-slow.exe");
    129  tt_ptr_op(offset, OP_NE, NULL);
    130 
    131  /* Change test-slow.exe to test-process.exe. */
    132  memcpy(offset, TEST_PROCESS, strlen(TEST_PROCESS));
    133 
    134  return buffer;
    135 done:
    136  return NULL;
    137 }
    138 #endif /* defined(_WIN32) */
    139 
    140 static void
    141 main_loop_timeout_cb(periodic_timer_t *timer, void *data)
    142 {
    143  /* Sanity check. */
    144  tt_ptr_op(timer, OP_EQ, main_loop_timeout_timer);
    145  tt_ptr_op(data, OP_NE, NULL);
    146 
    147  /* Our process data. */
    148  process_data_t *process_data = data;
    149 
    150  /* Our process did exit. */
    151  if (process_data->did_exit)
    152    tor_shutdown_event_loop_and_exit(0);
    153 
    154  /* Have we been called 10 times we exit the main loop. */
    155  timer_tick_count++;
    156 
    157  tt_int_op(timer_tick_count, OP_LT, 10);
    158 
    159 #ifndef _WIN32
    160  /* Call waitpid callbacks. */
    161  notify_pending_waitpid_callbacks();
    162 #endif
    163 
    164  return;
    165 done:
    166  /* Exit with an error. */
    167  tor_shutdown_event_loop_and_exit(-1);
    168 }
    169 
    170 static void
    171 run_main_loop(process_data_t *process_data)
    172 {
    173  int ret;
    174 
    175  /* Wake up after 1 seconds. */
    176  static const struct timeval interval = {1, 0};
    177 
    178  timer_tick_count = 0;
    179  main_loop_timeout_timer = periodic_timer_new(tor_libevent_get_base(),
    180                                               &interval,
    181                                               main_loop_timeout_cb,
    182                                               process_data);
    183 
    184  /* Run our main loop. */
    185  ret = run_main_loop_until_done();
    186 
    187  /* Clean up our main loop timeout timer. */
    188  tt_int_op(ret, OP_EQ, 0);
    189 
    190 done:
    191  periodic_timer_free(main_loop_timeout_timer);
    192 }
    193 
    194 static void
    195 test_callbacks(void *arg)
    196 {
    197  (void)arg;
    198  const char *filename = NULL;
    199 
    200 #ifdef _WIN32
    201  filename = get_win32_test_binary_path();
    202 #else
    203  filename = TEST_PROCESS;
    204 #endif
    205 
    206  /* Process callback data. */
    207  process_data_t *process_data = process_data_new();
    208 
    209  /* Setup our process. */
    210  process_t *process = process_new(filename);
    211  process_set_data(process, process_data);
    212  process_set_stdout_read_callback(process, process_stdout_callback);
    213  process_set_stderr_read_callback(process, process_stderr_callback);
    214  process_set_exit_callback(process, process_exit_callback);
    215 
    216  /* Set environment variable. */
    217  process_set_environment(process, "TOR_TEST_ENV", "Hello, from Tor!");
    218 
    219  /* Add some arguments. */
    220  process_append_argument(process, "This is the first one");
    221  process_append_argument(process, "Second one");
    222  process_append_argument(process, "Third: Foo bar baz");
    223 
    224  /* Run our process. */
    225  process_status_t status;
    226 
    227  status = process_exec(process);
    228  tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
    229 
    230  /* Write some lines to stdin. */
    231  process_printf(process, "Hi process!\r\n");
    232  process_printf(process, "Can you read more than one line?\n");
    233  process_printf(process, "Can you read partial ...");
    234  process_printf(process, " lines?\r\n");
    235 
    236  /* Start our main loop. */
    237  run_main_loop(process_data);
    238 
    239  /* We returned. Let's see what our event loop said. */
    240  tt_int_op(smartlist_len(process_data->stdout_data), OP_EQ, 12);
    241  tt_int_op(smartlist_len(process_data->stderr_data), OP_EQ, 3);
    242  tt_assert(process_data->did_exit);
    243  tt_u64_op(process_data->exit_code, OP_EQ, 0);
    244 
    245  /* Check stdout output. */
    246  char argv0_expected[256];
    247  tor_snprintf(argv0_expected, sizeof(argv0_expected),
    248               "argv[0] = '%s'", filename);
    249 
    250  tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
    251            argv0_expected);
    252  tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
    253            "argv[1] = 'This is the first one'");
    254  tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
    255            "argv[2] = 'Second one'");
    256  tt_str_op(smartlist_get(process_data->stdout_data, 3), OP_EQ,
    257            "argv[3] = 'Third: Foo bar baz'");
    258  tt_str_op(smartlist_get(process_data->stdout_data, 4), OP_EQ,
    259            "Environment variable TOR_TEST_ENV = 'Hello, from Tor!'");
    260  tt_str_op(smartlist_get(process_data->stdout_data, 5), OP_EQ,
    261            "Output on stdout");
    262  tt_str_op(smartlist_get(process_data->stdout_data, 6), OP_EQ,
    263            "This is a new line");
    264  tt_str_op(smartlist_get(process_data->stdout_data, 7), OP_EQ,
    265            "Partial line on stdout ...end of partial line on stdout");
    266  tt_str_op(smartlist_get(process_data->stdout_data, 8), OP_EQ,
    267            "Read line from stdin: 'Hi process!'");
    268  tt_str_op(smartlist_get(process_data->stdout_data, 9), OP_EQ,
    269            "Read line from stdin: 'Can you read more than one line?'");
    270  tt_str_op(smartlist_get(process_data->stdout_data, 10), OP_EQ,
    271            "Read line from stdin: 'Can you read partial ... lines?'");
    272  tt_str_op(smartlist_get(process_data->stdout_data, 11), OP_EQ,
    273            "We are done for here, thank you!");
    274 
    275  /* Check stderr output. */
    276  tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
    277            "Output on stderr");
    278  tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
    279            "This is a new line");
    280  tt_str_op(smartlist_get(process_data->stderr_data, 2), OP_EQ,
    281            "Partial line on stderr ...end of partial line on stderr");
    282 
    283 done:
    284  process_data_free(process_data);
    285  process_free(process);
    286 }
    287 
    288 static void
    289 test_callbacks_terminate(void *arg)
    290 {
    291  (void)arg;
    292  const char *filename = NULL;
    293 
    294 #ifdef _WIN32
    295  filename = get_win32_test_binary_path();
    296 #else
    297  filename = TEST_PROCESS;
    298 #endif
    299 
    300  /* Process callback data. */
    301  process_data_t *process_data = process_data_new();
    302 
    303  /* Setup our process. */
    304  process_t *process = process_new(filename);
    305  process_set_data(process, process_data);
    306  process_set_exit_callback(process, process_exit_callback);
    307 
    308  /* Run our process. */
    309  process_status_t status;
    310 
    311  status = process_exec(process);
    312  tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
    313 
    314  /* Zap our process. */
    315  bool success;
    316 
    317  success = process_terminate(process);
    318  tt_assert(success);
    319 
    320  /* Start our main loop. */
    321  run_main_loop(process_data);
    322 
    323  /* Check if we did exit. */
    324  tt_assert(process_data->did_exit);
    325 
    326 done:
    327  process_data_free(process_data);
    328  process_free(process);
    329 }
    330 
    331 static void
    332 test_nonexistent_executable(void *arg)
    333 {
    334  (void)arg;
    335 
    336  /* Process callback data. */
    337  process_data_t *process_data = process_data_new();
    338 
    339  /* Setup our process. */
    340  process_t *process = process_new("binary-does-not-exist");
    341  process_set_data(process, process_data);
    342  process_set_exit_callback(process, process_exit_callback);
    343 
    344  /* Run our process. */
    345  process_exec(process);
    346 
    347  /* Start our main loop. */
    348  run_main_loop(process_data);
    349 
    350  /* Ensure that the exit callback was actually called even though the binary
    351   * did not exist.
    352   */
    353  tt_assert(process_data->did_exit);
    354 
    355 done:
    356  process_data_free(process_data);
    357  process_free(process);
    358 }
    359 
    360 struct testcase_t slow_process_tests[] = {
    361  { "callbacks", test_callbacks, 0, NULL, NULL },
    362  { "callbacks_terminate", test_callbacks_terminate, 0, NULL, NULL },
    363  { "nonexistent_executable", test_nonexistent_executable, 0, NULL, NULL },
    364  END_OF_TESTCASES
    365 };