Merge branch 'maint-0.4.8' into release-0.4.8
[tor.git] / src / test / test_process_slow.c
blob6cb1b01b503e0ee187b70218c09e521e0b91f3b2
1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file test_process_slow.c
6 * \brief Slow test cases for the Process API.
7 */
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"
18 #ifndef BUILDDIR
19 #define BUILDDIR "."
20 #endif
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) */
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;
39 bool did_exit;
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();
51 return process_data;
54 static void
55 process_data_free(process_data_t *process_data)
57 if (process_data == NULL)
58 return;
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);
70 static void
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));
80 done:
81 return;
84 static void
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));
94 done:
95 return;
98 static bool
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);
113 done:
114 /* Do not free up our process_t. */
115 return false;
118 #ifdef _WIN32
119 static const char *
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));
134 return buffer;
135 done:
136 return NULL;
138 #endif /* defined(_WIN32) */
140 static void
141 main_loop_timeout_cb(periodic_timer_t *timer, void *data)
143 /* Sanity check. */
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. */
155 timer_tick_count++;
157 tt_int_op(timer_tick_count, OP_LT, 10);
159 #ifndef _WIN32
160 /* Call waitpid callbacks. */
161 notify_pending_waitpid_callbacks();
162 #endif
164 return;
165 done:
166 /* Exit with an error. */
167 tor_shutdown_event_loop_and_exit(-1);
170 static void
171 run_main_loop(process_data_t *process_data)
173 int ret;
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(),
180 &interval,
181 main_loop_timeout_cb,
182 process_data);
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);
190 done:
191 periodic_timer_free(main_loop_timeout_timer);
194 static void
195 test_callbacks(void *arg)
197 (void)arg;
198 const char *filename = NULL;
200 #ifdef _WIN32
201 filename = get_win32_test_binary_path();
202 #else
203 filename = TEST_PROCESS;
204 #endif
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,
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!");
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");
283 done:
284 process_data_free(process_data);
285 process_free(process);
288 static void
289 test_callbacks_terminate(void *arg)
291 (void)arg;
292 const char *filename = NULL;
294 #ifdef _WIN32
295 filename = get_win32_test_binary_path();
296 #else
297 filename = TEST_PROCESS;
298 #endif
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. */
315 bool success;
317 success = process_terminate(process);
318 tt_assert(success);
320 /* Start our main loop. */
321 run_main_loop(process_data);
323 /* Check if we did exit. */
324 tt_assert(process_data->did_exit);
326 done:
327 process_data_free(process_data);
328 process_free(process);
331 static void
332 test_nonexistent_executable(void *arg)
334 (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
351 * did not exist.
353 tt_assert(process_data->did_exit);
355 done:
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 },
364 END_OF_TESTCASES