Testsuite: 4509 independent of calling user
[exim.git] / test / runtest
index afaaee3b21e01d54ade86c90a21cb1bd2faee161..a0d7fd14b92e249f7a0dd1c0c114ebbbb7779a26 100755 (executable)
 
 #use strict;
 use 5.010;
+use feature 'state';   # included in 5.010
 use warnings;
 
 use Errno;
 use FileHandle;
-use IO::Socket::INET;
 use Socket;
 use Time::Local;
 use Cwd;
 use File::Basename;
+use FindBin qw'$Bin';
+
+use lib "$Bin/lib";
+use Exim::Runtest;
+
 use if $ENV{DEBUG} && $ENV{DEBUG} =~ /\bruntest\b/ => ('Smart::Comments' => '####');
 
 
@@ -41,10 +46,13 @@ $testversion = "4.80 (08-May-12)";
 # normal = 2432   tls_dh_max_bits = 2236
 $gnutls_dh_bits_normal = 2236;
 
-$cf = "bin/cf -exact";
+$cf = 'bin/cf -exact';
 $cr = "\r";
 $debug = 0;
-$flavour = 'FOO';
+$flavour = do {
+  my $f = Exim::Runtest::flavour();
+  (grep { $f eq $_ } Exim::Runtest::flavours()) ? $f : 'FOO';
+};
 $force_continue = 0;
 $force_update = 0;
 $log_failed_filename = "failed-summary.log";
@@ -88,6 +96,10 @@ $parm_port_d3 = 1227;        # Additional for daemon
 $parm_port_d4 = 1228;        # Additional for daemon
 my $dynamic_socket;          # allocated later for PORT_DYNAMIC
 
+# Find a suiteable group name for test (currently only 0001
+# uses a group name. A numeric group id would do
+my $parm_mailgroup = Exim::Runtest::mailgroup('mail');
+
 # Manually set locale
 $ENV{LC_ALL} = 'C';
 
@@ -149,6 +161,7 @@ s?\bTESTNUM\b?$_[0]?g;
 s?(\b|_)V4NET([\._])?$1$parm_ipv4_test_net$2?g;
 s?\bV6NET:?$parm_ipv6_test_net:?g;
 s?\bPORT_DYNAMIC\b?$dynamic_socket->sockport()?eg;
+s?\bMAILGROUP\b?$parm_mailgroup?g;
 }
 
 
@@ -878,7 +891,7 @@ RESET_AFTER_EXTRA_LINE_READ:
     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/;
+    s/SSL3_READ_BYTES/ssl3_read_bytes/i;
 
     # gnutls version variances
     next if /^Error in the pull function./;
@@ -1045,14 +1058,30 @@ RESET_AFTER_EXTRA_LINE_READ:
     # Spool filesystem free space changes on different systems.
     s/^((?:spool|log) directory space =) -?\d+K (inodes =)\s*-?\d+/$1 nnnnnK $2 nnnnn/;
 
+    # Non-TLS builds have different expansions for received_header_text
+    if (s/(with \$received_protocol)\}\} \$\{if def:tls_cipher \{\(\$tls_cipher\)\n$/$1/)
+      {
+      $_ .= <IN>;
+      s/\s+\}\}(?=\(Exim )/\}\} /;
+      }
+    if (/^  condition: def:tls_cipher$/)
+      {
+      <IN>; <IN>; <IN>; <IN>; <IN>; <IN>;
+      <IN>; <IN>; <IN>; <IN>; <IN>; next;
+      }
+
+    # Not all platforms build with DKIM enabled
+    next if /^PDKIM >> Body data for hash, canonicalized/;
+
     # Not all platforms support TCP Fast Open, and the compile omits the check
-    if (/^((>>> )?Connecting to .* )in hosts_try_fastopen\? no \(option unset\)$/)
+    if (s/\S+ in hosts_try_fastopen\? no \(option unset\)\n$//)
       {
-      my $p = $1;
-      $_ = <IN>;
-      s/^/$p/;
+      $_ .= <IN>;
+      s/ \.\.\. >>> / ... /;
       }
 
+    next if /^(ppppp )?setsockopt FASTOPEN: Protocol not available$/;
+
     # 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
@@ -1127,11 +1156,22 @@ return $yield;
 #            [2] if there is a C in the prompt and $force_continue is true
 # Returns:   returns the answer
 
-sub interact{
-print $_[0];
-if ($_[1]) { $_ = "u"; print "... update forced\n"; }
-  elsif ($_[2]) { $_ = "c"; print "... continue forced\n"; }
-  else { $_ = <T>; }
+sub interact {
+  my ($prompt, $have_u, $have_c) = @_;
+
+  print $prompt;
+
+  if ($have_u) {
+    print "... update forced\n";
+    return 'u';
+  }
+
+  if ($have_c) {
+    print "... continue forced\n";
+    return 'c';
+  }
+
+  return lc <T>;
 }
 
 
@@ -1151,13 +1191,13 @@ if ($_[1]) { $_ = "u"; print "... update forced\n"; }
 
 
 sub log_failure {
-  my $logfile = shift();
-  my $testno  = shift();
-  my $detail  = shift() || '';
-  if ( open(my $fh, ">>", $logfile) ) {
-    print $fh "Test $testno $detail failed\n";
-    close $fh;
-  }
+  my ($logfile, $testno, $detail) = @_;
+
+  open(my $fh, '>>', $logfile) or return;
+
+  print $fh "Test $testno "
+        . (defined $detail ? "$detail " : '')
+        . "failed\n";
 }
 
 
@@ -1204,10 +1244,9 @@ if (! -e $sf_current)
 
   for (;;)
     {
-    print "Continue, Show, or Quit? [Q] ";
-    $_ = $force_continue ? "c" : <T>;
-    tests_exit(1) if /^q?$/i;
-    log_failure($log_failed_filename, $testno, $rf) if (/^c$/i && $force_continue);
+    $_ = interact('Continue, Show, or Quit? [Q] ', undef, $force_continue);
+    tests_exit(1) if /^q?$/;
+    log_failure($log_failed_filename, $testno, $rf) if (/^c$/ && $force_continue);
     return 0 if /^c$/i;
     last if (/^s$/);
     }
@@ -1226,9 +1265,9 @@ if (! -e $sf_current)
   print "\n";
   for (;;)
     {
-    interact("Continue, Update & retry, Quit? [Q] ", $force_update, $force_continue);
-    tests_exit(1) if /^q?$/i;
-    log_failure($log_failed_filename, $testno, $rsf) if (/^c$/i && $force_continue);
+    $_ = interact('Continue, Update & retry, Quit? [Q] ', $force_update, $force_continue);
+    tests_exit(1) if /^q?$/;
+    log_failure($log_failed_filename, $testno, $rsf) if (/^c$/ && $force_continue);
     return 0 if /^c$/i;
     last if (/^u$/i);
     }
@@ -1346,10 +1385,10 @@ if (-e $sf_current)
   print "\n";
   for (;;)
     {
-    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;
+    $_ = 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?$/;
     log_failure($log_failed_filename, $testno, $sf_current) if (/^c$/i && $force_continue);
     return 0 if /^c$/i;
     return 1 if /^r$/i;
@@ -1566,16 +1605,16 @@ if (! $message_skip)
 
     for (;;)
       {
-      interact("Continue, Update & retry, or Quit? [Q] ", $force_update, $force_continue);
-      tests_exit(1) if /^q?$/i;
-      log_failure($log_failed_filename, $testno, "missing email") if (/^c$/i && $force_continue);
-      last if /^c$/i;
+      $_ = interact('Continue, Update & retry, or Quit? [Q] ', $force_update, $force_continue);
+      tests_exit(1) if /^q?$/;
+      log_failure($log_failed_filename, $testno, "missing email") if (/^c$/ && $force_continue);
+      last if /^c$/;
 
       # For update, we not only have to unlink the file, but we must also
       # remove it from the @oldmails vector, as otherwise it will still be
       # checked for when we re-run the test.
 
-      if (/^u$/i)
+      if (/^u$/)
         {
         foreach $key (keys %expected_mails)
           {
@@ -1650,11 +1689,11 @@ if (! $msglog_skip)
 
     for (;;)
       {
-      interact("Continue, Update, or Quit? [Q] ", $force_update, $force_continue);
-      tests_exit(1) if /^q?$/i;
-      log_failure($log_failed_filename, $testno, "missing msglog") if (/^c$/i && $force_continue);
-      last if /^c$/i;
-      if (/^u$/i)
+      $_ = interact('Continue, Update, or Quit? [Q] ', $force_update, $force_continue);
+      tests_exit(1) if /^q?$/;
+      log_failure($log_failed_filename, $testno, "missing msglog") if (/^c$/ && $force_continue);
+      last if /^c$/;
+      if (/^u$/)
         {
         foreach $key (keys %expected_msglogs)
           {
@@ -1683,14 +1722,9 @@ return $yield;
 # Returns:  nothing
 
 sub run_system {
-my($cmd) = $_[0];
-if ($debug)
-  {
-  my($prcmd) = $cmd;
-  $prcmd =~ s/; /;\n>> /;
-  print ">> $prcmd\n";
-  }
-system("$cmd");
+  my $cmd = shift;
+  print '>> ' . $cmd =~ s/; /;\n>>/r . "\n" if $debug;
+  system $cmd;
 }
 
 
@@ -2217,7 +2251,7 @@ elsif (/^((?i:[A-Z\d_]+=\S+\s+)+)?(\d+)?\s*(sudo(?:\s+-u\s+(\w+))?\s+)?exim(_\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 mkdir spool/log 2>/dev/null');
     run_system("sudo chown $parm_eximuser:$parm_eximgroup spool/log");
 
     # Before running the command, convert the -bd option into -bdf so that an
@@ -2253,7 +2287,7 @@ elsif (/^((?i:[A-Z\d_]+=\S+\s+)+)?(\d+)?\s*(sudo(?:\s+-u\s+(\w+))?\s+)?exim(_\S+
 
     my $listen_port = $1;
     if ($debug) { printf ">> wait-mode daemon: $cmd\n"; }
-    run_system("sudo mkdir spool/log 2>/dev/null");
+    run_system('sudo mkdir spool/log 2>/dev/null');
     run_system("sudo chown $parm_eximuser:$parm_eximgroup spool/log");
 
     my $pid = fork();
@@ -2419,7 +2453,7 @@ else
 # as the path to the binary. If the first argument does not start with a
 # '/' but exists in the file system, it's assumed to be the Exim binary.
 
-$parm_exim = (@ARGV > 0 && (-x $ARGV[0] or $ARGV[0] =~ m?^/?))? Cwd::abs_path(shift @ARGV) : "";
+($parm_exim, @ARGV) = Exim::Runtest::exim_binary(@ARGV);
 print "Exim binary is $parm_exim\n" if $parm_exim ne "";
 
 
@@ -2486,55 +2520,6 @@ $parm_cwd = Cwd::getcwd();
 # takes precedence; otherwise exim-snapshot takes precedence over any numbered
 # releases.
 
-if ($parm_exim eq "")
-  {
-  my($use_srcdir) = "";
-
-  opendir DIR, ".." || die "** Failed to opendir \"..\": $!\n";
-  while ($f = readdir(DIR))
-    {
-    my($srcdir);
-
-    # Try this directory if it is "exim4" or if it is exim-snapshot or exim-n.m
-    # possibly followed by -RCx where n.m is greater than any previously tried
-    # directory. Thus, we should choose the highest version of Exim that has
-    # been compiled.
-
-    if ($f eq "exim4" || $f eq "exim-snapshot" || $f eq 'src')
-      { $srcdir = $f; }
-    else
-      { $srcdir = $f
-        if ($f =~ /^exim-\d+\.\d+(-RC\d+)?$/ && $f gt $use_srcdir); }
-
-    # Look for a build directory with a binary in it. If we find a binary,
-    # accept this source directory.
-
-    if ($srcdir)
-      {
-      opendir SRCDIR, "../$srcdir" ||
-        die "** Failed to opendir \"$cwd/../$srcdir\": $!\n";
-      while ($f = readdir(SRCDIR))
-        {
-        if ($f =~ /^build-/ && -e "../$srcdir/$f/exim")
-          {
-          $use_srcdir = $srcdir;
-          $parm_exim = "$cwd/../$srcdir/$f/exim";
-          $parm_exim =~ s'/[^/]+/\.\./'/';
-          last;
-          }
-        }
-      closedir(SRCDIR);
-      }
-
-    # If we have found "exim4" or "exim-snapshot", that takes precedence.
-    # Otherwise, continue to see if there's a later version.
-
-    last if $use_srcdir eq "exim4" || $use_srcdir eq "exim-snapshot";
-    }
-  closedir(DIR);
-  print "Exim binary found in $parm_exim\n" if $parm_exim ne "";
-  }
-
 # If $parm_exim is still empty, ask the caller
 
 if ($parm_exim eq "")
@@ -2587,7 +2572,13 @@ while(<EXIMINFO>)
       $version =~ s/^\d+\K\./_/;
       $git =~ s/^exim-//i;
       $git =~ s/.*-\Kg([[:xdigit:]]+(?:-XX)?)/$1/;
-      print "\n*** Version mismatch (Exim: $version vs. GIT: $git). ***\n\n"
+      print <<___
+
+*** Version mismatch
+*** Exim binary: $version
+*** Git        : $git
+
+___
         if not $version eq $git;
     }
   }
@@ -2596,7 +2587,7 @@ while(<EXIMINFO>)
   $parm_trusted_config_list = $1 if /^TRUSTED_CONFIG_LIST:.*?"(.*?)"$/;
   ($parm_configure_owner, $parm_configure_group) = ($1, $2)
        if /^Configure owner:\s*(\d+):(\d+)/;
-  print "$_" if /wrong owner/;
+  print if /wrong owner/;
   }
 close(EXIMINFO);
 
@@ -3147,8 +3138,15 @@ if ($parm_ipv6 =~ /^[\da-f]/)
 
 chomp($temp = `hostname`);
 die "'hostname' didn't return anything\n" unless defined $temp and length $temp;
-$parm_hostname = (gethostbyname($temp))[0];
-$parm_hostname = "no.host.name.found" unless defined $parm_hostname and length $parm_hostname;
+if ($temp =~ /\./)
+  {
+  $parm_hostname = $temp;
+  }
+else
+  {
+  $parm_hostname = (gethostbyname($temp))[0];
+  $parm_hostname = "no.host.name.found" unless defined $parm_hostname and length $parm_hostname;
+  }
 print "Hostname is $parm_hostname\n";
 
 if ($parm_hostname !~ /\./)
@@ -3182,13 +3180,9 @@ if ($parm_hostname =~ /[[:upper:]]/)
 # that was done above. Furthermore, we ensure that the binary is deleted at the
 # end of the test. First ensure the directory exists.
 
-if (-d "eximdir")
-  { unlink "eximdir/exim"; }     # Just in case
-else
-  {
-  mkdir("eximdir", 0710) || die "** Unable to mkdir $parm_cwd/eximdir: $!\n";
-  system("sudo chgrp $parm_eximgroup eximdir");
-  }
+unlink 'eximdir/exim';  # Just in case
+-d 'eximdir' or mkdir('eximdir', 0710) or die "** Unable to mkdir $parm_cwd/eximdir: $!\n";
+system("sudo chgrp $parm_eximgroup eximdir");
 
 # The construction of the patched binary must be done as root, so we use
 # a separate script. As well as indicating that this is a test-harness binary,
@@ -3203,16 +3197,16 @@ die "** Unable to make patched exim: $!\n"
 # tests_exit(), so that suitable cleaning up can be done when required.
 # Arrange to catch interrupting signals, to assist with this.
 
-$SIG{'INT'} = \&inthandler;
-$SIG{'PIPE'} = \&pipehandler;
+$SIG{INT} = \&inthandler;
+$SIG{PIPE} = \&pipehandler;
 
 # For some tests, we need another copy of the binary that is setuid exim rather
 # than root.
 
-system("sudo cp eximdir/exim eximdir/exim_exim;" .
+system('sudo cp eximdir/exim eximdir/exim_exim;' .
        "sudo chown $parm_eximuser eximdir/exim_exim;" .
        "sudo chgrp $parm_eximgroup eximdir/exim_exim;" .
-       "sudo chmod 06755 eximdir/exim_exim");
+       'sudo chmod 06755 eximdir/exim_exim');
 
 
 ##################################################
@@ -3591,7 +3585,12 @@ closedir(DIR);
 # contains ****. We open input from the terminal so that we can read responses
 # to prompts.
 
-open(T, "/dev/tty") || tests_exit(-1, "Failed to open /dev/tty: $!");
+if (not $force_continue) {
+  # runtest needs to interact if we're not in continue
+  # mode. It does so by communicate to /dev/tty
+  open(T, "/dev/tty") or tests_exit(-1, "Failed to open /dev/tty: $!");
+}
+
 
 print "\nPress RETURN to run the tests: ";
 $_ = $force_continue ? "c" : <T>;
@@ -3681,16 +3680,7 @@ foreach $test (@test_list)
     if (/^no_stdout_check/)  { $stdout_skip = 1; next; }
     if (/^rmfiltertest/)     { $rmfiltertest = 1; next; }
     if (/^sortlog/)          { $sortlog = 1; next; }
-    if (/\bPORT_DYNAMIC\b/)  {
-      for (my $port = 1024; $port < 65000; $port++) {
-        $dynamic_socket = IO::Socket::INET->new(
-          LocalHost => '127.0.0.1',
-          LocalPort => $port,
-          Listen => 10,
-          ReuseAddr => 1,
-        ) and last;
-      }
-      }
+    if (/\bPORT_DYNAMIC\b/)  { $dynamic_socket = Exim::Runtest::dynamic_socket(); next; }
     }
   # Reset to beginning of file for per test interpreting/processing
   seek(SCRIPT, 0, 0);