test_process_slow.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /* Copyright (c) 2018-2019, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. /**
  4. * \file test_process_slow.c
  5. * \brief Slow test cases for the Process API.
  6. */
  7. #define MAINLOOP_PRIVATE
  8. #include "orconfig.h"
  9. #include "core/or/or.h"
  10. #include "core/mainloop/mainloop.h"
  11. #include "lib/evloop/compat_libevent.h"
  12. #include "lib/process/process.h"
  13. #include "lib/process/waitpid.h"
  14. #include "test/test.h"
  15. #ifndef BUILDDIR
  16. #define BUILDDIR "."
  17. #endif
  18. #ifdef _WIN32
  19. #define TEST_PROCESS "test-process.exe"
  20. #else
  21. #define TEST_PROCESS BUILDDIR "/src/test/test-process"
  22. #endif /* defined(_WIN32) */
  23. /** Timer that ticks once a second and stop the event loop after 5 ticks. */
  24. static periodic_timer_t *main_loop_timeout_timer;
  25. /** How many times have our timer ticked? */
  26. static int timer_tick_count;
  27. struct process_data_t {
  28. smartlist_t *stdout_data;
  29. smartlist_t *stderr_data;
  30. smartlist_t *stdin_data;
  31. process_exit_code_t exit_code;
  32. bool did_exit;
  33. };
  34. typedef struct process_data_t process_data_t;
  35. static process_data_t *
  36. process_data_new(void)
  37. {
  38. process_data_t *process_data = tor_malloc_zero(sizeof(process_data_t));
  39. process_data->stdout_data = smartlist_new();
  40. process_data->stderr_data = smartlist_new();
  41. process_data->stdin_data = smartlist_new();
  42. return process_data;
  43. }
  44. static void
  45. process_data_free(process_data_t *process_data)
  46. {
  47. if (process_data == NULL)
  48. 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));
  52. smartlist_free(process_data->stdout_data);
  53. smartlist_free(process_data->stderr_data);
  54. smartlist_free(process_data->stdin_data);
  55. tor_free(process_data);
  56. }
  57. static void
  58. process_stdout_callback(process_t *process, const char *data, size_t size)
  59. {
  60. tt_ptr_op(process, OP_NE, NULL);
  61. tt_ptr_op(data, OP_NE, NULL);
  62. tt_int_op(strlen(data), OP_EQ, size);
  63. process_data_t *process_data = process_get_data(process);
  64. smartlist_add(process_data->stdout_data, tor_strdup(data));
  65. done:
  66. return;
  67. }
  68. static void
  69. process_stderr_callback(process_t *process, const char *data, size_t size)
  70. {
  71. tt_ptr_op(process, OP_NE, NULL);
  72. tt_ptr_op(data, OP_NE, NULL);
  73. tt_int_op(strlen(data), OP_EQ, size);
  74. process_data_t *process_data = process_get_data(process);
  75. smartlist_add(process_data->stderr_data, tor_strdup(data));
  76. done:
  77. return;
  78. }
  79. static bool
  80. process_exit_callback(process_t *process, process_exit_code_t exit_code)
  81. {
  82. process_status_t status;
  83. tt_ptr_op(process, OP_NE, NULL);
  84. process_data_t *process_data = process_get_data(process);
  85. process_data->exit_code = exit_code;
  86. process_data->did_exit = true;
  87. /* Check if our process is still running? */
  88. status = process_get_status(process);
  89. tt_int_op(status, OP_EQ, PROCESS_STATUS_NOT_RUNNING);
  90. done:
  91. /* Do not free up our process_t. */
  92. return false;
  93. }
  94. #ifdef _WIN32
  95. static const char *
  96. get_win32_test_binary_path(void)
  97. {
  98. static char buffer[MAX_PATH];
  99. /* Get the absolute path of our binary: \path\to\test-slow.exe. */
  100. GetModuleFileNameA(GetModuleHandle(0), buffer, sizeof(buffer));
  101. /* Find our process name. */
  102. char *offset = strstr(buffer, "test-slow.exe");
  103. tt_ptr_op(offset, OP_NE, NULL);
  104. /* Change test-slow.exe to test-process.exe. */
  105. memcpy(offset, TEST_PROCESS, strlen(TEST_PROCESS));
  106. return buffer;
  107. done:
  108. return NULL;
  109. }
  110. #endif /* defined(_WIN32) */
  111. static void
  112. main_loop_timeout_cb(periodic_timer_t *timer, void *data)
  113. {
  114. /* Sanity check. */
  115. tt_ptr_op(timer, OP_EQ, main_loop_timeout_timer);
  116. tt_ptr_op(data, OP_NE, NULL);
  117. /* Our process data. */
  118. process_data_t *process_data = data;
  119. /* Our process did exit. */
  120. if (process_data->did_exit)
  121. tor_shutdown_event_loop_and_exit(0);
  122. /* Have we been called 10 times we exit the main loop. */
  123. timer_tick_count++;
  124. tt_int_op(timer_tick_count, OP_LT, 10);
  125. #ifndef _WIN32
  126. /* Call waitpid callbacks. */
  127. notify_pending_waitpid_callbacks();
  128. #endif
  129. return;
  130. done:
  131. /* Exit with an error. */
  132. tor_shutdown_event_loop_and_exit(-1);
  133. }
  134. static void
  135. run_main_loop(process_data_t *process_data)
  136. {
  137. int ret;
  138. /* Wake up after 1 seconds. */
  139. static const struct timeval interval = {1, 0};
  140. timer_tick_count = 0;
  141. main_loop_timeout_timer = periodic_timer_new(tor_libevent_get_base(),
  142. &interval,
  143. main_loop_timeout_cb,
  144. process_data);
  145. /* Run our main loop. */
  146. ret = run_main_loop_until_done();
  147. /* Clean up our main loop timeout timer. */
  148. tt_int_op(ret, OP_EQ, 0);
  149. done:
  150. periodic_timer_free(main_loop_timeout_timer);
  151. }
  152. static void
  153. test_callbacks(void *arg)
  154. {
  155. (void)arg;
  156. const char *filename = NULL;
  157. #ifdef _WIN32
  158. filename = get_win32_test_binary_path();
  159. #else
  160. filename = TEST_PROCESS;
  161. #endif
  162. /* Process callback data. */
  163. process_data_t *process_data = process_data_new();
  164. /* Setup our process. */
  165. process_t *process = process_new(filename);
  166. process_set_data(process, process_data);
  167. process_set_stdout_read_callback(process, process_stdout_callback);
  168. process_set_stderr_read_callback(process, process_stderr_callback);
  169. process_set_exit_callback(process, process_exit_callback);
  170. /* Set environment variable. */
  171. process_set_environment(process, "TOR_TEST_ENV", "Hello, from Tor!");
  172. /* Add some arguments. */
  173. process_append_argument(process, "This is the first one");
  174. process_append_argument(process, "Second one");
  175. process_append_argument(process, "Third: Foo bar baz");
  176. /* Run our process. */
  177. process_status_t status;
  178. status = process_exec(process);
  179. tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
  180. /* Write some lines to stdin. */
  181. process_printf(process, "Hi process!\r\n");
  182. process_printf(process, "Can you read more than one line?\n");
  183. process_printf(process, "Can you read partial ...");
  184. process_printf(process, " lines?\r\n");
  185. /* Start our main loop. */
  186. run_main_loop(process_data);
  187. /* We returned. Let's see what our event loop said. */
  188. tt_int_op(smartlist_len(process_data->stdout_data), OP_EQ, 12);
  189. tt_int_op(smartlist_len(process_data->stderr_data), OP_EQ, 3);
  190. tt_assert(process_data->did_exit);
  191. tt_u64_op(process_data->exit_code, OP_EQ, 0);
  192. /* Check stdout output. */
  193. char argv0_expected[256];
  194. tor_snprintf(argv0_expected, sizeof(argv0_expected),
  195. "argv[0] = '%s'", filename);
  196. tt_str_op(smartlist_get(process_data->stdout_data, 0), OP_EQ,
  197. argv0_expected);
  198. tt_str_op(smartlist_get(process_data->stdout_data, 1), OP_EQ,
  199. "argv[1] = 'This is the first one'");
  200. tt_str_op(smartlist_get(process_data->stdout_data, 2), OP_EQ,
  201. "argv[2] = 'Second one'");
  202. tt_str_op(smartlist_get(process_data->stdout_data, 3), OP_EQ,
  203. "argv[3] = 'Third: Foo bar baz'");
  204. tt_str_op(smartlist_get(process_data->stdout_data, 4), OP_EQ,
  205. "Environment variable TOR_TEST_ENV = 'Hello, from Tor!'");
  206. tt_str_op(smartlist_get(process_data->stdout_data, 5), OP_EQ,
  207. "Output on stdout");
  208. tt_str_op(smartlist_get(process_data->stdout_data, 6), OP_EQ,
  209. "This is a new line");
  210. tt_str_op(smartlist_get(process_data->stdout_data, 7), OP_EQ,
  211. "Partial line on stdout ...end of partial line on stdout");
  212. tt_str_op(smartlist_get(process_data->stdout_data, 8), OP_EQ,
  213. "Read line from stdin: 'Hi process!'");
  214. tt_str_op(smartlist_get(process_data->stdout_data, 9), OP_EQ,
  215. "Read line from stdin: 'Can you read more than one line?'");
  216. tt_str_op(smartlist_get(process_data->stdout_data, 10), OP_EQ,
  217. "Read line from stdin: 'Can you read partial ... lines?'");
  218. tt_str_op(smartlist_get(process_data->stdout_data, 11), OP_EQ,
  219. "We are done for here, thank you!");
  220. /* Check stderr output. */
  221. tt_str_op(smartlist_get(process_data->stderr_data, 0), OP_EQ,
  222. "Output on stderr");
  223. tt_str_op(smartlist_get(process_data->stderr_data, 1), OP_EQ,
  224. "This is a new line");
  225. tt_str_op(smartlist_get(process_data->stderr_data, 2), OP_EQ,
  226. "Partial line on stderr ...end of partial line on stderr");
  227. done:
  228. process_data_free(process_data);
  229. process_free(process);
  230. }
  231. static void
  232. test_callbacks_terminate(void *arg)
  233. {
  234. (void)arg;
  235. const char *filename = NULL;
  236. #ifdef _WIN32
  237. filename = get_win32_test_binary_path();
  238. #else
  239. filename = TEST_PROCESS;
  240. #endif
  241. /* Process callback data. */
  242. process_data_t *process_data = process_data_new();
  243. /* Setup our process. */
  244. process_t *process = process_new(filename);
  245. process_set_data(process, process_data);
  246. process_set_exit_callback(process, process_exit_callback);
  247. /* Run our process. */
  248. process_status_t status;
  249. status = process_exec(process);
  250. tt_int_op(status, OP_EQ, PROCESS_STATUS_RUNNING);
  251. /* Zap our process. */
  252. bool success;
  253. success = process_terminate(process);
  254. tt_assert(success);
  255. /* Start our main loop. */
  256. run_main_loop(process_data);
  257. /* Check if we did exit. */
  258. tt_assert(process_data->did_exit);
  259. done:
  260. process_data_free(process_data);
  261. process_free(process);
  262. }
  263. struct testcase_t slow_process_tests[] = {
  264. { "callbacks", test_callbacks, 0, NULL, NULL },
  265. { "callbacks_terminate", test_callbacks_terminate, 0, NULL, NULL },
  266. END_OF_TESTCASES
  267. };