X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/9bfc60ebf1f86a212280c19a28bb4399e8fbb392..48dd94fd0c056591cba0935c97ce9b0481d00792:/test/runtest diff --git a/test/runtest b/test/runtest index 4a4427839..fa38ca019 100755 --- a/test/runtest +++ b/test/runtest @@ -14,11 +14,13 @@ ############################################################################### #use strict; -require Cwd; use Errno; use FileHandle; use Socket; use Time::Local; +use Cwd; +use File::Basename; +use if $ENV{DEBUG} && $ENV{DEBUG} =~ /\bruntest\b/ => ('Smart::Comments' => '####'); # Start by initializing some global variables @@ -36,6 +38,7 @@ $gnutls_dh_bits_normal = 2236; $cf = "bin/cf -exact"; $cr = "\r"; $debug = 0; +$flavour = 'FOO'; $force_continue = 0; $force_update = 0; $log_failed_filename = "failed-summary.log"; @@ -43,6 +46,7 @@ $more = "less -XF"; $optargs = ""; $save_output = 0; $server_opts = ""; +$valgrind = 0; $have_ipv4 = 1; $have_ipv6 = 1; @@ -80,6 +84,10 @@ $parm_port_d4 = 1228; # Additional for daemon # Manually set locale $ENV{'LC_ALL'} = 'C'; +# In some environments USER does not exists, but we +# need it for some test(s) +$ENV{USER} = getpwuid($>) + if not exists $ENV{USER}; ############################################################################### @@ -320,6 +328,8 @@ my($extra) = $_[1]; my($yield) = 0; my(@saved) = (); +local $_; + open(IN, "$file") || tests_exit(-1, "Failed to open $file: $!"); my($is_log) = $file =~ /log/; @@ -339,7 +349,7 @@ $spid = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; # that are specific to certain file types, though there are also some of those # inline too. -while() +LINE: while() { RESET_AFTER_EXTRA_LINE_READ: # Custom munges @@ -704,6 +714,9 @@ RESET_AFTER_EXTRA_LINE_READ: # ======== Other error numbers ======== s/errno=\d+/errno=dd/g; + # ======== System Error Messages ====== + # depending on the underlaying file system the error message seems to differ + s/(?: is not a regular file)|(?: has too many links \(\d+\))/ not a regular file or too many links/; # ======== Output from ls ======== # Different operating systems use different spacing on long output @@ -732,8 +745,8 @@ RESET_AFTER_EXTRA_LINE_READ: s/this message = \d+\b/this message = sss/; s/Size of headers = \d+/Size of headers = sss/; s/sum=(?!0)\d+/sum=dddd/; - s/(?<=sum=dddd )count=(?!0)\d+\b/count=dd/; - s/(?<=sum=0 )count=(?!0)\d+\b/count=dd/; + s/(?<=sum=dddd )count=\d+\b/count=dd/; + s/(?<=sum=0 )count=\d+\b/count=dd/; s/,S is \d+\b/,S is ddddd/; s/\+0100,\d+;/+0100,ddd;/; s/\(\d+ bytes written\)/(ddd bytes written)/; @@ -759,6 +772,7 @@ RESET_AFTER_EXTRA_LINE_READ: # different wording in the error messages, so we cannot compare them. s/(TLS error on connection (?:from .* )?\(SSL_\w+\): error:)(.*)/$1 <>/; + next if /SSL verify error: depth=0 error=certificate not trusted/; # ======== Maildir things ======== # timestamp output in maildir processing @@ -845,6 +859,11 @@ RESET_AFTER_EXTRA_LINE_READ: next if /^SSL info: unknown state/; next if /^SSL info: SSLv2\/v3 write client hello A/; next if /^SSL info: SSLv3 read server key exchange A/; + next if /SSL verify error: depth=0 error=certificate not trusted/; + s/SSL3_READ_BYTES/ssl3_read_bytes/; + + # gnutls version variances + next if /^Error in the pull function./; } # ======== stderr ======== @@ -862,7 +881,7 @@ RESET_AFTER_EXTRA_LINE_READ: # IP address lookups use gethostbyname() when IPv6 is not supported, # and gethostbyname2() or getipnodebyname() when it is. - s/\bgethostbyname2?|\bgetipnodebyname/get[host|ipnode]byname[2]/; + s/\b(gethostbyname2?|\bgetipnodebyname)(\(af=inet\))?/get[host|ipnode]byname[2]/; # drop gnutls version strings next if /GnuTLS compile-time version: \d+[\.\d]+$/; @@ -940,7 +959,7 @@ RESET_AFTER_EXTRA_LINE_READ: # are unset, because tls ain't always there. next if /in\s(?:tls_advertise_hosts\?|hosts_require_tls\?) - \sno\s\(option\sunset\)/x; + \sno\s\((option\sunset|end\sof\slist)\)/x; # Skip auxiliary group lists because they will vary. @@ -985,6 +1004,23 @@ RESET_AFTER_EXTRA_LINE_READ: while () { last if !/^\s/; } } + # remote port numbers vary + s/(Connection request from 127.0.0.1 port) \d{1,5}/$1 sssss/; + + # Skip hosts_require_dane checks when the options + # are unset, because dane ain't always there. + + next if /in\shosts_require_dane\?\sno\s\(option\sunset\)/x; + + # SUPPORT_PROXY + next if /host in hosts_proxy\?/; + + # Experimental_International + next if / in smtputf8_advertise_hosts\? no \(option unset\)/; + + # Environment cleaning + next if /\w+ in keep_environment\? (yes|no)/; + # When Exim is checking the size of directories for maildir, it uses # the check_dir_size() function to scan directories. Of course, the order # of the files that are obtained using readdir() varies from system to @@ -1006,14 +1042,6 @@ RESET_AFTER_EXTRA_LINE_READ: @saved = (); } - # Skip hosts_require_dane checks when the options - # are unset, because dane ain't always there. - - next if /in\shosts_require_dane\?\sno\s\(option\sunset\)/x; - - # Experimental_International - next if / in smtputf8_advertise_hosts\? no \(option unset\)/; - # Skip some lines that Exim puts out at the start of debugging output # because they will be different in different binaries. @@ -1064,7 +1092,7 @@ return $yield; # Arguments: [0] the prompt string # [1] if there is a U in the prompt and $force_update is true # [2] if there is a C in the prompt and $force_continue is true -# Returns: nothing (it sets $_) +# Returns: returns the answer sub interact{ print $_[0]; @@ -1127,7 +1155,13 @@ my($rf,$rsf,$mf,$sf,$sortfile,$extra) = @_; # If there is no saved file, the raw files must either not exist, or be # empty. The test ! -s is TRUE if the file does not exist or is empty. -if (! -e $sf) +# we check if there is a flavour specific file, but we remember +# the original file name as "generic" +$sf_generic = $sf; +$sf_flavour = "$sf_generic.$flavour"; +$sf_current = -e $sf_flavour ? $sf_flavour : $sf_generic; + +if (! -e $sf_current) { return 0 if (! -s $rf && (! defined $rsf || ! -s $rsf)); @@ -1167,6 +1201,8 @@ if (! -e $sf) } } +#### $_ + # Control reaches here if either (a) there is a saved file ($sf), or (b) there # was a request to create a saved file. First, create the munged file from any # data that does exist. @@ -1194,7 +1230,7 @@ close(MUNGED); # a result of parallel deliveries. We load the munged file and sort sequences # of delivery lines. -if (-e $sf) +if (-e $sf_current) { # Deal with truncated text items @@ -1205,7 +1241,7 @@ if (-e $sf) open(MUNGED, "$mf") || tests_exit(-1, "Failed to open $mf: $!"); @munged = ; close(MUNGED); - open(SAVED, "$sf") || tests_exit(-1, "Failed to open $sf: $!"); + open(SAVED, $sf_current) || tests_exit(-1, "Failed to open $sf_current: $!"); @saved = ; close(SAVED); @@ -1267,31 +1303,46 @@ if (-e $sf) # Do the comparison - return 0 if (system("$cf '$mf' '$sf' >test-cf") == 0); + return 0 if (system("$cf '$mf' '$sf_current' >test-cf") == 0); # Handle comparison failure - print "** Comparison of $mf with $sf failed"; + print "** Comparison of $mf with $sf_current failed"; system("$more test-cf"); print "\n"; for (;;) { - interact("Continue, Retry, Update & retry, Quit? [Q] ", $force_update, $force_continue); + interact("Continue, Retry, Update current" + . ($sf_current ne $sf_flavour ? "/Save for flavour '$flavour'" : "") + . " & retry, Quit? [Q] ", $force_update, $force_continue); tests_exit(1) if /^q?$/i; - log_failure($log_failed_filename, $testno, $sf) if (/^c$/i && $force_continue); + log_failure($log_failed_filename, $testno, $sf_current) if (/^c$/i && $force_continue); return 0 if /^c$/i; return 1 if /^r$/i; - last if (/^u$/i); + last if (/^[us]$/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; } + { + my $sf = /^u/i ? $sf_current : $sf_flavour; + 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); } + { + # if we deal with a flavour file, we can't delete it, because next time the generic + # file would be used again + if ($sf_current eq $sf_flavour) { + open(FOO, ">$sf_current"); + close(FOO); + } + else { + tests_exit(-1, "Failed to unlink $sf_current") if !unlink($sf_current); + } + } return 1; } @@ -1324,15 +1375,45 @@ $munges = 'optional_ocsp' => { 'stderr' => '/127.0.0.1 in hosts_requ(ire|est)_ocsp/' }, - 'no_tpt_filter_epipe' => - { 'stderr' => '/^writing error 32: Broken pipe$/' }, - 'optional_cert_hostnames' => { 'stderr' => '/in tls_verify_cert_hostnames\? no/' }, 'loopback' => { 'stdout' => 's/[[](127\.0\.0\.1|::1)]/[IP_LOOPBACK_ADDR]/' }, + 'scanfile_size' => + { 'stdout' => 's/(Content-length:) \d\d\d/$1 ddd/' }, + + 'delay_1500' => + { 'stderr' => 's/(1[5-9]|23\d)\d\d msec/ssss msec/' }, + + 'tls_anycipher' => + { 'mainlog' => 's/ X=TLS\S+ / X=TLS_proto_and_cipher /' }, + + 'debug_pid' => + { 'stderr' => 's/(^\s{0,4}|(?<=Process )|(?<=child ))\d{1,5}/ppppp/g' }, + + 'optional_dsn_info' => + { 'mail' => '/^(X-(Remote-MTA-(smtp-greeting|helo-response)|Exim-Diagnostic|(body|message)-linecount):|Remote-MTA: X-ip;)/' + }, + + 'optional_config' => + { 'stdout' => '/^( + dkim_(canon|domain|private_key|selector|sign_headers|strict) + |gnutls_require_(kx|mac|protocols) + |hosts_(requ(est|ire)|try)_(dane|ocsp) + |hosts_(avoid|nopass|require|verify_avoid)_tls + |socks_proxy + |tls_[^ ]* + )($|[ ]=)/x' }, + + 'sys_bindir' => + { 'mainlog' => 's%/(usr/)?bin/%SYSBINDIR/%' }, + + 'sync_check_data' => + { 'mainlog' => 's/^(.* SMTP protocol synchronization error .* next input=.{8}).*$/$1/', + 'rejectlog' => 's/^(.* SMTP protocol synchronization error .* next input=.{8}).*$/$1/'}, + }; @@ -1608,6 +1689,8 @@ my($commandnameref) = $_[3]; my($aux_info) = $_[4]; my($yield) = 1; +our %ENV = map { $_ => $ENV{$_} } grep { /^(?:USER|SHELL|PATH|TERM|EXIM_TEST_.*)$/ } keys %ENV; + if (/^(\d+)\s*$/) # Handle unusual return code { my($r) = $_[2]; @@ -1873,7 +1956,7 @@ if (/^sleep\s+(.*)$/) # Various Unix management commands are recognized if (/^(ln|ls|du|mkdir|mkfifo|touch|cp|cat)\s/ || - /^sudo (rmdir|rm|chown|chmod)\s/) + /^sudo\s(rmdir|rm|chown|chmod)\s/) { run_system("$_ >>test-stdout 2>>test-stderr"); return 1; @@ -1894,10 +1977,12 @@ if (/^(ln|ls|du|mkdir|mkfifo|touch|cp|cat)\s/ || # command, triggered by $server_pid being non-zero. The server sends its output # to a different file. The variable $server_opts, if not empty, contains # options to disable IPv4 or IPv6 if necessary. +# This works because "server" swallows its stdin before waiting for a connection. if (/^server\s+(.*)$/) { - $cmd = "./bin/server $server_opts $1 >>test-stdout-server"; + $pidfile = "$parm_cwd/aux-var/server-daemon.pid"; + $cmd = "./bin/server $server_opts -oP $pidfile $1 >>test-stdout-server"; print ">> $cmd\n" if ($debug); $server_pid = open SERVERCMD, "|$cmd" || tests_exit(-1, "Failed to run $cmd"); SERVERCMD->autoflush(1); @@ -1911,10 +1996,9 @@ if (/^server\s+(.*)$/) print SERVERCMD "++++\n"; # Send end to server; can't send EOF yet # because close() waits for the process. - # This gives the server time to get started; otherwise the next + # Interlock the server startup; otherwise the next # process may not find it there when it expects it. - - select(undef, undef, undef, 0.5); + while (! stat("$pidfile") ) { select(undef, undef, undef, 0.3); } return 3; } @@ -2011,12 +2095,12 @@ if (/^client/ || /^(sudo\s+)?perl\b/) # not drop privilege when -C and -D options are present. To run the exim # command as root, we use sudo. -elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/) +elsif (/^((?i:[A-Z\d_]+=\S+\s+)+)?(\d+)?\s*(sudo(?:\s+-u\s+(\w+))?\s+)?exim(_\S+)?\s+(.*)$/) { - $args = $5; + $args = $6; my($envset) = (defined $1)? $1 : ""; - my($sudo) = (defined $3)? "sudo " : ""; - my($special)= (defined $4)? $4 : ""; + my($sudo) = (defined $3)? "sudo " . (defined $4 ? "-u $4 ":"") : ""; + my($special)= (defined $5)? $5 : ""; $wait_time = (defined $2)? $2 : 0; # Return 2 rather than 1 afterwards @@ -2072,11 +2156,13 @@ elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/) $args =~ s/(?:^|\s)-d\S*// if $optargs =~ /(?:^|\s)-d/; - $cmd = "$envset$sudo$parm_cwd/eximdir/exim$special$optargs " . + 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 " . ">>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 @@ -2090,6 +2176,7 @@ elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/) if ($cmd =~ /\s-DSERVER=server\s/ && $cmd !~ /\s-DNOTDAEMON\s/) { + $pidfile = "$parm_cwd/spool/exim-daemon.pid"; if ($debug) { printf ">> daemon: $cmd\n"; } run_system("sudo mkdir spool/log 2>/dev/null"); run_system("sudo chown $parm_eximuser:$parm_eximgroup spool/log"); @@ -2100,12 +2187,23 @@ elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/) # 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 -oP $parm_cwd/spool/exim-daemon.pid !; + if ($cmd =~ /\s-oP\s/) + { + ($pidfile = $cmd) =~ s/^.*-oP ([^ ]+).*$/$1/; + $cmd =~ s!\s-bd\s! -bdf !; + } + else + { + $pidfile = "$parm_cwd/spool/exim-daemon.pid"; + $cmd =~ s!\s-bd\s! -bdf -oP $pidfile !; + } print ">> |${cmd}-server\n" if ($debug); open DAEMONCMD, "|${cmd}-server" || tests_exit(-1, "Failed to run $cmd"); DAEMONCMD->autoflush(1); while (