#use strict;
use v5.10.1;
use warnings;
-use if $^V >= v5.19.11, experimental => 'smartmatch';
use Errno;
use FileHandle;
s/\bgid=\d+/gid=gggg/;
s/\begid=\d+/egid=gggg/;
s/\b(?:pid=|pid\s|PID:\s|Process\s|child\s)\K(\d+)/new_value($1, "p%s", \$next_pid)/gxe;
+ s/ Ci=\K(\d+)/new_value($1, "p%s", \$next_pid)/gxe;
s/\buid=\d+/uid=uuuu/;
s/\beuid=\d+/euid=uuuu/;
s/set_process_info:\s+\d+/set_process_info: pppp/;
# ======== IP error numbers and messages ========
# These vary between operating systems
- s/Can(no|')t assign requested address/Netwk addr not available/;
+ s/(?:Can(?:no|')t assign requested address|Address not available)/Netwk addr not available/;
s/Operation timed out/Connection timed out/;
s/Address family not supported by protocol family/Network Error/;
- s/Network( is)? unreachable/Network unreachable/;
+ s/Network(?: is)? unreachable/Network Error/;
s/Invalid argument/Network Error/;
s/\(\d+\): Network/(dd): Network/;
# DMARC is not always supported by the build
next if /^dmarc_tld_file =/;
+ # timestamp in dmarc history file
+ s/received \K\d{10}$/1692480217/;
# ARC is not always supported by the build
next if /^arc_sign =/;
# mailq times change with when the run is done, vs. static-source spoolfiles
s/\s*\d*[hd](?= 317 [0-9A-Za-z\-]{23} <nobody\@test.ex>)/DDd/;
+
+ # Not all builds include EXPERIMENTAL_DSN_INFO (1 of 2)
+ if (/^X-Exim-Diagnostic:/)
+ {
+ while (<IN>) {
+ last if (/^$/ || !/^\s/);
+ }
+ goto RESET_AFTER_EXTRA_LINE_READ;
+ }
}
# ======== stderr ========
s/ \.\.\. >>> / ... /;
if (s/ non-TFO mode connection attempt to 224.0.0.0, 0 data\b$//) { chomp; $_ .= <IN>; }
s/Address family not supported by protocol family/Network Error/;
- s/Network is unreachable/Network Error/;
+ s/Network(?: is)? unreachable/Network Error/;
}
next if /^(ppppp |\d+ )?setsockopt FASTOPEN: Protocol not available$/;
s/^(Connecting to .* \.\.\. sending) \d+ (nonTFO early-data)$/$1 dd $2/;
# OpenSSL version variances
s/(TLS error on connection [^:]*: error:)[0-9A-F]{8}(:system library):(?:fopen|func\(4095\)|):(No such file or directory)$/$1xxxxxxxx$2:fopen:$3/;
- next if /TLS error \(SSL_read\): error:0A000126:SSL routines::unexpected eof while reading$/ ;
+ next if /TLS error \(SSL_read\): .*error:0A000126:SSL routines::unexpected eof while reading$/ ;
s/EVDATA: \K\(SSL_accept\): error:0A000126:SSL routines::unexpected eof while reading/SSL_accept: TCP connection closed by peer/;
s/(DANE attempt failed.*error:)[0-9A-F]{8}(:SSL routines:)(?:(?i)ssl3_get_server_certificate|tls_process_server_certificate|CONNECT_CR_CERT|)(?=:certificate verify failed$)/$1xxxxxxxx$2ssl3_get_server_certificate/;
s/(DKIM: validation error: )error:[0-9A-F]{8}:rsa routines:(?:(?i)int_rsa_verify|CRYPTO_internal):(?:bad signature|algorithm mismatch)$/$1Public key signature verification has failed./;
<IN>;
<IN>;
}
+
+ # Not all builds include EXPERIMENTAL_DSN_INFO (2 of 2)
+ if (/^X-Exim-Diagnostic:/)
+ {
+ while (<IN>) {
+ last if (/^$/ || !/^\s/);
+ }
+ goto RESET_AFTER_EXTRA_LINE_READ;
+ }
}
# ======== All files other than stderr ========
for (my $i = 0; $i < @munged; $i++)
{
- if ($munged[$i] =~ /^[-\d]{10}\s[:\d]{8}\s[-A-Za-z\d]{16}\s[-=*]>/)
+ if ($munged[$i] =~ /^[-\d]{10}\s[:\d]{8}(\.\d{3})?\s[-A-Za-z\d]{23}\s[-=*]>/)
{
my $j;
for ($j = $i + 1; $j < @munged; $j++)
{
last if $munged[$j] !~
- /^[-\d]{10}\s[:\d]{8}\s[-A-Za-z\d]{16}\s[-=*]>/;
+ /^[-\d]{10}\s[:\d]{8}(\.\d{3})?\s[-A-Za-z\d]{23}\s[-=*]>/;
}
@temp = splice(@munged, $i, $j - $i);
@temp = sort(@temp);
# Arguments: the current test number
# reference to the subtest number, holding previous value
# reference to the expected return code value
+# reference to flag for not-expected return value
# reference to where to put the command name (for messages)
# auxiliary information returned from a previous run
#
sub run_command{
my($testno) = $_[0];
my($subtestref) = $_[1];
-my($commandnameref) = $_[3];
-my($aux_info) = $_[4];
+my($commandnameref) = $_[4];
+my($aux_info) = $_[5];
my($yield) = 1;
our %ENV = map { $_ => $ENV{$_} } grep { /^(?:USER|SHELL|PATH|TERM|EXIM_TEST_.*)$/ } keys %ENV;
-if (/^(\d+)\s*(?:([A-Z]+)=(\S+))?$/) # Handle unusual return code
+if (/^(~)?(\d+)\s*(?:([A-Z]+)=(\S+))?$/) # Handle unusual return code
{
- my($r) = $_[2];
- $$r = $1 << 8;
- $ENV{$2} = $3 if (defined $2);
+ my($r, $rn) = ($_[2], $_[3]);
+ $$r = $2 << 8;
+ $$rn = 1 if (defined $1);
+ $ENV{$3} = $4 if (defined $3);
$_ = <SCRIPT>;
return 4 if !defined $_; # Missing command
$lineno++;
run_system("$_ >>test-stdout 2>>test-stderr");
return 1;
}
+if (/^cat2\s/)
+ {
+ s/^cat2/cat/;
+ run_system("$_ 2>&1 >test-stderr");
+ return 1;
+ }
if (/^client/ || /^(sudo\s+)?perl\b/)
{
+ if (defined($tls)) {
+ s/^client-anytls/client-ssl/ if ($tls eq 'openssl');
+ s/^client-anytls/client-gnutls/ if ($tls eq 'gnutls');
+ }
s"client"./bin/client";
$cmd = "$_ >>test-stdout 2>>test-stderr";
}
if (defined $queuespec)
{
- @listcmd = ("$parm_cwd/eximdir/exim", '-bp',
+ @listcmd = ("$parm_cwd/$exim_server", '-bp',
$queuespec,
- "-DEXIM_PATH=$parm_cwd/eximdir/exim",
+ "-DEXIM_PATH=$parm_cwd$exim_server",
-C => "$parm_cwd/test-config");
}
else
{
- @listcmd = ("$parm_cwd/eximdir/exim", '-bp',
- "-DEXIM_PATH=$parm_cwd/eximdir/exim",
+ @listcmd = ("$parm_cwd/$exim_server", '-bp',
+ "-DEXIM_PATH=$parm_cwd/$exim_server",
-C => "$parm_cwd/test-config");
}
print ">> Getting queue list from:\n>> @listcmd\n" if $debug;
my $opt_valgrind = $valgrind ? "valgrind --leak-check=yes --suppressions=$parm_cwd/aux-fixed/valgrind.supp " : '';
- $cmd = "$envset$sudo$opt_valgrind" .
- "$parm_cwd/eximdir/exim$special$optargs " .
- "-DEXIM_PATH=$parm_cwd/eximdir/exim$special " .
- "-C $parm_cwd/test-config $args " .
+ $cmd = "$envset$sudo$opt_valgrind";
+
+ if ($special ne '') {
+ $cmd .= "$parm_cwd/eximdir/exim$special$optargs " .
+ "-DEXIM_PATH=$parm_cwd/eximdir/exim$special ";
+ }
+ elsif ($args =~ /(^|\s)-DSERVER=server\s/) {
+ $cmd .= "$parm_cwd/$exim_server$optargs " .
+ "-DEXIM_PATH=$parm_cwd/$exim_server ";
+ }
+ else {
+ $cmd .= "$parm_cwd/$exim_client$optargs " .
+ "-DEXIM_PATH=$parm_cwd/$exim_client ";
+ }
+
+ $cmd .= "-C $parm_cwd/test-config $args " .
">>test-stdout 2>>test-stderr";
+
# If the command is starting an Exim daemon, we run it in the same
# way as the "server" command above, that is, we don't want to wait
# for the process to finish. That happens when "killdaemon" is obeyed later
'ipv6!' => \$have_ipv6,
'keep' => \$save_output,
'slow' => \$slow,
+ 'tls=s' => \my $tls,
'valgrind' => \$valgrind,
'range=s{2}' => \my @range_wanted,
'test=i@' => \my @tests_wanted,
print "Exim binary is `$parm_exim'\n" if defined $parm_exim;
+my %wanted;
my @wanted = sort numerically uniq
@tests_wanted ? @tests_wanted : (),
@range_wanted ? $range_wanted[0] .. $range_wanted[1] : (),
0+$ARGV[0]..0+$ARGV[1] # add 0 to cope with test numbers starting with zero
: ();
@wanted = 1..TEST_TOP if not @wanted;
+map { $wanted{sprintf("%04d",$_)}= $_; } @wanted;
##################################################
# Check for sudo access to root #
die "** Unable to make patched exim: $!\n"
if (system("sudo ./patchexim $parm_exim") != 0);
+# If TLS-library-specific binaries have been made, grab them too
+
+$suff = 'openssl';
+$f = $parm_exim . '_' . $suff;
+if (-f $f) {
+ $exim_openssl = "eximdir/exim_$suff";
+ die "** Unable to make patched exim: $!\n"
+ if (system("sudo ./patchexim -o $exim_openssl $f") != 0);
+ }
+$suff = 'gnutls';
+$f = $parm_exim . '_' . $suff;
+if (-f $f) {
+ $exim_gnutls = "eximdir/exim_$suff";
+ die "** Unable to make patched exim: $!\n"
+ if (system("sudo ./patchexim -o $exim_gnutls $f") != 0);
+ }
+
+if (defined($tls))
+ {
+ die "** Need both $exim_openssl and $exim_gnutls for cross-library teting\n"
+ if ( !defined($exim_openssl) || !defined($exim_gnutls) );
+ if ($tls eq 'openssl')
+ {
+ $exim_client = $exim_openssl;
+ $exim_server = $exim_gnutls;
+ }
+ elsif ($tls eq 'gnutls')
+ {
+ $exim_client = $exim_gnutls;
+ $exim_server = $exim_openssl;
+ }
+ else
+ { die "** need eother openssl or gnutls speified as the client for cross-library testing, saw $tls\n"; }
+ }
+else
+ { $exim_client = $exim_server = 'eximdir/exim'; }
+print ">> \$exim_client <$exim_client>\n";;
+print ">> \$exim_server <$exim_server>\n";;
+
# From this point on, exits from the program must go via the subroutine
# tests_exit(), so that suitable cleaning up can be done when required.
# Arrange to catch interrupting signals, to assist with this.
# We want the tests from this subdirectory, provided they are in the
# range that was selected.
- @testlist = grep { $_ ~~ @wanted } grep { /^\d+(?:\.\d+)?$/ } map { basename $_ } glob "scripts/$testdir/*";
+ undef @testlist;
+ map { push @testlist, $_ if exists $wanted{$_} } grep { /^\d+(?:\.\d+)?$/ } map { basename $_ } glob "scripts/$testdir/*";
+
tests_exit(-1, "Failed to read test scripts from `scripts/$testdir/*': $!")
if not @testlist;
# was run and not waited for (usually a daemon or server startup).
my($commandname) = '';
- my($expectrc) = 0;
- my($rc, $run_extra) = run_command($testno, \$subtestno, \$expectrc, \$commandname, $TEST_STATE);
+ my($expectrc, $expect_not) = (0, 0);
+ my($rc, $run_extra) = run_command($testno, \$subtestno, \$expectrc, \$expect_not, \$commandname, $TEST_STATE);
my($cmdrc) = $?;
if ($debug) {
# We ran and waited for a command. Check for the expected result unless
# it died.
- if ($cmdrc != $expectrc && !$sigpipehappened)
+ if (!$sigpipehappened && ($expect_not ? ($cmdrc == $expectrc) : ($cmdrc != $expectrc)))
{
printf("** Command $commandno (\"$commandname\", starting at line $subtest_startline)\n");
if (($cmdrc & 0xff) == 0)
{
- printf("** Return code %d (expected %d)", $cmdrc/256, $expectrc/256);
+ if ($expect_not)
+ { printf("** Return code %d (expected anything but that)", $cmdrc/256); }
+ else
+ { printf("** Return code %d (expected %d)", $cmdrc/256, $expectrc/256); }
}
elsif (($cmdrc & 0xff00) == 0)
{ printf("** Killed by signal %d", $cmdrc & 255); }