tor

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

test_process.c (20274B)


      1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * \file test_process.c
      6 * \brief Test cases for the Process API.
      7 */
      8 
      9 #include "orconfig.h"
     10 #include "core/or/or.h"
     11 #include "test/test.h"
     12 #include "lib/process/env.h"
     13 
     14 #define PROCESS_PRIVATE
     15 #include "lib/process/process.h"
     16 #define PROCESS_UNIX_PRIVATE
     17 #include "lib/process/process_unix.h"
     18 #define PROCESS_WIN32_PRIVATE
     19 #include "lib/process/process_win32.h"
     20 
     21 static const char *stdout_read_buffer;
     22 static const char *stderr_read_buffer;
     23 
     24 struct process_data_t {
     25  smartlist_t *stdout_data;
     26  smartlist_t *stderr_data;
     27  smartlist_t *stdin_data;
     28  process_exit_code_t exit_code;
     29 };
     30 
     31 typedef struct process_data_t process_data_t;
     32 
     33 static process_data_t *
     34 process_data_new(void)
     35 {
     36  process_data_t *process_data = tor_malloc_zero(sizeof(process_data_t));
     37  process_data->stdout_data = smartlist_new();
     38  process_data->stderr_data = smartlist_new();
     39  process_data->stdin_data = smartlist_new();
     40  return process_data;
     41 }
     42 
     43 static void
     44 process_data_free(process_data_t *process_data)
     45 {
     46  if (process_data == NULL)
     47    return;
     48 
     49  SMARTLIST_FOREACH(process_data->stdout_data, char *, x, tor_free(x));
     50  SMARTLIST_FOREACH(process_data->stderr_data, char *, x, tor_free(x));
     51  SMARTLIST_FOREACH(process_data->stdin_data, char *, x, tor_free(x));
     52 
     53  smartlist_free(process_data->stdout_data);
     54  smartlist_free(process_data->stderr_data);
     55  smartlist_free(process_data->stdin_data);
     56  tor_free(process_data);
     57 }
     58 
     59 static int
     60 process_mocked_read_stdout(process_t *process, buf_t *buffer)
     61 {
     62  (void)process;
     63 
     64  if (stdout_read_buffer != NULL) {
     65    buf_add_string(buffer, stdout_read_buffer);
     66    stdout_read_buffer = NULL;
     67  }
     68 
     69  return (int)buf_datalen(buffer);
     70 }
     71 
     72 static int
     73 process_mocked_read_stderr(process_t *process, buf_t *buffer)
     74 {
     75  (void)process;
     76 
     77  if (stderr_read_buffer != NULL) {
     78    buf_add_string(buffer, stderr_read_buffer);
     79    stderr_read_buffer = NULL;
     80  }
     81 
     82  return (int)buf_datalen(buffer);
     83 }
     84 
     85 static void
     86 process_mocked_write_stdin(process_t *process, buf_t *buffer)
     87 {
     88  const size_t size = buf_datalen(buffer);
     89 
     90  if (size == 0)
     91    return;
     92 
     93  char *data = tor_malloc_zero(size + 1);
     94  process_data_t *process_data = process_get_data(process);
     95 
     96  buf_get_bytes(buffer, data, size);
     97  smartlist_add(process_data->stdin_data, data);
     98 }
     99 
    100 static void
    101 process_stdout_callback(process_t *process, const char *data, size_t size)
    102 {
    103  tt_ptr_op(process, OP_NE, NULL);
    104  tt_ptr_op(data, OP_NE, NULL);
    105  tt_int_op(strlen(data), OP_EQ, size);
    106 
    107  process_data_t *process_data = process_get_data(process);
    108  smartlist_add(process_data->stdout_data, tor_strdup(data));
    109 
    110 done:
    111  return;
    112 }
    113 
    114 static void
    115 process_stderr_callback(process_t *process, const char *data, size_t size)
    116 {
    117  tt_ptr_op(process, OP_NE, NULL);
    118  tt_ptr_op(data, OP_NE, NULL);
    119  tt_int_op(strlen(data), OP_EQ, size);
    120 
    121  process_data_t *process_data = process_get_data(process);
    122  smartlist_add(process_data->stderr_data, tor_strdup(data));
    123 
    124 done:
    125  return;
    126 }
    127 
    128 static bool
    129 process_exit_callback(process_t *process, process_exit_code_t exit_code)
    130 {
    131  tt_ptr_op(process, OP_NE, NULL);
    132 
    133  process_data_t *process_data = process_get_data(process);
    134  process_data->exit_code = exit_code;
    135 
    136 done:
    137  /* Do not free up our process_t. */
    138  return false;
    139 }
    140 
    141 static void
    142 test_default_values(void *arg)
    143 {
    144  (void)arg;
    145  process_t *process = process_new("/path/to/nothing");
    146 
    147  /* We are not running by default. */
    148  tt_int_op(PROCESS_STATUS_NOT_RUNNING, OP_EQ, process_get_status(process));
    149 
    150  /* We use the line protocol by default. */
    151  tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
    152 
    153  /* We don't set any custom data by default. */
    154  tt_ptr_op(NULL, OP_EQ, process_get_data(process));
    155 
    156  /* Our command was given to the process_t's constructor in process_new(). */
    157  tt_str_op("/path/to/nothing", OP_EQ, process_get_command(process));
    158 
    159  /* Make sure we are listed in the list of processes. */
    160  tt_assert(smartlist_contains(process_get_all_processes(),
    161                               process));
    162 
    163  /* Default PID is 0. */
    164  tt_u64_op(0, OP_EQ, process_get_pid(process));
    165 
    166  /* Our arguments should be empty. */
    167  tt_int_op(0, OP_EQ,
    168            smartlist_len(process_get_arguments(process)));
    169 
    170 done:
    171  process_free(process);
    172 }
    173 
    174 static void
    175 test_environment(void *arg)
    176 {
    177  (void)arg;
    178 
    179  process_t *process = process_new("");
    180  process_environment_t *env = NULL;
    181 
    182  process_set_environment(process, "E", "F");
    183  process_set_environment(process, "C", "D");
    184  process_set_environment(process, "A", "B");
    185 
    186  env = process_get_environment(process);
    187  tt_mem_op(env->windows_environment_block, OP_EQ,
    188            "A=B\0C=D\0E=F\0", 12);
    189  tt_str_op(env->unixoid_environment_block[0], OP_EQ,
    190            "A=B");
    191  tt_str_op(env->unixoid_environment_block[1], OP_EQ,
    192            "C=D");
    193  tt_str_op(env->unixoid_environment_block[2], OP_EQ,
    194            "E=F");
    195  tt_ptr_op(env->unixoid_environment_block[3], OP_EQ,
    196            NULL);
    197  process_environment_free(env);
    198 
    199  /* Reset our environment. */
    200  smartlist_t *new_env = smartlist_new();
    201  smartlist_add(new_env, (char *)"FOO=bar");
    202  smartlist_add(new_env, (char *)"HELLO=world");
    203 
    204  process_reset_environment(process, new_env);
    205  smartlist_free(new_env);
    206 
    207  env = process_get_environment(process);
    208  tt_mem_op(env->windows_environment_block, OP_EQ,
    209            "FOO=bar\0HELLO=world\0", 20);
    210  tt_str_op(env->unixoid_environment_block[0], OP_EQ,
    211            "FOO=bar");
    212  tt_str_op(env->unixoid_environment_block[1], OP_EQ,
    213            "HELLO=world");
    214  tt_ptr_op(env->unixoid_environment_block[2], OP_EQ,
    215            NULL);
    216 
    217 done:
    218  process_environment_free(env);
    219  process_free(process);
    220 }
    221 
    222 static void
    223 test_stringified_types(void *arg)
    224 {
    225  (void)arg;
    226 
    227  /* process_protocol_t values. */
    228  tt_str_op("Raw", OP_EQ, process_protocol_to_string(PROCESS_PROTOCOL_RAW));
    229  tt_str_op("Line", OP_EQ, process_protocol_to_string(PROCESS_PROTOCOL_LINE));
    230 
    231  /* process_status_t values. */
    232  tt_str_op("not running", OP_EQ,
    233            process_status_to_string(PROCESS_STATUS_NOT_RUNNING));
    234  tt_str_op("running", OP_EQ,
    235            process_status_to_string(PROCESS_STATUS_RUNNING));
    236  tt_str_op("error", OP_EQ,
    237            process_status_to_string(PROCESS_STATUS_ERROR));
    238 
    239 done:
    240  return;
    241 }
    242 
    243 static void
    244 test_line_protocol_simple(void *arg)
    245 {
    246  (void)arg;
    247 
    248  process_data_t *process_data = process_data_new();
    249 
    250  process_t *process = process_new("");
    251  process_set_data(process, process_data);
    252 
    253  process_set_stdout_read_callback(process, process_stdout_callback);
    254  process_set_stderr_read_callback(process, process_stderr_callback);
    255 
    256  MOCK(process_read_stdout, process_mocked_read_stdout);
    257  MOCK(process_read_stderr, process_mocked_read_stderr);
    258 
    259  /* Make sure we are running with the line protocol. */
    260  tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
    261 
    262  tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
    263  tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
    264 
    265  stdout_read_buffer = "Hello stdout\n";
    266  process_notify_event_stdout(process);
    267  tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
    268 
    269  stderr_read_buffer = "Hello stderr\r\n";
    270  process_notify_event_stderr(process);
    271  tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
    272 
    273  /* Data should be ready. */
    274  tt_int_op(1, OP_EQ, smartlist_len(process_data->stdout_data));
    275  tt_int_op(1, OP_EQ, smartlist_len(process_data->stderr_data));
    276 
    277  /* Check if the data is correct. */
    278  tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
    279            "Hello stdout");
    280  tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
    281            "Hello stderr");
    282 
    283 done:
    284  process_data_free(process_data);
    285  process_free(process);
    286 
    287  UNMOCK(process_read_stdout);
    288  UNMOCK(process_read_stderr);
    289 }
    290 
    291 static void
    292 test_line_protocol_multi(void *arg)
    293 {
    294  (void)arg;
    295 
    296  process_data_t *process_data = process_data_new();
    297 
    298  process_t *process = process_new("");
    299  process_set_data(process, process_data);
    300  process_set_stdout_read_callback(process, process_stdout_callback);
    301  process_set_stderr_read_callback(process, process_stderr_callback);
    302 
    303  MOCK(process_read_stdout, process_mocked_read_stdout);
    304  MOCK(process_read_stderr, process_mocked_read_stderr);
    305 
    306  /* Make sure we are running with the line protocol. */
    307  tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
    308 
    309  tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
    310  tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
    311 
    312  stdout_read_buffer = "Hello stdout\r\nOnion Onion Onion\nA B C D\r\n\r\n";
    313  process_notify_event_stdout(process);
    314  tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
    315 
    316  stderr_read_buffer = "Hello stderr\nFoo bar baz\nOnion Onion Onion\n";
    317  process_notify_event_stderr(process);
    318  tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
    319 
    320  /* Data should be ready. */
    321  tt_int_op(4, OP_EQ, smartlist_len(process_data->stdout_data));
    322  tt_int_op(3, OP_EQ, smartlist_len(process_data->stderr_data));
    323 
    324  /* Check if the data is correct. */
    325  tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
    326            "Hello stdout");
    327  tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
    328            "Onion Onion Onion");
    329  tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
    330            "A B C D");
    331  tt_str_op(smartlist_get(process_data->stdout_data, 3), OP_EQ,
    332            "");
    333 
    334  tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
    335            "Hello stderr");
    336  tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
    337            "Foo bar baz");
    338  tt_str_op(smartlist_get(process_data->stderr_data, 2), OP_EQ,
    339            "Onion Onion Onion");
    340 
    341 done:
    342  process_data_free(process_data);
    343  process_free(process);
    344 
    345  UNMOCK(process_read_stdout);
    346  UNMOCK(process_read_stderr);
    347 }
    348 
    349 static void
    350 test_line_protocol_partial(void *arg)
    351 {
    352  (void)arg;
    353 
    354  process_data_t *process_data = process_data_new();
    355 
    356  process_t *process = process_new("");
    357  process_set_data(process, process_data);
    358  process_set_stdout_read_callback(process, process_stdout_callback);
    359  process_set_stderr_read_callback(process, process_stderr_callback);
    360 
    361  MOCK(process_read_stdout, process_mocked_read_stdout);
    362  MOCK(process_read_stderr, process_mocked_read_stderr);
    363 
    364  /* Make sure we are running with the line protocol. */
    365  tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
    366 
    367  tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
    368  tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
    369 
    370  stdout_read_buffer = "Hello stdout this is a partial line ...";
    371  process_notify_event_stdout(process);
    372  tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
    373 
    374  stderr_read_buffer = "Hello stderr this is a partial line ...";
    375  process_notify_event_stderr(process);
    376  tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
    377 
    378  /* Data should NOT be ready. */
    379  tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
    380  tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
    381 
    382  stdout_read_buffer = " the end\nAnother partial string goes here ...";
    383  process_notify_event_stdout(process);
    384  tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
    385 
    386  stderr_read_buffer = " the end\nAnother partial string goes here ...";
    387  process_notify_event_stderr(process);
    388  tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
    389 
    390  /* Some data should be ready. */
    391  tt_int_op(1, OP_EQ, smartlist_len(process_data->stdout_data));
    392  tt_int_op(1, OP_EQ, smartlist_len(process_data->stderr_data));
    393 
    394  stdout_read_buffer = " the end\nFoo bar baz\n";
    395  process_notify_event_stdout(process);
    396  tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
    397 
    398  stderr_read_buffer = " the end\nFoo bar baz\n";
    399  process_notify_event_stderr(process);
    400  tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
    401 
    402  /* Some data should be ready. */
    403  tt_int_op(3, OP_EQ, smartlist_len(process_data->stdout_data));
    404  tt_int_op(3, OP_EQ, smartlist_len(process_data->stderr_data));
    405 
    406  /* Check if the data is correct. */
    407  tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
    408                          "Hello stdout this is a partial line ... the end");
    409  tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
    410                          "Another partial string goes here ... the end");
    411  tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
    412                          "Foo bar baz");
    413 
    414  tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
    415                          "Hello stderr this is a partial line ... the end");
    416  tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
    417                          "Another partial string goes here ... the end");
    418  tt_str_op(smartlist_get(process_data->stderr_data, 2), OP_EQ,
    419                          "Foo bar baz");
    420 
    421 done:
    422  process_data_free(process_data);
    423  process_free(process);
    424 
    425  UNMOCK(process_read_stdout);
    426  UNMOCK(process_read_stderr);
    427 }
    428 
    429 static void
    430 test_raw_protocol_simple(void *arg)
    431 {
    432  (void)arg;
    433 
    434  process_data_t *process_data = process_data_new();
    435 
    436  process_t *process = process_new("");
    437  process_set_data(process, process_data);
    438  process_set_protocol(process, PROCESS_PROTOCOL_RAW);
    439 
    440  process_set_stdout_read_callback(process, process_stdout_callback);
    441  process_set_stderr_read_callback(process, process_stderr_callback);
    442 
    443  MOCK(process_read_stdout, process_mocked_read_stdout);
    444  MOCK(process_read_stderr, process_mocked_read_stderr);
    445 
    446  /* Make sure we are running with the raw protocol. */
    447  tt_int_op(PROCESS_PROTOCOL_RAW, OP_EQ, process_get_protocol(process));
    448 
    449  tt_int_op(0, OP_EQ, smartlist_len(process_data->stdout_data));
    450  tt_int_op(0, OP_EQ, smartlist_len(process_data->stderr_data));
    451 
    452  stdout_read_buffer = "Hello stdout\n";
    453  process_notify_event_stdout(process);
    454  tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
    455 
    456  stderr_read_buffer = "Hello stderr\n";
    457  process_notify_event_stderr(process);
    458  tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
    459 
    460  /* Data should be ready. */
    461  tt_int_op(1, OP_EQ, smartlist_len(process_data->stdout_data));
    462  tt_int_op(1, OP_EQ, smartlist_len(process_data->stderr_data));
    463 
    464  stdout_read_buffer = "Hello, again, stdout\nThis contains multiple lines";
    465  process_notify_event_stdout(process);
    466  tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
    467 
    468  stderr_read_buffer = "Hello, again, stderr\nThis contains multiple lines";
    469  process_notify_event_stderr(process);
    470  tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
    471 
    472  /* Data should be ready. */
    473  tt_int_op(2, OP_EQ, smartlist_len(process_data->stdout_data));
    474  tt_int_op(2, OP_EQ, smartlist_len(process_data->stderr_data));
    475 
    476  /* Check if the data is correct. */
    477  tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
    478            "Hello stdout\n");
    479  tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
    480            "Hello, again, stdout\nThis contains multiple lines");
    481 
    482  tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
    483            "Hello stderr\n");
    484  tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
    485            "Hello, again, stderr\nThis contains multiple lines");
    486 
    487 done:
    488  process_data_free(process_data);
    489  process_free(process);
    490 
    491  UNMOCK(process_read_stdout);
    492  UNMOCK(process_read_stderr);
    493 }
    494 
    495 static void
    496 test_write_simple(void *arg)
    497 {
    498  (void)arg;
    499 
    500  process_data_t *process_data = process_data_new();
    501 
    502  process_t *process = process_new("");
    503  process_set_data(process, process_data);
    504 
    505  MOCK(process_write_stdin, process_mocked_write_stdin);
    506 
    507  process_write(process, (uint8_t *)"Hello world\n", 12);
    508  process_notify_event_stdin(process);
    509  tt_int_op(1, OP_EQ, smartlist_len(process_data->stdin_data));
    510 
    511  process_printf(process, "Hello %s !\n", "moon");
    512  process_notify_event_stdin(process);
    513  tt_int_op(2, OP_EQ, smartlist_len(process_data->stdin_data));
    514 
    515 done:
    516  process_data_free(process_data);
    517  process_free(process);
    518 
    519  UNMOCK(process_write_stdin);
    520 }
    521 
    522 static void
    523 test_exit_simple(void *arg)
    524 {
    525  (void)arg;
    526 
    527  process_data_t *process_data = process_data_new();
    528 
    529  process_t *process = process_new("");
    530  process_set_data(process, process_data);
    531  process_set_exit_callback(process, process_exit_callback);
    532 
    533  /* Our default is 0. */
    534  tt_u64_op(0, OP_EQ, process_data->exit_code);
    535 
    536  /* Fake that we are a running process. */
    537  process_set_status(process, PROCESS_STATUS_RUNNING);
    538  tt_int_op(process_get_status(process), OP_EQ, PROCESS_STATUS_RUNNING);
    539 
    540  /* Fake an exit. */
    541  process_notify_event_exit(process, 1337);
    542 
    543  /* Check if our state changed and if our callback fired. */
    544  tt_int_op(process_get_status(process), OP_EQ, PROCESS_STATUS_NOT_RUNNING);
    545  tt_u64_op(1337, OP_EQ, process_data->exit_code);
    546 
    547 done:
    548  process_set_data(process, process_data);
    549  process_data_free(process_data);
    550  process_free(process);
    551 }
    552 
    553 static void
    554 test_argv_simple(void *arg)
    555 {
    556  (void)arg;
    557 
    558  process_t *process = process_new("/bin/cat");
    559  char **argv = NULL;
    560 
    561  /* Setup some arguments. */
    562  process_append_argument(process, "foo");
    563  process_append_argument(process, "bar");
    564  process_append_argument(process, "baz");
    565 
    566  /* Check the number of elements. */
    567  tt_int_op(3, OP_EQ,
    568            smartlist_len(process_get_arguments(process)));
    569 
    570  /* Let's try to convert it into a Unix style char **argv. */
    571  argv = process_get_argv(process);
    572 
    573  /* Check our values. */
    574  tt_str_op(argv[0], OP_EQ, "/bin/cat");
    575  tt_str_op(argv[1], OP_EQ, "foo");
    576  tt_str_op(argv[2], OP_EQ, "bar");
    577  tt_str_op(argv[3], OP_EQ, "baz");
    578  tt_ptr_op(argv[4], OP_EQ, NULL);
    579 
    580 done:
    581  tor_free(argv);
    582  process_free(process);
    583 }
    584 
    585 static void
    586 test_unix(void *arg)
    587 {
    588  (void)arg;
    589 #ifndef _WIN32
    590  process_t *process = process_new("");
    591 
    592  /* On Unix all processes should have a Unix process handle. */
    593  tt_ptr_op(NULL, OP_NE, process_get_unix_process(process));
    594 
    595 done:
    596  process_free(process);
    597 #endif /* !defined(_WIN32) */
    598 }
    599 
    600 static void
    601 test_win32(void *arg)
    602 {
    603  (void)arg;
    604 #ifdef _WIN32
    605  process_t *process = process_new("");
    606  char *joined_argv = NULL;
    607 
    608  /* On Win32 all processes should have a Win32 process handle. */
    609  tt_ptr_op(NULL, OP_NE, process_get_win32_process(process));
    610 
    611  /* Based on some test cases from "Parsing C++ Command-Line Arguments" in
    612   * MSDN but we don't exercise all quoting rules because tor_join_win_cmdline
    613   * will try to only generate simple cases for the child process to parse;
    614   * i.e. we never embed quoted strings in arguments. */
    615 
    616  const char *argvs[][4] = {
    617    {"a", "bb", "CCC", NULL}, // Normal
    618    {NULL, NULL, NULL, NULL}, // Empty argument list
    619    {"", NULL, NULL, NULL}, // Empty argument
    620    {"\"a", "b\"b", "CCC\"", NULL}, // Quotes
    621    {"a\tbc", "dd  dd", "E", NULL}, // Whitespace
    622    {"a\\\\\\b", "de fg", "H", NULL}, // Backslashes
    623    {"a\\\"b", "\\c", "D\\", NULL}, // Backslashes before quote
    624    {"a\\\\b c", "d", "E", NULL}, // Backslashes not before quote
    625    { NULL } // Terminator
    626  };
    627 
    628  const char *cmdlines[] = {
    629    "a bb CCC",
    630    "",
    631    "\"\"",
    632    "\\\"a b\\\"b CCC\\\"",
    633    "\"a\tbc\" \"dd  dd\" E",
    634    "a\\\\\\b \"de fg\" H",
    635    "a\\\\\\\"b \\c D\\",
    636    "\"a\\\\b c\" d E",
    637    NULL // Terminator
    638  };
    639 
    640  int i;
    641 
    642  for (i=0; cmdlines[i]!=NULL; i++) {
    643    log_info(LD_GENERAL, "Joining argvs[%d], expecting <%s>", i, cmdlines[i]);
    644    joined_argv = tor_join_win_cmdline(argvs[i]);
    645    tt_str_op(cmdlines[i],OP_EQ, joined_argv);
    646    tor_free(joined_argv);
    647  }
    648 
    649 done:
    650  tor_free(joined_argv);
    651  process_free(process);
    652 #endif /* defined(_WIN32) */
    653 }
    654 
    655 struct testcase_t process_tests[] = {
    656  { "default_values", test_default_values, TT_FORK, NULL, NULL },
    657  { "environment", test_environment, TT_FORK, NULL, NULL },
    658  { "stringified_types", test_stringified_types, TT_FORK, NULL, NULL },
    659  { "line_protocol_simple", test_line_protocol_simple, TT_FORK, NULL, NULL },
    660  { "line_protocol_multi", test_line_protocol_multi, TT_FORK, NULL, NULL },
    661  { "line_protocol_partial", test_line_protocol_partial, TT_FORK, NULL, NULL },
    662  { "raw_protocol_simple", test_raw_protocol_simple, TT_FORK, NULL, NULL },
    663  { "write_simple", test_write_simple, TT_FORK, NULL, NULL },
    664  { "exit_simple", test_exit_simple, TT_FORK, NULL, NULL },
    665  { "argv_simple", test_argv_simple, TT_FORK, NULL, NULL },
    666  { "unix", test_unix, TT_FORK, NULL, NULL },
    667  { "win32", test_win32, TT_FORK, NULL, NULL },
    668  END_OF_TESTCASES
    669 };