LCOV - code coverage report
Current view: top level - test - test_process_slow.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 141 143 98.6 %
Date: 2021-11-24 03:28:48 Functions: 10 10 100.0 %

          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             : };

Generated by: LCOV version 1.14