runtests: turn singletest() into a state machine
This allows it to run in a non-blocking manner. Ref: #10818
This commit is contained in:
Родитель
a98277fcc7
Коммит
d4a1b5b60c
|
@ -1146,12 +1146,13 @@ sub runnerar {
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Returns nonzero if a response from an async call is ready
|
# Returns nonzero if a response from an async call is ready
|
||||||
|
# argument is 0 for nonblocking, undef for blocking, anything else for timeout
|
||||||
# Called by controller
|
# Called by controller
|
||||||
sub runnerar_ready {
|
sub runnerar_ready {
|
||||||
my ($blocking) = @_;
|
my ($blocking) = @_;
|
||||||
my $rin = "";
|
my $rin = "";
|
||||||
vec($rin, fileno($controllerr), 1) = 1;
|
vec($rin, fileno($controllerr), 1) = 1;
|
||||||
return select(my $rout=$rin, undef, my $eout=$rin, $blocking ? undef : 0);
|
return select(my $rout=$rin, undef, my $eout=$rin, $blocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
|
@ -1184,7 +1185,7 @@ sub ipcrecv {
|
||||||
elsif($funcname eq "runner_shutdown") {
|
elsif($funcname eq "runner_shutdown") {
|
||||||
runner_shutdown(@$argsarrayref);
|
runner_shutdown(@$argsarrayref);
|
||||||
# Special case: no response
|
# Special case: no response
|
||||||
return;
|
return 1;
|
||||||
}
|
}
|
||||||
elsif($funcname eq "runner_stopservers") {
|
elsif($funcname eq "runner_stopservers") {
|
||||||
@res = runner_stopservers(@$argsarrayref);
|
@res = runner_stopservers(@$argsarrayref);
|
||||||
|
@ -1203,6 +1204,8 @@ sub ipcrecv {
|
||||||
$buf = freeze \@res;
|
$buf = freeze \@res;
|
||||||
|
|
||||||
syswrite($runnerw, (pack "L", length($buf)) . $buf);
|
syswrite($runnerw, (pack "L", length($buf)) . $buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
|
|
|
@ -150,12 +150,22 @@ my %timetoolini; # timestamp for each test command run starting
|
||||||
my %timetoolend; # timestamp for each test command run stopping
|
my %timetoolend; # timestamp for each test command run stopping
|
||||||
my %timesrvrlog; # timestamp for each test server logs lock removal
|
my %timesrvrlog; # timestamp for each test server logs lock removal
|
||||||
my %timevrfyend; # timestamp for each test result verification end
|
my %timevrfyend; # timestamp for each test result verification end
|
||||||
my $runnerid; # ID for runner async calls
|
my $globalabort; # flag signalling program abort
|
||||||
|
|
||||||
|
# values for $singletest_state
|
||||||
|
use constant {
|
||||||
|
ST_INIT => 0,
|
||||||
|
ST_CLEARLOCKS => 1,
|
||||||
|
ST_INITED => 2,
|
||||||
|
ST_PREPROCESS => 3,
|
||||||
|
ST_RUN => 4,
|
||||||
|
};
|
||||||
|
my $singletest_state = ST_INIT; # current state of singletest()
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# variables that command line options may set
|
# variables that command line options may set
|
||||||
#
|
#
|
||||||
|
|
||||||
my $short;
|
my $short;
|
||||||
my $no_debuginfod;
|
my $no_debuginfod;
|
||||||
my $keepoutfiles; # keep stdout and stderr files after tests
|
my $keepoutfiles; # keep stdout and stderr files after tests
|
||||||
|
@ -186,14 +196,7 @@ sub logmsg {
|
||||||
sub catch_zap {
|
sub catch_zap {
|
||||||
my $signame = shift;
|
my $signame = shift;
|
||||||
logmsg "runtests.pl received SIG$signame, exiting\n";
|
logmsg "runtests.pl received SIG$signame, exiting\n";
|
||||||
# TODO: make this set a flag that is checked in the main test loop
|
$globalabort = 1;
|
||||||
if($runnerid) {
|
|
||||||
runnerac_stopservers($runnerid);
|
|
||||||
runnerar(); # ignore the results
|
|
||||||
# Kill the runner entirely
|
|
||||||
runnerac_shutdown($runnerid);
|
|
||||||
}
|
|
||||||
die "Somebody sent me a SIG$signame";
|
|
||||||
}
|
}
|
||||||
$SIG{INT} = \&catch_zap;
|
$SIG{INT} = \&catch_zap;
|
||||||
$SIG{TERM} = \&catch_zap;
|
$SIG{TERM} = \&catch_zap;
|
||||||
|
@ -1077,7 +1080,7 @@ sub singletest_count {
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Verify test succeeded
|
# Verify test succeeded
|
||||||
sub singletest_check {
|
sub singletest_check {
|
||||||
my ($testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind)=@_;
|
my ($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind)=@_;
|
||||||
|
|
||||||
# Skip all the verification on torture tests
|
# Skip all the verification on torture tests
|
||||||
if ($torture) {
|
if ($torture) {
|
||||||
|
@ -1619,118 +1622,153 @@ sub singletest_success {
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Run a single specified test case
|
# Run a single specified test case
|
||||||
|
# This is structured as a state machine which changes states after an
|
||||||
|
# asynchronous call is made that awaits a response. The function returns with
|
||||||
|
# an error code and a flag that indicates if the state machine has completed,
|
||||||
|
# which means (if not) the function must be called again once the response has
|
||||||
|
# arrived.
|
||||||
#
|
#
|
||||||
sub singletest {
|
sub singletest {
|
||||||
my ($testnum, $count, $total)=@_;
|
my ($runnerid, $testnum, $count, $total)=@_;
|
||||||
|
|
||||||
my $logdir = getlogdir($testnum);
|
if($singletest_state == ST_INIT) {
|
||||||
|
my $logdir = getlogdir($testnum);
|
||||||
|
|
||||||
# first, remove all lingering log files
|
# first, remove all lingering log files
|
||||||
if(!cleardir($logdir) && $clearlocks) {
|
if(!cleardir($logdir) && $clearlocks) {
|
||||||
runnerac_clearlocks($runnerid, $logdir);
|
runnerac_clearlocks($runnerid, $logdir);
|
||||||
|
$singletest_state = ST_CLEARLOCKS;
|
||||||
|
} else {
|
||||||
|
$singletest_state = ST_INITED;
|
||||||
|
# Recursively call the state machine again because there is no
|
||||||
|
# event expected that would otherwise trigger a new call.
|
||||||
|
return singletest(@_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} elsif($singletest_state == ST_CLEARLOCKS) {
|
||||||
my ($rid, $logs) = runnerar();
|
my ($rid, $logs) = runnerar();
|
||||||
logmsg $logs;
|
logmsg $logs;
|
||||||
|
my $logdir = getlogdir($testnum);
|
||||||
cleardir($logdir);
|
cleardir($logdir);
|
||||||
}
|
$singletest_state = ST_INITED;
|
||||||
|
# Recursively call the state machine again because there is no
|
||||||
|
# event expected that would otherwise trigger a new call.
|
||||||
|
return singletest(@_);
|
||||||
|
|
||||||
###################################################################
|
} elsif($singletest_state == ST_INITED) {
|
||||||
# Restore environment variables that were modified in a previous run.
|
###################################################################
|
||||||
# Test definition may instruct to (un)set environment vars.
|
# Restore environment variables that were modified in a previous run.
|
||||||
# This is done this early so that leftover variables don't affect
|
# Test definition may instruct to (un)set environment vars.
|
||||||
# starting servers or CI registration.
|
# This is done this early so that leftover variables don't affect
|
||||||
restore_test_env(1);
|
# starting servers or CI registration.
|
||||||
|
restore_test_env(1);
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Load test file so CI registration can get the right data before the
|
# Load test file so CI registration can get the right data before the
|
||||||
# runner is called
|
# runner is called
|
||||||
loadtest("${TESTDIR}/test${testnum}");
|
loadtest("${TESTDIR}/test${testnum}");
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Register the test case with the CI environment
|
# Register the test case with the CI environment
|
||||||
citest_starttest($testnum);
|
citest_starttest($testnum);
|
||||||
|
|
||||||
runnerac_test_preprocess($runnerid, $testnum);
|
runnerac_test_preprocess($runnerid, $testnum);
|
||||||
my ($rid, $why, $error, $logs, $testtimings) = runnerar();
|
$singletest_state = ST_PREPROCESS;
|
||||||
logmsg $logs;
|
|
||||||
if($error == -2) {
|
} elsif($singletest_state == ST_PREPROCESS) {
|
||||||
if($postmortem) {
|
my ($rid, $why, $error, $logs, $testtimings) = runnerar();
|
||||||
# Error indicates an actual problem starting the server, so
|
logmsg $logs;
|
||||||
# display the server logs
|
if($error == -2) {
|
||||||
displaylogs($testnum);
|
if($postmortem) {
|
||||||
|
# Error indicates an actual problem starting the server, so
|
||||||
|
# display the server logs
|
||||||
|
displaylogs($testnum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
updatetesttimings($testnum, %$testtimings);
|
||||||
updatetesttimings($testnum, %$testtimings);
|
|
||||||
|
#######################################################################
|
||||||
|
# Print the test name and count tests
|
||||||
|
$error = singletest_count($testnum, $why);
|
||||||
|
if($error) {
|
||||||
|
# Submit the test case result with the CI environment
|
||||||
|
citest_finishtest($testnum, $error);
|
||||||
|
$singletest_state = ST_INIT;
|
||||||
|
return ($error, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Execute this test number
|
||||||
|
my $cmdres;
|
||||||
|
my $CURLOUT;
|
||||||
|
my $tool;
|
||||||
|
my $usedvalgrind;
|
||||||
|
runnerac_test_run($runnerid, $testnum);
|
||||||
|
$singletest_state = ST_RUN;
|
||||||
|
|
||||||
|
} elsif($singletest_state == ST_RUN) {
|
||||||
|
my ($rid, $error, $logs, $testtimings, $cmdres, $CURLOUT, $tool, $usedvalgrind) = runnerar();
|
||||||
|
logmsg $logs;
|
||||||
|
updatetesttimings($testnum, %$testtimings);
|
||||||
|
if($error == -1) {
|
||||||
|
# no further verification will occur
|
||||||
|
$timevrfyend{$testnum} = Time::HiRes::time();
|
||||||
|
my $err = ignoreresultcode($testnum);
|
||||||
|
# Submit the test case result with the CI environment
|
||||||
|
citest_finishtest($testnum, $err);
|
||||||
|
$singletest_state = ST_INIT;
|
||||||
|
# return a test failure, either to be reported or to be ignored
|
||||||
|
return ($err, 0);
|
||||||
|
}
|
||||||
|
elsif($error == -2) {
|
||||||
|
# fill in the missing timings on error
|
||||||
|
timestampskippedevents($testnum);
|
||||||
|
# Submit the test case result with the CI environment
|
||||||
|
citest_finishtest($testnum, $error);
|
||||||
|
$singletest_state = ST_INIT;
|
||||||
|
return ($error, 0);
|
||||||
|
}
|
||||||
|
elsif($error > 0) {
|
||||||
|
# no further verification will occur
|
||||||
|
$timevrfyend{$testnum} = Time::HiRes::time();
|
||||||
|
# Submit the test case result with the CI environment
|
||||||
|
citest_finishtest($testnum, $error);
|
||||||
|
$singletest_state = ST_INIT;
|
||||||
|
return ($error, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Verify that the test succeeded
|
||||||
|
$error = singletest_check($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind);
|
||||||
|
if($error == -1) {
|
||||||
|
my $err = ignoreresultcode($testnum);
|
||||||
|
# Submit the test case result with the CI environment
|
||||||
|
citest_finishtest($testnum, $err);
|
||||||
|
$singletest_state = ST_INIT;
|
||||||
|
# return a test failure, either to be reported or to be ignored
|
||||||
|
return ($err, 0);
|
||||||
|
}
|
||||||
|
elsif($error == -2) {
|
||||||
|
# torture test; there is no verification, so the run result holds the
|
||||||
|
# test success code
|
||||||
|
# Submit the test case result with the CI environment
|
||||||
|
citest_finishtest($testnum, $cmdres);
|
||||||
|
$singletest_state = ST_INIT;
|
||||||
|
return ($cmdres, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Report a successful test
|
||||||
|
singletest_success($testnum, $count, $total, ignoreresultcode($testnum));
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Print the test name and count tests
|
|
||||||
$error = singletest_count($testnum, $why);
|
|
||||||
if($error) {
|
|
||||||
# Submit the test case result with the CI environment
|
# Submit the test case result with the CI environment
|
||||||
citest_finishtest($testnum, $error);
|
citest_finishtest($testnum, 0);
|
||||||
return $error;
|
$singletest_state = ST_INIT;
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
return (0, 0); # state machine is finished
|
||||||
# Execute this test number
|
|
||||||
my $cmdres;
|
|
||||||
my $CURLOUT;
|
|
||||||
my $tool;
|
|
||||||
my $usedvalgrind;
|
|
||||||
runnerac_test_run($runnerid, $testnum);
|
|
||||||
($rid, $error, $logs, $testtimings, $cmdres, $CURLOUT, $tool, $usedvalgrind) = runnerar();
|
|
||||||
logmsg $logs;
|
|
||||||
updatetesttimings($testnum, %$testtimings);
|
|
||||||
if($error == -1) {
|
|
||||||
# no further verification will occur
|
|
||||||
$timevrfyend{$testnum} = Time::HiRes::time();
|
|
||||||
my $err = ignoreresultcode($testnum);
|
|
||||||
# Submit the test case result with the CI environment
|
|
||||||
citest_finishtest($testnum, $err);
|
|
||||||
# return a test failure, either to be reported or to be ignored
|
|
||||||
return $err;
|
|
||||||
}
|
}
|
||||||
elsif($error == -2) {
|
return (0, 1); # state machine must be called again on event
|
||||||
# fill in the missing timings on error
|
|
||||||
timestampskippedevents($testnum);
|
|
||||||
# Submit the test case result with the CI environment
|
|
||||||
citest_finishtest($testnum, $error);
|
|
||||||
return $error;
|
|
||||||
}
|
|
||||||
elsif($error > 0) {
|
|
||||||
# no further verification will occur
|
|
||||||
$timevrfyend{$testnum} = Time::HiRes::time();
|
|
||||||
# Submit the test case result with the CI environment
|
|
||||||
citest_finishtest($testnum, $error);
|
|
||||||
return $error;
|
|
||||||
}
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Verify that the test succeeded
|
|
||||||
$error = singletest_check($testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind);
|
|
||||||
if($error == -1) {
|
|
||||||
my $err = ignoreresultcode($testnum);
|
|
||||||
# Submit the test case result with the CI environment
|
|
||||||
citest_finishtest($testnum, $err);
|
|
||||||
# return a test failure, either to be reported or to be ignored
|
|
||||||
return $err;
|
|
||||||
}
|
|
||||||
elsif($error == -2) {
|
|
||||||
# torture test; there is no verification, so the run result holds the
|
|
||||||
# test success code
|
|
||||||
# Submit the test case result with the CI environment
|
|
||||||
citest_finishtest($testnum, $cmdres);
|
|
||||||
return $cmdres;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
|
||||||
# Report a successful test
|
|
||||||
singletest_success($testnum, $count, $total, ignoreresultcode($testnum));
|
|
||||||
|
|
||||||
# Submit the test case result with the CI environment
|
|
||||||
citest_finishtest($testnum, 0);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
|
@ -2522,48 +2560,77 @@ citest_starttestrun();
|
||||||
# Initialize the runner to prepare to run tests
|
# Initialize the runner to prepare to run tests
|
||||||
cleardir($LOGDIR);
|
cleardir($LOGDIR);
|
||||||
mkdir($LOGDIR, 0777);
|
mkdir($LOGDIR, 0777);
|
||||||
$runnerid = runner_init($LOGDIR);
|
my $runnerid = runner_init($LOGDIR);
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# The main test-loop
|
# The main test-loop
|
||||||
#
|
#
|
||||||
# run through each candidate test and execute it
|
# run through each candidate test and execute it
|
||||||
|
nexttest:
|
||||||
foreach my $testnum (@runtests) {
|
foreach my $testnum (@runtests) {
|
||||||
$count++;
|
$count++;
|
||||||
|
|
||||||
# execute one test case
|
# Loop over state machine waiting for singletest to complete
|
||||||
my $error = singletest($testnum, $count, scalar(@runtests));
|
my $again;
|
||||||
|
while () {
|
||||||
|
# check the abort flag
|
||||||
|
if($globalabort) {
|
||||||
|
logmsg "Aborting tests\n";
|
||||||
|
if($again) {
|
||||||
|
logmsg "Waiting for test to finish...\n";
|
||||||
|
# Wait for the last request to complete and throw it away so
|
||||||
|
# that IPC calls & responses stay in sync
|
||||||
|
# TODO: send a signal to the runner to interrupt a long test
|
||||||
|
runnerar();
|
||||||
|
}
|
||||||
|
last nexttest;
|
||||||
|
}
|
||||||
|
|
||||||
if($error < 0) {
|
# execute one test case
|
||||||
# not a test we can run
|
my $error;
|
||||||
next;
|
($error, $again) = singletest($runnerid, $testnum, $count, scalar(@runtests));
|
||||||
}
|
if($again) {
|
||||||
|
# Wait for asynchronous response
|
||||||
|
if(!runnerar_ready(0.05)) {
|
||||||
|
# TODO: If a response isn't ready, this is a chance to do
|
||||||
|
# something else first
|
||||||
|
}
|
||||||
|
next; # another iteration of the same singletest
|
||||||
|
}
|
||||||
|
|
||||||
$total++; # number of tests we've run
|
# Test has completed
|
||||||
|
if($error < 0) {
|
||||||
|
# not a test we can run
|
||||||
|
next nexttest;
|
||||||
|
}
|
||||||
|
|
||||||
if($error>0) {
|
$total++; # number of tests we've run
|
||||||
if($error==2) {
|
|
||||||
# ignored test failures
|
if($error>0) {
|
||||||
$failedign .= "$testnum ";
|
if($error==2) {
|
||||||
|
# ignored test failures
|
||||||
|
$failedign .= "$testnum ";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$failed.= "$testnum ";
|
||||||
|
}
|
||||||
|
if($postmortem) {
|
||||||
|
# display all files in $LOGDIR/ in a nice way
|
||||||
|
displaylogs($testnum);
|
||||||
|
}
|
||||||
|
if($error==2) {
|
||||||
|
$ign++; # ignored test result counter
|
||||||
|
}
|
||||||
|
elsif(!$anyway) {
|
||||||
|
# a test failed, abort
|
||||||
|
logmsg "\n - abort tests\n";
|
||||||
|
last;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
elsif(!$error) {
|
||||||
$failed.= "$testnum ";
|
$ok++; # successful test counter
|
||||||
}
|
}
|
||||||
if($postmortem) {
|
next nexttest;
|
||||||
# display all files in $LOGDIR/ in a nice way
|
|
||||||
displaylogs($testnum);
|
|
||||||
}
|
|
||||||
if($error==2) {
|
|
||||||
$ign++; # ignored test result counter
|
|
||||||
}
|
|
||||||
elsif(!$anyway) {
|
|
||||||
# a test failed, abort
|
|
||||||
logmsg "\n - abort tests\n";
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elsif(!$error) {
|
|
||||||
$ok++; # successful test counter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# loop for next test
|
# loop for next test
|
||||||
|
|
Загрузка…
Ссылка в новой задаче