#! /usr/bin/perl -w
-# $Cambridge: exim/test/runtest,v 1.12 2006/07/04 09:07:20 ph10 Exp $
-
###############################################################################
# This is the controlling script for the "new" test suite for Exim. It should #
# be possible to export this suite for running on a wide variety of hosts, in #
# Start by initializing some global variables
-$testversion = "4.62 (20-Apr-06)";
+$testversion = "4.72 (02-Jun-10)";
-$cf = "bin/cf";
+$cf = "bin/cf -exact";
$cr = "\r";
$debug = 0;
$force_update = 0;
sub do_substitute{
s?\bCALLER\b?$parm_caller?g;
+s?\bCALLERGROUP\b?$parm_caller_group?g;
s?\bCALLER_UID\b?$parm_caller_uid?g;
s?\bCALLER_GID\b?$parm_caller_gid?g;
s?\bCLAMSOCKET\b?$parm_clamsocket?g;
while(<IN>)
{
+RESET_AFTER_EXTRA_LINE_READ:
# Check for "*** truncated ***"
$yield = 1 if /\*\*\* truncated \*\*\*/;
s?\Q$parm_cwd\E?TESTSUITE?g;
# Replace the Exim version number (may appear in various places)
- s/Exim \d+\.\d+[\w-]*/Exim x.yz/i;
+ # patchexim should have fixed this for us
+ #s/(Exim) \d+\.\d+[\w_-]*/$1 x.yz/i;
# Replace Exim message ids by a unique series
s/((?:[^\W_]{6}-){2}[^\W_]{2})
s/TlRMTVNTUAACAAAAAAAAAAAoAAABgg[\w+\/]+/TlRMTVNTUAACAAAAAAAAAAAoAAABggAAAEbBRwqFwwIAAAAAAAAAAAAt1sgAAAAA/;
# PRVS values
- s?prvs=([^/]+)/[\da-f]{10}@?prvs=$1/xxxxxxxxxx@?g;
+ s?prvs=([^/]+)/[\da-f]{10}@?prvs=$1/xxxxxxxxxx@?g; # Old form
+ s?prvs=[\da-f]{10}=([^@]+)@?prvs=xxxxxxxxxx=$1@?g; # New form
# Error lines on stdout from SSL contain process id values and file names.
# They also contain a source file name and line number, which may vary from
s/^\d+:error:/pppp:error:/;
s/:(?:\/[^\s:]+\/)?([^\/\s]+\.c):\d+:/:$1:dddd:/;
+ # There are differences in error messages between OpenSSL versions
+ s/SSL_CTX_set_cipher_list/SSL_connect/;
+
# One error test in expansions mentions base 62 or 36
s/is not a base (36|62) number/is not a base 36\/62 number/;
# Random local part in callout cache testing
s/myhost.test.ex-\d+-testing/myhost.test.ex-dddddddd-testing/;
+ # File descriptor numbers may vary
+ s/^writing data block fd=\d+/writing data block fd=dddd/;
+ s/running as transport filter: write=\d+ read=\d+/running as transport filter: write=dddd read=dddd/;
+
# ======== Dumpdb output ========
# This must be before the general date/date munging.
# Date/time in exim -bV output
s/\d\d-[A-Z][a-z]{2}-\d{4}\s\d\d:\d\d:\d\d/07-Mar-2000 12:21:52/g;
+ # Time on queue tolerance
+ s/QT=1s/QT=0s/;
+
+ # Eximstats heading
+ s/Exim\sstatistics\sfrom\s\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d\sto\s
+ \d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d/Exim statistics from <time> to <time>/x;
- # ======== Caller's login, uid, gid, home ========
+
+ # ======== Caller's login, uid, gid, home, gecos ========
s/\Q$parm_caller_home\E/CALLER_HOME/g; # NOTE: these must be done
s/\b\Q$parm_caller\E\b/CALLER/g; # in this order!
s/\buid=$parm_caller_uid\b/uid=CALLER_UID/g;
s/\bgid=$parm_caller_gid\b/gid=CALLER_GID/g;
+ s/\bname=$parm_caller_gecos\b/name=CALLER_GECOS/g;
+
# When looking at spool files with -Mvh, we will find not only the caller
# login, but also the uid and gid. It seems that $) in some Perls gives all
# the auxiliary gids as well, so don't bother checking for that.
# ======== Exim's login ========
- # For bounce messages, this will appear on the U= lines in logs and also
- # after Received: and in addresses. In one pipe test it appears after
- # "Running as:". It also appears in addresses, and in the names of lock
+ # For messages received by the daemon, this is in the -H file, which some
+ # tests inspect. For bounce messages, this will appear on the U= lines in
+ # logs and also after Received: and in addresses. In one pipe test it appears
+ # after "Running as:". It also appears in addresses, and in the names of lock
# files.
s/U=$parm_eximuser/U=EXIMUSER/;
s/\buid=$parm_exim_uid\b/uid=EXIM_UID/g;
s/\bgid=$parm_exim_gid\b/gid=EXIM_GID/g;
+ s/^$parm_eximuser $parm_exim_uid $parm_exim_gid/EXIMUSER EXIM_UID EXIM_GID/;
+
# ======== General uids, gids, and pids ========
# Note: this must come after munges for caller's and exim's uid/gid
+ # These are for systems where long int is 64
+ s/\buid=4294967295/uid=-1/;
+ s/\beuid=4294967295/euid=-1/;
+ s/\bgid=4294967295/gid=-1/;
+ s/\begid=4294967295/egid=-1/;
+
s/\bgid=\d+/gid=gggg/;
s/\begid=\d+/egid=gggg/;
s/\bpid=\d+/pid=pppp/;
# Pid in temp file in appendfile transport
s"test-mail/temp\.\d+\."test-mail/temp.pppp.";
+ # Optional pid in log lines
+ s/^(\d{4}-\d\d-\d\d\s\d\d:\d\d:\d\d)(\s[+-]\d\d\d\d|)(\s\[\d+\])/
+ "$1$2 [" . new_value($3, "%s", \$next_pid) . "]"/gxe;
+
# Detect a daemon stderr line with a pid and save the pid for subsequent
# removal from following lines.
$spid = $1 if /^(\s*\d+) (?:listening|LOG: MAIN|(?:daemon_smtp_port|local_interfaces) overridden by)/;
s/\(port=(\d+)/"(port=" . new_value($1, "%s", \$next_port)/e;
# This handles "connection from" and the like, when the port is given
- if (!/listening for SMTP on/ && !/Connecting to/ && !/=>/ && !/\*>/ &&
- !/Connection refused/)
+ if (!/listening for SMTP on/ && !/Connecting to/ && !/=>/ && !/->/
+ && !/\*>/ && !/Connection refused/)
{
s/\[([a-z\d:]+|\d+(?:\.\d+){3})\]:(\d+)/"[".$1."]:".new_value($2,"%s",\$next_port)/ie;
}
# The amount of space between "host" and the address in verification output
# depends on the length of the host name. We therefore reduce it to one space
# for all of them.
+ # Also, the length of space at the end of the host line is dependent
+ # on the length of the longest line, so strip it also on otherwise
+ # un-rewritten lines like localhost
s/^\s+host\s(\S+)\s+(\S+)/ host $1 $2/;
s/^\s+(host\s\S+\s\S+)\s+(port=.*)/ host $1 $2/;
s/host\s\Q$parm_ipv4\E\s\[\Q$parm_ipv4\E\]/host ipv4.ipv4.ipv4.ipv4 [ipv4.ipv4.ipv4.ipv4]/;
s/host\s\Q$parm_ipv6\E\s\[\Q$parm_ipv6\E\]/host ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6 [ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6]/;
s/\b\Q$parm_ipv4\E\b/ip4.ip4.ip4.ip4/g;
- s/\b\Q$parm_ipv6\E\b/ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6/g;
+ s/(^|\W)\K\Q$parm_ipv6\E/ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6/g;
s/\b\Q$parm_ipv4r\E\b/ip4-reverse/g;
- s/\b\Q$parm_ipv6r\E\b/ip6-reverse/g;
+ s/(^|\W)\K\Q$parm_ipv6r\E/ip6-reverse/g;
+ s/^(\s+host\s\S+\s+\[\S+\]) +$/$1 /;
# ======== Test network IP addresses ========
# ======== Output from ls ========
# Different operating systems use different spacing on long output
- s/ +/ /g if /^[-rwd]{10} /;
+ #s/ +/ /g if /^[-rwd]{10} /;
+ # (Bug 1226) SUSv3 allows a trailing printable char for modified access method control.
+ # Handle only the Gnu and MacOS space, dot, plus and at-sign. A full [[:graph:]]
+ # unfortunately matches a non-ls linefull of dashes.
+ # Allow the case where we've already picked out the file protection bits.
+ if (s/^([-d](?:[-r][-w][-SsTtx]){3})[.+@]?( +|$)/$1$2/) {
+ s/ +/ /g;
+ }
# ======== Message sizes =========
s/([\s,])S=\d+\b/$1S=sss/;
s/:S\d+\b/:Ssss/;
s/^(\s*\d+m\s+)\d+(\s+[a-z0-9-]{16} <)/$1sss$2/i if $is_stdout;
- s/\sSIZE=\d+\b/ SIZE=ssss/ if $is_stderr || $is_stdout;
+ s/\sSIZE=\d+\b/ SIZE=ssss/;
s/\ssize=\d+\b/ size=sss/ if $is_stderr;
s/old size = \d+\b/old size = sssss/;
s/message size = \d+\b/message size = sss/;
s/\+0100,\d+;/+0100,ddd;/;
s/\(\d+ bytes written\)/(ddd bytes written)/;
s/added '\d+ 1'/added 'ddd 1'/;
+ s/Received\s+\d+/Received nnn/;
+ s/Delivered\s+\d+/Delivered nnn/;
# ======== Values in spool space failure message ========
- s/space=\d+ inodes=\d+/space=xxxxx inodes=xxxxx/;
+ s/space=\d+ inodes=[+-]?\d+/space=xxxxx inodes=xxxxx/;
# ======== Filter sizes ========
s/^\d\d\d(?=[PFS*])/ddd/;
+ # ========= Exim lookups ==================
+ # Lookups have a char which depends on the number of lookup types compiled in,
+ # in stderr output. Replace with a "0". Recognising this while avoiding
+ # other output is fragile; perhaps the debug output should be revised instead.
+ s%(?<!sqlite)(?<!lsearch\*@)(?<!lsearch\*)(?<!lsearch)[0-?]TESTSUITE/aux-fixed/%0TESTSUITE/aux-fixed/%g;
+
# ==========================================================
# Some munging is specific to the specific file types
s/\bgethostbyname2?|\bgetipnodebyname/get[host|ipnode]byname[2]/;
+ # drop gnutls version strings
+ next if /GnuTLS compile-time version: \d+[\.\d]+$/;
+ next if /GnuTLS runtime version: \d+[\.\d]+$/;
+
+ # drop openssl version strings
+ next if /OpenSSL compile-time version: OpenSSL \d+[\.\da-z]+/;
+ next if /OpenSSL runtime version: OpenSSL \d+[\.\da-z]+/;
+
+ # drop lookups
+ next if /^Lookups \(built-in\):/;
+ next if /^Loading lookup modules from/;
+ next if /^Loaded \d+ lookup modules/;
+ next if /^Total \d+ lookups/;
+
+ # drop compiler information
+ next if /^Compiler:/;
+
+ # and the ugly bit
+ # different libraries will have different numbers (possibly 0) of follow-up
+ # lines, indenting with more data
+ if (/^Library version:/) {
+ while (1) {
+ $_ = <IN>;
+ next if /^\s/;
+ goto RESET_AFTER_EXTRA_LINE_READ;
+ }
+ }
+
+ # drop other build-time controls emitted for debugging
+ next if /^WHITELIST_D_MACROS:/;
+ next if /^TRUSTED_CONFIG_LIST:/;
+
+ # As of Exim 4.74, we log when a setgid fails; because we invoke Exim
+ # with -be, privileges will have been dropped, so this will always
+ # be the case
+ next if /^changing group to \d+ failed: Operation not permitted/;
+
+ # We invoke Exim with -D, so we hit this new messag as of Exim 4.73:
+ next if /^macros_trusted overridden to true by whitelisting/;
+
# We have to omit the localhost ::1 address so that all is well in
# the IPv4-only case.
if (/looked up these IP addresses/);
next if /name=localhost address=::1/;
+ # drop pdkim debugging header
+ next if /^PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<+$/;
+
# Various other IPv6 lines must be omitted too
next if /using host_fake_gethostbyname for \S+ \(IPv6\)/;
# [4] TRUE if this is a log file whose deliveries must be sorted
#
# Returns: 0 comparison succeeded or differences to be ignored
-# 1 comparison failed; files were updated (=> re-compare)
+# 1 comparison failed; files may have been updated (=> re-compare)
#
# Does not return if the user replies "Q" to a prompt.
print "\n";
print "------------ $f -----------\n"
if (defined $rf && -s $rf && defined $rsf && -s $rsf);
- system("$more $f");
+ system("$more '$f'");
}
}
# Do the comparison
- return 0 if (system("$cf $mf $sf >test-cf") == 0);
+ return 0 if (system("$cf '$mf' '$sf' >test-cf") == 0);
# Handle comparison failure
print "\n";
for (;;)
{
- interact("Continue, Update & retry, Quit? [Q] ", $force_update);
+ interact("Continue, Retry, Update & retry, Quit? [Q] ", $force_update);
tests_exit(1) if /^q?$/i;
return 0 if /^c$/i;
+ return 1 if /^r$/i;
last if (/^u$/i);
}
}
# Update or delete the saved file, and give the appropriate return code.
if (-s $mf)
- { tests_exit(-1, "Failed to cp $mf $sf") if system("cp $mf $sf") != 0; }
+ { tests_exit(-1, "Failed to cp $mf $sf") if system("cp '$mf' '$sf'") != 0; }
else
{ tests_exit(-1, "Failed to unlink $sf") if !unlink($sf); }
#
# Arguments: none
# Returns: 0 if the output compared equal
-# 1 if files were updated and the test must be re-run
+# 1 if re-run needed (files may have been updated)
sub check_output{
my($yield) = 0;
# This gives the process time to get started; otherwise the next
# process may not find it there when it expects it.
- select(undef, undef, undef, 0.01);
+ select(undef, undef, undef, 0.1);
return 3;
}
}
+# The "exigrep" command runs exigrep on the current mainlog
+
+if (/^exigrep\s+(.*)/)
+ {
+ run_system("(./eximdir/exigrep " .
+ "$1 $parm_cwd/spool/log/mainlog;" .
+ "echo exigrep exit code = \$?)" .
+ ">>test-stdout");
+ return 1;
+ }
+
+
+# The "eximstats" command runs eximstats on the current mainlog
+
+if (/^eximstats\s+(.*)/)
+ {
+ run_system("(./eximdir/eximstats " .
+ "$1 $parm_cwd/spool/log/mainlog;" .
+ "echo eximstats exit code = \$?)" .
+ ">>test-stdout");
+ return 1;
+ }
+
+
# The "gnutls" command makes a copy of saved GnuTLS parameter data in the
# spool directory, to save Exim from re-creating it each time.
# This gives the server time to get started; otherwise the next
# process may not find it there when it expects it.
- select(undef, undef, undef, 0.01);
+ select(undef, undef, undef, 0.5);
return 3;
}
# command in the variable $cmd. Shared code to run this command and handle its
# input and output follows.
-# The "client" and "client-ssl" commands run a script-driven program that plays
-# the part of an email client. We also have the availability of running Perl
-# for doing one-off special things. Note that all these commands expect stdin
-# data to be supplied.
+# The "client", "client-gnutls", and "client-ssl" commands run a script-driven
+# program that plays the part of an email client. We also have the availability
+# of running Perl for doing one-off special things. Note that all these
+# commands expect stdin data to be supplied.
-if (/^client/ || /^client-ssl/ || /^(sudo\s+)?perl\b/)
+if (/^client/ || /^(sudo\s+)?perl\b/)
{
s"client"./bin/client";
$cmd = "$_ >>test-stdout 2>>test-stderr";
# Before running the command, convert the -bd option into -bdf so that an
# Exim daemon doesn't double fork. This means that when we wait close
- # DAEMONCMD, it waits for the correct process.
+ # DAEMONCMD, it waits for the correct process. Also, ensure that the pid
+ # file is written to the spool directory, in case the Exim binary was
+ # built with PID_FILE_PATH pointing somewhere else.
- $cmd =~ s/\s-bd\s/ -bdf /;
+ $cmd =~ s!\s-bd\s! -bdf -oP $parm_cwd/spool/exim-daemon.pid !;
print ">> |${cmd}-server\n" if ($debug);
open DAEMONCMD, "|${cmd}-server" || tests_exit(-1, "Failed to run $cmd");
DAEMONCMD->autoflush(1);
print "Exim tester $testversion\n";
+##################################################
+# Some tests check created file modes #
+##################################################
+
+umask 022;
+
+
##################################################
# Check for the "less" command #
##################################################
# Find what is in the binary #
##################################################
-open(EXIMINFO, "$parm_exim -C confs/0000 -DDIR=$parm_cwd " .
+# deal with TRUSTED_CONFIG_LIST restrictions
+unlink("$parm_cwd/test-config") if -e "$parm_cwd/test-config";
+symlink("$parm_cwd/confs/0000", "$parm_cwd/test-config")
+ or die "Unable to link initial config into place: $!\n";
+
+print("Probing with config file: $parm_cwd/test-config\n");
+open(EXIMINFO, "$parm_exim -d -C $parm_cwd/test-config -DDIR=$parm_cwd " .
"-bP exim_user exim_group|") ||
die "** Cannot run $parm_exim: $!\n";
while(<EXIMINFO>)
if ($parm_eximuser =~ /^\d+$/) { $parm_exim_uid = $parm_eximuser; }
else { $parm_exim_uid = getpwnam($parm_eximuser); }
}
+else
+ {
+ print "Unable to extract exim_user from binary.\n";
+ print "Check if Exim refused to run; if so, consider:\n";
+ print " TRUSTED_CONFIG_LIST ALT_CONFIG_PREFIX WHITELIST_D_MACROS\n";
+ die "Failing to get information from binary.\n";
+ }
if (defined $parm_eximgroup)
{
else { $parm_exim_gid = getgrnam($parm_eximgroup); }
}
-open(EXIMINFO, "$parm_exim -bV -C confs/0000 -DDIR=$parm_cwd |") ||
+open(EXIMINFO, "$parm_exim -bV -C $parm_cwd/test-config -DDIR=$parm_cwd |") ||
die "** Cannot run $parm_exim: $!\n";
print "-" x 78, "\n";
elsif (/^Size of off_t: (\d+)/)
{
+ print;
$have_largefiles = 1 if $1 > 4;
+ die "** Size of off_t > 32 which seems improbable, not running tests\n"
+ if ($1 > 32);
}
elsif (/^Support for: (.*)/)
%parm_support = @temp;
}
- elsif (/^Lookups: (.*)/)
+ elsif (/^Lookups \(built-in\): (.*)/)
{
print;
@temp = split /(\s+)/, $1;
close(EXIMINFO);
print "-" x 78, "\n";
+unlink("$parm_cwd/test-config");
##################################################
# Check for SpamAssassin and ClamAV #
{
if (system("spamc -h 2>/dev/null >/dev/null") == 0)
{
- $parm_running{'SpamAssassin'} = ' ';
print "The spamc command works:\n";
# This test for an active SpamAssassin is courtesy of John Jetmore.
if($@)
{
- warn $@;
+ print " $@";
print " Assume ClamAV is not running\n";
}
else
# Find the caller of this program.
($parm_caller,$pwpw,$parm_caller_uid,$parm_caller_gid,$pwquota,$pwcomm,
- $pwgecos, $parm_caller_home) = getpwuid($>);
+ $parm_caller_gecos, $parm_caller_home) = getpwuid($>);
$pwpw = $pwpw; # Kill Perl warnings
$pwquota = $pwquota;
$pwcomm = $pwcomm;
-$pwgecos = $pwgecos;
$parm_caller_group = getgrgid($parm_caller_gid);
print "Program caller is $parm_caller, whose group is $parm_caller_group\n";
print "Home directory is $parm_caller_home\n";
+unless (defined $parm_eximgroup)
+ {
+ print "Unable to derive \$parm_eximgroup.\n";
+ die "** ABANDONING.\n";
+ }
+
print "You need to be in the Exim group to run these tests. Checking ...";
if (`groups` =~ /\b\Q$parm_eximgroup\E\b/)
$parm_ipv4r = ($parm_ipv4 !~ /^\d/)? "" :
join(".", reverse(split /\./, $parm_ipv4));
-$parm_ipv6r = "";
+$parm_ipv6r = $parm_ipv6; # Appropriate if not in use
if ($parm_ipv6 =~ /^[\da-f]/)
{
my(@comps) = split /:/, $parm_ipv6;
tests_exit(-1, "Failed to make a copy of exinext: $!");
}
+if (system("cp $parm_exim_dir/exigrep eximdir") != 0)
+ {
+ tests_exit(-1, "Failed to make a copy of exigrep: $!");
+ }
+
+if (system("cp $parm_exim_dir/eximstats eximdir") != 0)
+ {
+ tests_exit(-1, "Failed to make a copy of eximstats: $!");
+ }
+
##################################################
# Check that the Exim user can access stuff #
print "Exim user is $parm_eximuser ($parm_exim_uid)\n";
print "Exim group is $parm_eximgroup ($parm_exim_gid)\n";
+
+if ($parm_caller_uid eq $parm_exim_uid) {
+ tests_exit(-1, "Exim user ($parm_eximuser,$parm_exim_uid) cannot be "
+ ."the same as caller ($parm_caller,$parm_caller_uid)");
+}
+
print "The Exim user needs access to the test suite directory. Checking ...";
if (($rc = system("sudo bin/checkaccess $parm_cwd/eximdir/exim $parm_eximuser $parm_eximgroup")) != 0)
if ($have_ipv6 && $parm_ipv6 ne "::1")
{
- my(@components) = split /:/, $parm_ipv6;
+ my($exp_v6) = $parm_ipv6;
+ $exp_v6 =~ s/[^:]//g;
+ if ( $parm_ipv6 =~ /^([^:].+)::$/ ) {
+ $exp_v6 = $1 . ':0' x (9-length($exp_v6));
+ } elsif ( $parm_ipv6 =~ /^(.+)::(.+)$/ ) {
+ $exp_v6 = $1 . ':0' x (8-length($exp_v6)) . ':' . $2;
+ } elsif ( $parm_ipv6 =~ /^::(.+[^:])$/ ) {
+ $exp_v6 = '0:' x (9-length($exp_v6)) . $1;
+ }
+ my(@components) = split /:/, $exp_v6;
my(@nibbles) = reverse (split /\s*/, shift @components);
my($sep) = "";
undef %cache;
$next_msgid = "aX";
+ $next_pid = 1234;
$next_port = 1111;
$message_skip = 0;
$msglog_skip = 0;
for (;;)
{
- print "\nshow stdErr, show stdOut, Continue (without file comparison), or Quit? [Q] ";
+ print "\nshow stdErr, show stdOut, Retry, Continue (without file comparison), or Quit? [Q] ";
$_ = <T>;
tests_exit(1) if /^q?$/i;
- last if /^c$/i;
+ last if /^[rc]$/i;
if (/^e$/i)
{
system("$more test-stderr");
}
}
+ $retry = 1 if /^r$/i;
$docheck = 0;
}
for (;;)
{
- print "\nShow server stdout, Continue, or Quit? [Q] ";
+ print "\nShow server stdout, Retry, Continue, or Quit? [Q] ";
$_ = <T>;
tests_exit(1) if /^q?$/i;
- last if /^c$/i;
+ last if /^[rc]$/i;
if (/^s$/i)
{
close(S);
}
}
+ $retry = 1 if /^r$/i;
}
}
}
# function returns 0 if all is well, 1 if we should rerun the test (the files
# have been updated). It does not return if the user responds Q to a prompt.
+ if ($retry)
+ {
+ $retry = '0';
+ print (("#" x 79) . "\n");
+ redo;
+ }
+
if ($docheck)
{
if (check_output() != 0)
tests_exit(0);
# End of runtest script
-
+# vim: set sw=2 :