test_process_slow.c (10207B)
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 process_data_new(void) 46 { 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; 52 } 53 54 static void 55 process_data_free(process_data_t *process_data) 56 { 57 if (process_data == NULL) 58 return; 59 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)); 63 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); 68 } 69 70 static void 71 process_stdout_callback(process_t *process, const char *data, size_t size) 72 { 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); 76 77 process_data_t *process_data = process_get_data(process); 78 smartlist_add(process_data->stdout_data, tor_strdup(data)); 79 80 done: 81 return; 82 } 83 84 static void 85 process_stderr_callback(process_t *process, const char *data, size_t size) 86 { 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); 90 91 process_data_t *process_data = process_get_data(process); 92 smartlist_add(process_data->stderr_data, tor_strdup(data)); 93 94 done: 95 return; 96 } 97 98 static bool 99 process_exit_callback(process_t *process, process_exit_code_t exit_code) 100 { 101 process_status_t status; 102 103 tt_ptr_op(process, OP_NE, NULL); 104 105 process_data_t *process_data = process_get_data(process); 106 process_data->exit_code = exit_code; 107 process_data->did_exit = true; 108 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); 112 113 done: 114 /* Do not free up our process_t. */ 115 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 main_loop_timeout_cb(periodic_timer_t *timer, void *data) 142 { 143 /* Sanity check. */ 144 tt_ptr_op(timer, OP_EQ, main_loop_timeout_timer); 145 tt_ptr_op(data, OP_NE, NULL); 146 147 /* Our process data. */ 148 process_data_t *process_data = data; 149 150 /* Our process did exit. */ 151 if (process_data->did_exit) 152 tor_shutdown_event_loop_and_exit(0); 153 154 /* Have we been called 10 times we exit the main loop. */ 155 timer_tick_count++; 156 157 tt_int_op(timer_tick_count, OP_LT, 10); 158 159 #ifndef _WIN32 160 /* Call waitpid callbacks. */ 161 notify_pending_waitpid_callbacks(); 162 #endif 163 164 return; 165 done: 166 /* Exit with an error. */ 167 tor_shutdown_event_loop_and_exit(-1); 168 } 169 170 static void 171 run_main_loop(process_data_t *process_data) 172 { 173 int ret; 174 175 /* Wake up after 1 seconds. */ 176 static const struct timeval interval = {1, 0}; 177 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); 183 184 /* Run our main loop. */ 185 ret = run_main_loop_until_done(); 186 187 /* Clean up our main loop timeout timer. */ 188 tt_int_op(ret, OP_EQ, 0); 189 190 done: 191 periodic_timer_free(main_loop_timeout_timer); 192 } 193 194 static void 195 test_callbacks(void *arg) 196 { 197 (void)arg; 198 const char *filename = NULL; 199 200 #ifdef _WIN32 201 filename = get_win32_test_binary_path(); 202 #else 203 filename = TEST_PROCESS; 204 #endif 205 206 /* Process callback data. */ 207 process_data_t *process_data = process_data_new(); 208 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); 215 216 /* Set environment variable. */ 217 process_set_environment(process, "TOR_TEST_ENV", "Hello, from Tor!"); 218 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"); 223 224 /* Run our process. */ 225 process_status_t status; 226 227 status = process_exec(process); 228 tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING); 229 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"); 235 236 /* Start our main loop. */ 237 run_main_loop(process_data); 238 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); 244 245 /* Check stdout output. */ 246 char argv0_expected[256]; 247 tor_snprintf(argv0_expected, sizeof(argv0_expected), 248 "argv[0] = '%s'", filename); 249 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!"); 274 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"); 282 283 done: 284 process_data_free(process_data); 285 process_free(process); 286 } 287 288 static void 289 test_callbacks_terminate(void *arg) 290 { 291 (void)arg; 292 const char *filename = NULL; 293 294 #ifdef _WIN32 295 filename = get_win32_test_binary_path(); 296 #else 297 filename = TEST_PROCESS; 298 #endif 299 300 /* Process callback data. */ 301 process_data_t *process_data = process_data_new(); 302 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); 307 308 /* Run our process. */ 309 process_status_t status; 310 311 status = process_exec(process); 312 tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING); 313 314 /* Zap our process. */ 315 bool success; 316 317 success = process_terminate(process); 318 tt_assert(success); 319 320 /* Start our main loop. */ 321 run_main_loop(process_data); 322 323 /* Check if we did exit. */ 324 tt_assert(process_data->did_exit); 325 326 done: 327 process_data_free(process_data); 328 process_free(process); 329 } 330 331 static void 332 test_nonexistent_executable(void *arg) 333 { 334 (void)arg; 335 336 /* Process callback data. */ 337 process_data_t *process_data = process_data_new(); 338 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); 343 344 /* Run our process. */ 345 process_exec(process); 346 347 /* Start our main loop. */ 348 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 tt_assert(process_data->did_exit); 354 355 done: 356 process_data_free(process_data); 357 process_free(process); 358 } 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 };