#
###########################################################################
-# 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;
our @EXPORT = qw(
checktestcmd
prepro
+ readtestkeywords
restore_test_env
runner_init
runnerac_clearlocks
# these are for debugging only
our @EXPORT_OK = qw(
- readtestkeywords
singletest_preprocess
);
}
#######################################################################
# 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";
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 {
# 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";
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;
+ }
+ }
}
#######################################################################
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);
# 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();
+ }
}
###################################################################
my $resarrayref = thaw $buf;
# First argument is runner ID
- unshift @$resarrayref, "singleton";
+ unshift @$resarrayref, $onerunnerid;
return @$resarrayref;
}
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;
"* 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",
if($singletest_state == ST_INIT) {
my $logdir = getlogdir($testnum);
-
# first, remove all lingering log files
if(!cleardir($logdir) && $clearlocks) {
runnerac_clearlocks($runnerid, $logdir);
# 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
}
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);
#######################################################################
# 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);
# 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;
-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
#
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
# 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