Line data Source code
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 3 : process_data_new(void)
46 : {
47 3 : process_data_t *process_data = tor_malloc_zero(sizeof(process_data_t));
48 3 : process_data->stdout_data = smartlist_new();
49 3 : process_data->stderr_data = smartlist_new();
50 3 : process_data->stdin_data = smartlist_new();
51 3 : return process_data;
52 : }
53 :
54 : static void
55 3 : process_data_free(process_data_t *process_data)
56 : {
57 3 : if (process_data == NULL)
58 : return;
59 :
60 15 : SMARTLIST_FOREACH(process_data->stdout_data, char *, x, tor_free(x));
61 6 : SMARTLIST_FOREACH(process_data->stderr_data, char *, x, tor_free(x));
62 3 : SMARTLIST_FOREACH(process_data->stdin_data, char *, x, tor_free(x));
63 :
64 3 : smartlist_free(process_data->stdout_data);
65 3 : smartlist_free(process_data->stderr_data);
66 3 : smartlist_free(process_data->stdin_data);
67 3 : tor_free(process_data);
68 : }
69 :
70 : static void
71 12 : process_stdout_callback(process_t *process, const char *data, size_t size)
72 : {
73 12 : tt_ptr_op(process, OP_NE, NULL);
74 12 : tt_ptr_op(data, OP_NE, NULL);
75 12 : tt_int_op(strlen(data), OP_EQ, size);
76 :
77 12 : process_data_t *process_data = process_get_data(process);
78 12 : smartlist_add(process_data->stdout_data, tor_strdup(data));
79 :
80 12 : done:
81 12 : return;
82 : }
83 :
84 : static void
85 3 : process_stderr_callback(process_t *process, const char *data, size_t size)
86 : {
87 3 : tt_ptr_op(process, OP_NE, NULL);
88 3 : tt_ptr_op(data, OP_NE, NULL);
89 3 : tt_int_op(strlen(data), OP_EQ, size);
90 :
91 3 : process_data_t *process_data = process_get_data(process);
92 3 : smartlist_add(process_data->stderr_data, tor_strdup(data));
93 :
94 3 : done:
95 3 : return;
96 : }
97 :
98 : static bool
99 3 : process_exit_callback(process_t *process, process_exit_code_t exit_code)
100 : {
101 3 : process_status_t status;
102 :
103 3 : tt_ptr_op(process, OP_NE, NULL);
104 :
105 3 : process_data_t *process_data = process_get_data(process);
106 3 : process_data->exit_code = exit_code;
107 3 : process_data->did_exit = true;
108 :
109 : /* Check if our process is still running? */
110 3 : status = process_get_status(process);
111 3 : tt_int_op(status, OP_EQ, PROCESS_STATUS_NOT_RUNNING);
112 :
113 3 : done:
114 : /* Do not free up our process_t. */
115 3 : 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 8 : main_loop_timeout_cb(periodic_timer_t *timer, void *data)
142 : {
143 : /* Sanity check. */
144 8 : tt_ptr_op(timer, OP_EQ, main_loop_timeout_timer);
145 8 : tt_ptr_op(data, OP_NE, NULL);
146 :
147 : /* Our process data. */
148 8 : process_data_t *process_data = data;
149 :
150 : /* Our process did exit. */
151 8 : if (process_data->did_exit)
152 3 : tor_shutdown_event_loop_and_exit(0);
153 :
154 : /* Have we been called 10 times we exit the main loop. */
155 8 : timer_tick_count++;
156 :
157 8 : tt_int_op(timer_tick_count, OP_LT, 10);
158 :
159 : #ifndef _WIN32
160 : /* Call waitpid callbacks. */
161 8 : notify_pending_waitpid_callbacks();
162 : #endif
163 :
164 8 : return;
165 0 : done:
166 : /* Exit with an error. */
167 0 : tor_shutdown_event_loop_and_exit(-1);
168 : }
169 :
170 : static void
171 3 : run_main_loop(process_data_t *process_data)
172 : {
173 3 : int ret;
174 :
175 : /* Wake up after 1 seconds. */
176 3 : static const struct timeval interval = {1, 0};
177 :
178 3 : timer_tick_count = 0;
179 3 : 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 3 : ret = run_main_loop_until_done();
186 :
187 : /* Clean up our main loop timeout timer. */
188 3 : tt_int_op(ret, OP_EQ, 0);
189 :
190 3 : done:
191 3 : periodic_timer_free(main_loop_timeout_timer);
192 3 : }
193 :
194 : static void
195 1 : test_callbacks(void *arg)
196 : {
197 1 : (void)arg;
198 1 : const char *filename = NULL;
199 :
200 : #ifdef _WIN32
201 : filename = get_win32_test_binary_path();
202 : #else
203 1 : filename = TEST_PROCESS;
204 : #endif
205 :
206 : /* Process callback data. */
207 1 : process_data_t *process_data = process_data_new();
208 :
209 : /* Setup our process. */
210 1 : process_t *process = process_new(filename);
211 1 : process_set_data(process, process_data);
212 1 : process_set_stdout_read_callback(process, process_stdout_callback);
213 1 : process_set_stderr_read_callback(process, process_stderr_callback);
214 1 : process_set_exit_callback(process, process_exit_callback);
215 :
216 : /* Set environment variable. */
217 1 : process_set_environment(process, "TOR_TEST_ENV", "Hello, from Tor!");
218 :
219 : /* Add some arguments. */
220 1 : process_append_argument(process, "This is the first one");
221 1 : process_append_argument(process, "Second one");
222 1 : process_append_argument(process, "Third: Foo bar baz");
223 :
224 : /* Run our process. */
225 1 : process_status_t status;
226 :
227 1 : status = process_exec(process);
228 1 : tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
229 :
230 : /* Write some lines to stdin. */
231 1 : process_printf(process, "Hi process!\r\n");
232 1 : process_printf(process, "Can you read more than one line?\n");
233 1 : process_printf(process, "Can you read partial ...");
234 1 : process_printf(process, " lines?\r\n");
235 :
236 : /* Start our main loop. */
237 1 : run_main_loop(process_data);
238 :
239 : /* We returned. Let's see what our event loop said. */
240 1 : tt_int_op(smartlist_len(process_data->stdout_data), OP_EQ, 12);
241 1 : tt_int_op(smartlist_len(process_data->stderr_data), OP_EQ, 3);
242 1 : tt_assert(process_data->did_exit);
243 1 : tt_u64_op(process_data->exit_code, OP_EQ, 0);
244 :
245 : /* Check stdout output. */
246 1 : char argv0_expected[256];
247 1 : tor_snprintf(argv0_expected, sizeof(argv0_expected),
248 : "argv[0] = '%s'", filename);
249 :
250 1 : tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
251 : argv0_expected);
252 1 : tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
253 : "argv[1] = 'This is the first one'");
254 1 : tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
255 : "argv[2] = 'Second one'");
256 1 : tt_str_op(smartlist_get(process_data->stdout_data, 3), OP_EQ,
257 : "argv[3] = 'Third: Foo bar baz'");
258 1 : tt_str_op(smartlist_get(process_data->stdout_data, 4), OP_EQ,
259 : "Environment variable TOR_TEST_ENV = 'Hello, from Tor!'");
260 1 : tt_str_op(smartlist_get(process_data->stdout_data, 5), OP_EQ,
261 : "Output on stdout");
262 1 : tt_str_op(smartlist_get(process_data->stdout_data, 6), OP_EQ,
263 : "This is a new line");
264 1 : tt_str_op(smartlist_get(process_data->stdout_data, 7), OP_EQ,
265 : "Partial line on stdout ...end of partial line on stdout");
266 1 : tt_str_op(smartlist_get(process_data->stdout_data, 8), OP_EQ,
267 : "Read line from stdin: 'Hi process!'");
268 1 : 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 1 : tt_str_op(smartlist_get(process_data->stdout_data, 10), OP_EQ,
271 : "Read line from stdin: 'Can you read partial ... lines?'");
272 1 : 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 1 : tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
277 : "Output on stderr");
278 1 : tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
279 : "This is a new line");
280 1 : 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 1 : done:
284 1 : process_data_free(process_data);
285 1 : process_free(process);
286 1 : }
287 :
288 : static void
289 1 : test_callbacks_terminate(void *arg)
290 : {
291 1 : (void)arg;
292 1 : const char *filename = NULL;
293 :
294 : #ifdef _WIN32
295 : filename = get_win32_test_binary_path();
296 : #else
297 1 : filename = TEST_PROCESS;
298 : #endif
299 :
300 : /* Process callback data. */
301 1 : process_data_t *process_data = process_data_new();
302 :
303 : /* Setup our process. */
304 1 : process_t *process = process_new(filename);
305 1 : process_set_data(process, process_data);
306 1 : process_set_exit_callback(process, process_exit_callback);
307 :
308 : /* Run our process. */
309 1 : process_status_t status;
310 :
311 1 : status = process_exec(process);
312 1 : tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
313 :
314 : /* Zap our process. */
315 1 : bool success;
316 :
317 1 : success = process_terminate(process);
318 1 : tt_assert(success);
319 :
320 : /* Start our main loop. */
321 1 : run_main_loop(process_data);
322 :
323 : /* Check if we did exit. */
324 1 : tt_assert(process_data->did_exit);
325 :
326 1 : done:
327 1 : process_data_free(process_data);
328 1 : process_free(process);
329 1 : }
330 :
331 : static void
332 1 : test_nonexistent_executable(void *arg)
333 : {
334 1 : (void)arg;
335 :
336 : /* Process callback data. */
337 1 : process_data_t *process_data = process_data_new();
338 :
339 : /* Setup our process. */
340 1 : process_t *process = process_new("binary-does-not-exist");
341 1 : process_set_data(process, process_data);
342 1 : process_set_exit_callback(process, process_exit_callback);
343 :
344 : /* Run our process. */
345 1 : process_exec(process);
346 :
347 : /* Start our main loop. */
348 1 : 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 1 : tt_assert(process_data->did_exit);
354 :
355 1 : done:
356 1 : process_data_free(process_data);
357 1 : process_free(process);
358 1 : }
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 : };
|