]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
runtests: spawn a new process for the test runner
authorDan Fandrich <dan@coneharvesters.com>
Sun, 30 Apr 2023 05:11:25 +0000 (22:11 -0700)
committerDan Fandrich <dan@coneharvesters.com>
Fri, 5 May 2023 07:45:43 +0000 (00:45 -0700)
When the -j option is given, a new process is spawned in which the test
programs are run and from which test servers are started. Only one
process can be started at once, but this is sufficient to test that the
infrastructure can isolate those functions in a new task. There should
be no visible difference between the two modes at the moment.

Ref: #10818
Closes #11064

tests/globalconfig.pm
tests/runner.pm
tests/runtests.pl

index 9287e2b0dfdb3c5a841fcb6fa49d200bebaa1192..e44781f95741db19592c226a1e6ca6e7c1d0a6f1 100644 (file)
@@ -85,7 +85,8 @@ our $CURLVERSION="";  # curl's reported version number
 our $pwd = getcwd();  # current working directory
 our $srcdir = $ENV{'srcdir'} || '.';  # root of the test source code
 our $perl="perl -I$srcdir"; # invoke perl like this
-our $LOGDIR="log";  # root of the log directory
+our $LOGDIR="log";  # root of the log directory; this will be different for
+                    # each runner in multiprocess mode
 our $LIBDIR="./libtest";
 our $TESTDIR="$srcdir/data";
 our $CURL="../src/curl".exe_ext('TOOL'); # what curl binary to run on the tests
index 71e20ce58b85bc97430cb878a3a6c7cf21b1dc66..8ea50de8903dfb34d7f63a76b5374373a41c1a63 100644 (file)
 #
 ###########################################################################
 
-# This module contains entry points to run a single test
+# This module contains entry points to run a single test. runner_init
+# determines whether they will run in a separate process or in the process of
+# the caller. The relevant interface is asynchronous so it will work in either
+# case. Program arguments are marshalled and then written to the end of a pipe
+# (in controlleripccall) which is later read from and the arguments
+# unmarshalled (in ipcrecv) before the desired function is called normally.
+# The function return values are then marshalled and written into another pipe
+# (again in ipcrecv) when is later read from and unmarshalled (in runnerar)
+# before being returned to the caller.
 
 package runner;
 
@@ -36,6 +44,7 @@ BEGIN {
     our @EXPORT = qw(
         checktestcmd
         prepro
+        readtestkeywords
         restore_test_env
         runner_init
         runnerac_clearlocks
@@ -59,7 +68,6 @@ BEGIN {
 
     # these are for debugging only
     our @EXPORT_OK = qw(
-        readtestkeywords
         singletest_preprocess
     );
 }
@@ -99,6 +107,7 @@ use testutil qw(
 
 #######################################################################
 # Global variables set elsewhere but used only by this package
+# These may only be set *before* runner_init is called
 our $DBGCURL=$CURL; #"../src/.libs/curl";  # alternative for debugging
 our $valgrind_logfile="--log-file";  # the option name for valgrind >=3
 our $valgrind_tool="--tool=memcheck";
@@ -121,7 +130,8 @@ my $controllerw;    # pipe that controller writes to
 my $runnerr;        # pipe that runner reads from
 my $runnerw;        # pipe that runner writes to
 my $controllerr;    # pipe that controller reads from
-
+my $multiprocess;   # nonzero with a separate test runner process
+my $onerunnerid;    # a single runner ID
 
 # redirected stdout/stderr to these files
 sub stdoutfilename {
@@ -140,12 +150,9 @@ sub stderrfilename {
 # runnerac_* functions
 # Called by controller
 sub runner_init {
-    my ($logdir)=@_;
+    my ($logdir, $jobs)=@_;
 
-    # Set this directory as ours
-    # TODO: This will need to be uncommented once there are multiple runners
-    #$LOGDIR = $logdir;
-    mkdir("$LOGDIR/$PIDDIR", 0777);
+    $multiprocess = !!$jobs;
 
     # enable memory debugging if curl is compiled with it
     $ENV{'CURL_MEMDEBUG'} = "$LOGDIR/$MEMDUMP";
@@ -161,8 +168,58 @@ sub runner_init {
     pipe $runnerr, $controllerw;
     pipe $controllerr, $runnerw;
 
-    # There is only one runner right now
-    return "singleton";
+    if($multiprocess) {
+        # Create a separate process in multiprocess mode
+        my $child = fork();
+        if(0 == $child) {
+            # TODO: set up a better signal handler
+            $SIG{INT} = 'IGNORE';
+            $SIG{TERM} = 'IGNORE';
+
+            $onerunnerid = $$;
+            print "Runner $onerunnerid starting\n" if($verbose);
+
+            # Here we are the child (runner).
+            close($controllerw);
+            close($controllerr);
+
+            # Set this directory as ours
+            $LOGDIR = $logdir;
+            mkdir("$LOGDIR/$PIDDIR", 0777);
+
+            # handle IPC calls
+            event_loop();
+
+            # Can't rely on logmsg here in case it's buffered
+            print "Runner $onerunnerid exiting\n" if($verbose);
+            exit 0;
+        }
+
+        # Here we are the parent (controller).
+        close($runnerw);
+        close($runnerr);
+
+        $onerunnerid = $child;
+
+    } else {
+        # Create our pid directory
+        mkdir("$LOGDIR/$PIDDIR", 0777);
+
+        # Don't create a separate process
+        $onerunnerid = "integrated";
+    }
+
+    return $onerunnerid;
+}
+
+#######################################################################
+# Loop to execute incoming IPC calls until the shutdown call
+sub event_loop {
+    while () {
+        if(ipcrecv()) {
+            last;
+        }
+    }
 }
 
 #######################################################################
@@ -979,6 +1036,11 @@ sub runner_test_preprocess {
     loadtest("${TESTDIR}/test${testnum}");
     readtestkeywords();
 
+    ###################################################################
+    # Restore environment variables that were modified in a previous run.
+    # Test definition may instruct to (un)set environment vars.
+    restore_test_env(1);
+
     ###################################################################
     # Start the servers needed to run this test case
     my ($why, $error) = singletest_startservers($testnum, \%testtimings);
@@ -1115,10 +1177,10 @@ sub controlleripccall {
     # Send IPC call via pipe
     syswrite($controllerw, (pack "L", length($margs)) . $margs);
 
-    # Call the remote function
-    # TODO: this will eventually be done in a separate runner process
-    # kicked off by runner_init()
-    ipcrecv();
+    if(!$multiprocess) {
+        # Call the remote function here in single process mode
+        ipcrecv();
+     }
 }
 
 ###################################################################
@@ -1140,7 +1202,7 @@ sub runnerar {
     my $resarrayref = thaw $buf;
 
     # First argument is runner ID
-    unshift @$resarrayref, "singleton";
+    unshift @$resarrayref, $onerunnerid;
     return @$resarrayref;
 }
 
index bf90583a027978b6dfee6e93d64571a1151e85e8..dca495076d0e02f8ac3c0992e819fd358b9d6e94 100755 (executable)
@@ -174,6 +174,7 @@ my $postmortem;   # display detailed info about failed tests
 my $run_disabled; # run the specific tests even if listed in DISABLED
 my $scrambleorder;
 my $randseed = 0;
+my $jobs = 0;
 
 # Azure Pipelines specific variables
 my $AZURE_RUN_ID = 0;
@@ -748,6 +749,10 @@ sub checksystemfeatures {
             "* System: $hosttype",
             "* OS: $hostos\n");
 
+    if($jobs) {
+        # Only show if not the default for now
+        logmsg "* Jobs: $jobs\n";
+    }
     if($feature{"TrackMemory"} && $feature{"threaded-resolver"}) {
         logmsg("*\n",
                "*** DISABLES memory tracking when using threaded resolver\n",
@@ -1633,7 +1638,6 @@ sub singletest {
 
     if($singletest_state == ST_INIT) {
         my $logdir = getlogdir($testnum);
-
         # first, remove all lingering log files
         if(!cleardir($logdir) && $clearlocks) {
             runnerac_clearlocks($runnerid, $logdir);
@@ -1661,7 +1665,7 @@ sub singletest {
         # Test definition may instruct to (un)set environment vars.
         # This is done this early so that leftover variables don't affect
         # starting servers or CI registration.
-        restore_test_env(1);
+        restore_test_env(1);
 
         ###################################################################
         # Load test file so CI registration can get the right data before the
@@ -1687,6 +1691,11 @@ sub singletest {
         }
         updatetesttimings($testnum, %$testtimings);
 
+        #######################################################################
+        # Load test file for this test number
+        my $logdir = getlogdir($testnum);
+        loadtest("${logdir}/test${testnum}");
+
         #######################################################################
         # Print the test name and count tests
         $error = singletest_count($testnum, $why);
@@ -1739,6 +1748,12 @@ sub singletest {
 
         #######################################################################
         # Verify that the test succeeded
+        #
+        # Load test file for this test number
+        my $logdir = getlogdir($testnum);
+        loadtest("${logdir}/test${testnum}");
+        readtestkeywords();
+
         $error = singletest_check($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind);
         if($error == -1) {
             my $err = ignoreresultcode($testnum);
@@ -2076,6 +2091,14 @@ while(@ARGV) {
         # lists the test case names only
         $listonly=1;
     }
+    elsif($ARGV[0] =~ /^-j(.*)/) {
+        # parallel jobs
+        $jobs=1;
+        my $xtra = $1;
+        if($xtra =~ s/(\d+)$//) {
+            $jobs = $1;
+        }
+    }
     elsif($ARGV[0] eq "-k") {
         # keep stdout and stderr files after tests
         $keepoutfiles=1;
@@ -2133,6 +2156,7 @@ Usage: runtests.pl [options] [test selection(s)]
   -g       run the test case with gdb
   -gw      run the test case with gdb as a windowed application
   -h       this help text
+  -j[N]    spawn this number of processes to run tests (default 0, max. 1)
   -k       keep stdout and stderr files present after tests
   -L path  require an additional perl library file to replace certain functions
   -l       list all test case names/descriptions
@@ -2291,8 +2315,10 @@ mkdir($LOGDIR, 0777);
 #
 
 get_disttests();
-# Disable buffered logging for now
-setlogfunc(\&logmsg);
+if(!$jobs) {
+    # Disable buffered logging with only one test job
+    setlogfunc(\&logmsg);
+}
 
 #######################################################################
 # Output curl version and host info being tested
@@ -2560,7 +2586,7 @@ citest_starttestrun();
 # Initialize the runner to prepare to run tests
 cleardir($LOGDIR);
 mkdir($LOGDIR, 0777);
-my $runnerid = runner_init($LOGDIR);
+my $runnerid = runner_init($LOGDIR, $jobs);
 
 #######################################################################
 # The main test-loop