Bump copyright date to 2019
[tor.git] / src / test / test_process.c
blob7cc01d24427e2e506137869f557d3ccfafce1840
1 /* Copyright (c) 2018-2019, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file test_process.c
6 * \brief Test cases for the Process API.
7 */
9 #include "orconfig.h"
10 #include "core/or/or.h"
11 #include "test/test.h"
12 #include "lib/process/env.h"
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"
21 static const char *stdout_read_buffer;
22 static const char *stderr_read_buffer;
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;
31 typedef struct process_data_t process_data_t;
33 static process_data_t *
34 process_data_new(void)
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;
43 static void
44 process_data_free(process_data_t *process_data)
46 if (process_data == NULL)
47 return;
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));
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);
59 static int
60 process_mocked_read_stdout(process_t *process, buf_t *buffer)
62 (void)process;
64 if (stdout_read_buffer != NULL) {
65 buf_add_string(buffer, stdout_read_buffer);
66 stdout_read_buffer = NULL;
69 return (int)buf_datalen(buffer);
72 static int
73 process_mocked_read_stderr(process_t *process, buf_t *buffer)
75 (void)process;
77 if (stderr_read_buffer != NULL) {
78 buf_add_string(buffer, stderr_read_buffer);
79 stderr_read_buffer = NULL;
82 return (int)buf_datalen(buffer);
85 static void
86 process_mocked_write_stdin(process_t *process, buf_t *buffer)
88 const size_t size = buf_datalen(buffer);
90 if (size == 0)
91 return;
93 char *data = tor_malloc_zero(size + 1);
94 process_data_t *process_data = process_get_data(process);
96 buf_get_bytes(buffer, data, size);
97 smartlist_add(process_data->stdin_data, data);
100 static void
101 process_stdout_callback(process_t *process, const char *data, size_t size)
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);
107 process_data_t *process_data = process_get_data(process);
108 smartlist_add(process_data->stdout_data, tor_strdup(data));
110 done:
111 return;
114 static void
115 process_stderr_callback(process_t *process, const char *data, size_t size)
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);
121 process_data_t *process_data = process_get_data(process);
122 smartlist_add(process_data->stderr_data, tor_strdup(data));
124 done:
125 return;
128 static bool
129 process_exit_callback(process_t *process, process_exit_code_t exit_code)
131 tt_ptr_op(process, OP_NE, NULL);
133 process_data_t *process_data = process_get_data(process);
134 process_data->exit_code = exit_code;
136 done:
137 /* Do not free up our process_t. */
138 return false;
141 static void
142 test_default_values(void *arg)
144 (void)arg;
145 process_t *process = process_new("/path/to/nothing");
147 /* We are not running by default. */
148 tt_int_op(PROCESS_STATUS_NOT_RUNNING, OP_EQ, process_get_status(process));
150 /* We use the line protocol by default. */
151 tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
153 /* We don't set any custom data by default. */
154 tt_ptr_op(NULL, OP_EQ, process_get_data(process));
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));
159 /* Make sure we are listed in the list of proccesses. */
160 tt_assert(smartlist_contains(process_get_all_processes(),
161 process));
163 /* Default PID is 0. */
164 tt_u64_op(0, OP_EQ, process_get_pid(process));
166 /* Our arguments should be empty. */
167 tt_int_op(0, OP_EQ,
168 smartlist_len(process_get_arguments(process)));
170 done:
171 process_free(process);
174 static void
175 test_environment(void *arg)
177 (void)arg;
179 process_t *process = process_new("");
180 process_environment_t *env = NULL;
182 process_set_environment(process, "E", "F");
183 process_set_environment(process, "C", "D");
184 process_set_environment(process, "A", "B");
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);
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");
204 process_reset_environment(process, new_env);
205 smartlist_free(new_env);
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);
217 done:
218 process_environment_free(env);
219 process_free(process);
222 static void
223 test_stringified_types(void *arg)
225 (void)arg;
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));
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));
239 done:
240 return;
243 static void
244 test_line_protocol_simple(void *arg)
246 (void)arg;
248 process_data_t *process_data = process_data_new();
250 process_t *process = process_new("");
251 process_set_data(process, process_data);
253 process_set_stdout_read_callback(process, process_stdout_callback);
254 process_set_stderr_read_callback(process, process_stderr_callback);
256 MOCK(process_read_stdout, process_mocked_read_stdout);
257 MOCK(process_read_stderr, process_mocked_read_stderr);
259 /* Make sure we are running with the line protocol. */
260 tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
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));
265 stdout_read_buffer = "Hello stdout\n";
266 process_notify_event_stdout(process);
267 tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
269 stderr_read_buffer = "Hello stderr\r\n";
270 process_notify_event_stderr(process);
271 tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
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));
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");
283 done:
284 process_data_free(process_data);
285 process_free(process);
287 UNMOCK(process_read_stdout);
288 UNMOCK(process_read_stderr);
291 static void
292 test_line_protocol_multi(void *arg)
294 (void)arg;
296 process_data_t *process_data = process_data_new();
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);
303 MOCK(process_read_stdout, process_mocked_read_stdout);
304 MOCK(process_read_stderr, process_mocked_read_stderr);
306 /* Make sure we are running with the line protocol. */
307 tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
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));
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);
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);
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));
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 "");
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");
341 done:
342 process_data_free(process_data);
343 process_free(process);
345 UNMOCK(process_read_stdout);
346 UNMOCK(process_read_stderr);
349 static void
350 test_line_protocol_partial(void *arg)
352 (void)arg;
354 process_data_t *process_data = process_data_new();
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);
361 MOCK(process_read_stdout, process_mocked_read_stdout);
362 MOCK(process_read_stderr, process_mocked_read_stderr);
364 /* Make sure we are running with the line protocol. */
365 tt_int_op(PROCESS_PROTOCOL_LINE, OP_EQ, process_get_protocol(process));
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));
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);
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);
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));
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);
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);
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));
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);
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);
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));
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");
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");
421 done:
422 process_data_free(process_data);
423 process_free(process);
425 UNMOCK(process_read_stdout);
426 UNMOCK(process_read_stderr);
429 static void
430 test_raw_protocol_simple(void *arg)
432 (void)arg;
434 process_data_t *process_data = process_data_new();
436 process_t *process = process_new("");
437 process_set_data(process, process_data);
438 process_set_protocol(process, PROCESS_PROTOCOL_RAW);
440 process_set_stdout_read_callback(process, process_stdout_callback);
441 process_set_stderr_read_callback(process, process_stderr_callback);
443 MOCK(process_read_stdout, process_mocked_read_stdout);
444 MOCK(process_read_stderr, process_mocked_read_stderr);
446 /* Make sure we are running with the raw protocol. */
447 tt_int_op(PROCESS_PROTOCOL_RAW, OP_EQ, process_get_protocol(process));
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));
452 stdout_read_buffer = "Hello stdout\n";
453 process_notify_event_stdout(process);
454 tt_ptr_op(NULL, OP_EQ, stdout_read_buffer);
456 stderr_read_buffer = "Hello stderr\n";
457 process_notify_event_stderr(process);
458 tt_ptr_op(NULL, OP_EQ, stderr_read_buffer);
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));
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);
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);
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));
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");
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");
487 done:
488 process_data_free(process_data);
489 process_free(process);
491 UNMOCK(process_read_stdout);
492 UNMOCK(process_read_stderr);
495 static void
496 test_write_simple(void *arg)
498 (void)arg;
500 process_data_t *process_data = process_data_new();
502 process_t *process = process_new("");
503 process_set_data(process, process_data);
505 MOCK(process_write_stdin, process_mocked_write_stdin);
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));
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));
515 done:
516 process_data_free(process_data);
517 process_free(process);
519 UNMOCK(process_write_stdin);
522 static void
523 test_exit_simple(void *arg)
525 (void)arg;
527 process_data_t *process_data = process_data_new();
529 process_t *process = process_new("");
530 process_set_data(process, process_data);
531 process_set_exit_callback(process, process_exit_callback);
533 /* Our default is 0. */
534 tt_u64_op(0, OP_EQ, process_data->exit_code);
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);
540 /* Fake an exit. */
541 process_notify_event_exit(process, 1337);
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);
547 done:
548 process_set_data(process, process_data);
549 process_data_free(process_data);
550 process_free(process);
553 static void
554 test_argv_simple(void *arg)
556 (void)arg;
558 process_t *process = process_new("/bin/cat");
559 char **argv = NULL;
561 /* Setup some arguments. */
562 process_append_argument(process, "foo");
563 process_append_argument(process, "bar");
564 process_append_argument(process, "baz");
566 /* Check the number of elements. */
567 tt_int_op(3, OP_EQ,
568 smartlist_len(process_get_arguments(process)));
570 /* Let's try to convert it into a Unix style char **argv. */
571 argv = process_get_argv(process);
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);
580 done:
581 tor_free(argv);
582 process_free(process);
585 static void
586 test_unix(void *arg)
588 (void)arg;
589 #ifndef _WIN32
590 process_t *process = process_new("");
592 /* On Unix all processes should have a Unix process handle. */
593 tt_ptr_op(NULL, OP_NE, process_get_unix_process(process));
595 done:
596 process_free(process);
597 #endif
600 static void
601 test_win32(void *arg)
603 (void)arg;
604 #ifdef _WIN32
605 process_t *process = process_new("");
606 char *joined_argv = NULL;
608 /* On Win32 all processes should have a Win32 process handle. */
609 tt_ptr_op(NULL, OP_NE, process_get_win32_process(process));
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. */
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
628 const char *cmdlines[] = {
629 "a bb CCC",
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
640 int i;
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);
649 done:
650 tor_free(joined_argv);
651 process_free(process);
652 #endif
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