1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
5 * \file test_process_slow.c
6 * \brief Slow test cases for the Process API.
9 #define MAINLOOP_PRIVATE
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"
23 #define TEST_PROCESS "test-process.exe"
25 #define TEST_PROCESS BUILDDIR "/src/test/test-process"
26 #endif /* defined(_WIN32) */
28 /** Timer that ticks once a second and stop the event loop after 5 ticks. */
29 static periodic_timer_t
*main_loop_timeout_timer
;
31 /** How many times have our timer ticked? */
32 static int timer_tick_count
;
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
;
42 typedef struct process_data_t process_data_t
;
44 static process_data_t
*
45 process_data_new(void)
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();
55 process_data_free(process_data_t
*process_data
)
57 if (process_data
== NULL
)
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
));
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
);
71 process_stdout_callback(process_t
*process
, const char *data
, size_t size
)
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
);
77 process_data_t
*process_data
= process_get_data(process
);
78 smartlist_add(process_data
->stdout_data
, tor_strdup(data
));
85 process_stderr_callback(process_t
*process
, const char *data
, size_t size
)
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
);
91 process_data_t
*process_data
= process_get_data(process
);
92 smartlist_add(process_data
->stderr_data
, tor_strdup(data
));
99 process_exit_callback(process_t
*process
, process_exit_code_t exit_code
)
101 process_status_t status
;
103 tt_ptr_op(process
, OP_NE
, NULL
);
105 process_data_t
*process_data
= process_get_data(process
);
106 process_data
->exit_code
= exit_code
;
107 process_data
->did_exit
= true;
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
);
114 /* Do not free up our process_t. */
120 get_win32_test_binary_path(void)
122 static char buffer
[MAX_PATH
];
124 /* Get the absolute path of our binary: \path\to\test-slow.exe. */
125 GetModuleFileNameA(GetModuleHandle(0), buffer
, sizeof(buffer
));
127 /* Find our process name. */
128 char *offset
= strstr(buffer
, "test-slow.exe");
129 tt_ptr_op(offset
, OP_NE
, NULL
);
131 /* Change test-slow.exe to test-process.exe. */
132 memcpy(offset
, TEST_PROCESS
, strlen(TEST_PROCESS
));
138 #endif /* defined(_WIN32) */
141 main_loop_timeout_cb(periodic_timer_t
*timer
, void *data
)
144 tt_ptr_op(timer
, OP_EQ
, main_loop_timeout_timer
);
145 tt_ptr_op(data
, OP_NE
, NULL
);
147 /* Our process data. */
148 process_data_t
*process_data
= data
;
150 /* Our process did exit. */
151 if (process_data
->did_exit
)
152 tor_shutdown_event_loop_and_exit(0);
154 /* Have we been called 10 times we exit the main loop. */
157 tt_int_op(timer_tick_count
, OP_LT
, 10);
160 /* Call waitpid callbacks. */
161 notify_pending_waitpid_callbacks();
166 /* Exit with an error. */
167 tor_shutdown_event_loop_and_exit(-1);
171 run_main_loop(process_data_t
*process_data
)
175 /* Wake up after 1 seconds. */
176 static const struct timeval interval
= {1, 0};
178 timer_tick_count
= 0;
179 main_loop_timeout_timer
= periodic_timer_new(tor_libevent_get_base(),
181 main_loop_timeout_cb
,
184 /* Run our main loop. */
185 ret
= run_main_loop_until_done();
187 /* Clean up our main loop timeout timer. */
188 tt_int_op(ret
, OP_EQ
, 0);
191 periodic_timer_free(main_loop_timeout_timer
);
195 test_callbacks(void *arg
)
198 const char *filename
= NULL
;
201 filename
= get_win32_test_binary_path();
203 filename
= TEST_PROCESS
;
206 /* Process callback data. */
207 process_data_t
*process_data
= process_data_new();
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
);
216 /* Set environment variable. */
217 process_set_environment(process
, "TOR_TEST_ENV", "Hello, from Tor!");
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");
224 /* Run our process. */
225 process_status_t status
;
227 status
= process_exec(process
);
228 tt_int_op(status
, OP_EQ
, PROCESS_STATUS_RUNNING
);
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");
236 /* Start our main loop. */
237 run_main_loop(process_data
);
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);
245 /* Check stdout output. */
246 char argv0_expected
[256];
247 tor_snprintf(argv0_expected
, sizeof(argv0_expected
),
248 "argv[0] = '%s'", filename
);
250 tt_str_op(smartlist_get(process_data
->stdout_data
, 0), OP_EQ
,
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
,
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!");
275 /* Check stderr output. */
276 tt_str_op(smartlist_get(process_data
->stderr_data
, 0), OP_EQ
,
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");
284 process_data_free(process_data
);
285 process_free(process
);
289 test_callbacks_terminate(void *arg
)
292 const char *filename
= NULL
;
295 filename
= get_win32_test_binary_path();
297 filename
= TEST_PROCESS
;
300 /* Process callback data. */
301 process_data_t
*process_data
= process_data_new();
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
);
308 /* Run our process. */
309 process_status_t status
;
311 status
= process_exec(process
);
312 tt_int_op(status
, OP_EQ
, PROCESS_STATUS_RUNNING
);
314 /* Zap our process. */
317 success
= process_terminate(process
);
320 /* Start our main loop. */
321 run_main_loop(process_data
);
323 /* Check if we did exit. */
324 tt_assert(process_data
->did_exit
);
327 process_data_free(process_data
);
328 process_free(process
);
332 test_nonexistent_executable(void *arg
)
336 /* Process callback data. */
337 process_data_t
*process_data
= process_data_new();
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
);
344 /* Run our process. */
345 process_exec(process
);
347 /* Start our main loop. */
348 run_main_loop(process_data
);
350 /* Ensure that the exit callback was actually called even though the binary
353 tt_assert(process_data
->did_exit
);
356 process_data_free(process_data
);
357 process_free(process
);
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
},