Flush SMTP output buffer before "delay" in an ACL; add control =
[exim.git] / test / runtest
index 92bbe804f017f1730e870aee5eab26dee0a254c5..328b8995868f96fd780acf2389bffdfe7000b422 100755 (executable)
@@ -1,6 +1,6 @@
 #! /usr/bin/perl -w
 
-# $Cambridge: exim/test/runtest,v 1.1 2006/02/06 16:07:10 ph10 Exp $
+# $Cambridge: exim/test/runtest,v 1.23 2007/01/31 16:52:12 ph10 Exp $
 
 ###############################################################################
 # This is the controlling script for the "new" test suite for Exim. It should #
@@ -23,7 +23,7 @@ use Socket;
 
 # Start by initializing some global variables
 
-$testversion = "4.61 (06-Feb-06)";
+$testversion = "4.67 (15-Jan-07)";
 
 $cf = "bin/cf";
 $cr = "\r";
@@ -36,6 +36,7 @@ $server_opts = "";
 
 $have_ipv4 = 1;
 $have_ipv6 = 1;
+$have_largefiles = 0;
 
 $test_start = 1;
 $test_end = $test_top = 8999;
@@ -97,6 +98,7 @@ sub inthandler { print "\n"; tests_exit(-1, "Caught SIGINT"); }
 
 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;
@@ -357,6 +359,10 @@ while(<IN>)
   # 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.
@@ -406,20 +412,30 @@ while(<IN>)
   # Date/time in mbx mailbox files
   s/\d\d-\w\w\w-\d\d\d\d\s\d\d:\d\d:\d\d\s[-+]\d\d\d\d,/06-Sep-1999 15:52:48 +0100,/gx;
 
-  # Date/time in debugging output for writing retry records
+  # Dates/times in debugging output for writing retry records
   if (/^  first failed=(\d+) last try=(\d+) next try=(\d+) (.*)$/)
     {
     my($next) = $3 - $2;
     $_ = "  first failed=dddd last try=dddd next try=+$next $4\n";
     }
+  s/^(\s*)now=\d+ first_failed=\d+ next_try=\d+ expired=(\d)/$1now=tttt first_failed=tttt next_try=tttt expired=$2/;
+  s/^(\s*)received_time=\d+ diff=\d+ timeout=(\d+)/$1received_time=tttt diff=tttt timeout=$2/;
 
   # Time to retry may vary
-  s/time to retry = -\d+/time to retry = -ddddd/;
-  s/retry record exists: age=\d/retry record exists: age=d/;
+  s/time to retry = \S+/time to retry = tttt/;
+  s/retry record exists: age=\S+/retry record exists: age=ttt/;
+  s/failing_interval=\S+ message_age=\S+/failing_interval=ttt message_age=ttt/;
 
   # 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 ========
 
@@ -485,6 +501,10 @@ while(<IN>)
   # 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)/;
@@ -523,6 +543,8 @@ while(<IN>)
   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/\b\Q$parm_ipv4r\E\b/ip4-reverse/g;
+  s/\b\Q$parm_ipv6r\E\b/ip6-reverse/g;
 
 
   # ======== Test network IP addresses ========
@@ -576,10 +598,12 @@ while(<IN>)
   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 ========
@@ -610,9 +634,9 @@ while(<IN>)
   s/\b\d+\.H\d+P\d+\b/dddddddddd.HddddddPddddd/;
 
   # Maildirsize data
-  if (/^\d+S,\d+C\s*$/)
+  while (/^\d+S,\d+C\s*$/)
     {
-    print MUNGED "dddS,dC\n";
+    print MUNGED;
     while (<IN>)
       {
       last if !/^\d+ \d+\s*$/;
@@ -620,6 +644,7 @@ while(<IN>)
       }
     last if !defined $_;
     }
+  last if !defined $_;
 
 
   # ======== Output from the "fd" program about open descriptors ========
@@ -645,9 +670,11 @@ while(<IN>)
 
   if ($is_stdout)
     {
-    # Skip translate_ip_address in -bP output because it ain't always there
+    # Skip translate_ip_address and use_classresources in -bP output because
+    # they aren't always there.
 
     next if /translate_ip_address =/;
+    next if /use_classresources/;
 
     # In certain filter tests, remove initial filter lines because they just
     # clog up by repetition.
@@ -786,7 +813,8 @@ while(<IN>)
                 /^Transports:/ ||
                 /^log selectors =/ ||
                 /^cwd=/ ||
-                /^Fixed never_users:/
+                /^Fixed never_users:/ ||
+                /^Size of off_t:/
                 );
       }
 
@@ -849,7 +877,7 @@ my($rf,$rsf,$mf,$sf,$sortfile) = @_;
 
 if (! -e $sf)
   {
-  return 0 if (! -s $rf && ! -s $rsf);
+  return 0 if (! -s $rf && (! defined $rsf || ! -s $rsf));
 
   print "\n";
   print "** $rf is not empty\n" if (-s $rf);
@@ -1377,7 +1405,7 @@ if (/^exim_lock\s+(.*)$/)
   # 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;
   }
 
@@ -1395,6 +1423,30 @@ if (/^exinext\s+(.*)/)
   }
 
 
+# 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.
 
@@ -1457,7 +1509,7 @@ if (/^sleep\s+(.*)$/)
 
 # Various Unix management commands are recognized
 
-if (/^(ln|ls|du|mkdir|mkfifo|touch|cp)\s/ ||
+if (/^(ln|ls|du|mkdir|mkfifo|touch|cp|cat)\s/ ||
     /^sudo (rmdir|rm|chown|chmod)\s/)
   {
   run_system("$_ >>test-stdout 2>>test-stderr");
@@ -1499,7 +1551,7 @@ if (/^server\s+(.*)$/)
   # 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;
   }
 
@@ -1579,11 +1631,12 @@ if (/^(cat)?write\s+(\S+)(?:\s+(.*))?\s*$/)
 # 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.
+# 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";
@@ -1675,9 +1728,11 @@ elsif (/^([A-Z_]+=\S+\s+)?(\d+)?\s*(sudo\s+)?exim(_\S+)?\s+(.*)$/)
 
     # 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);
@@ -1949,9 +2004,14 @@ while (<EXIMINFO>)
   {
   my(@temp);
 
-  if (/^Exim version/) { print; next; }
+  if (/^Exim version/) { print; }
 
-  if (/^Support for: (.*)/)
+  elsif (/^Size of off_t: (\d+)/)
+    {
+    $have_largefiles = 1 if $1 > 4;
+    }
+
+  elsif (/^Support for: (.*)/)
     {
     print;
     @temp = split /(\s+)/, $1;
@@ -1959,7 +2019,7 @@ while (<EXIMINFO>)
     %parm_support = @temp;
     }
 
-  if (/^Lookups: (.*)/)
+  elsif (/^Lookups: (.*)/)
     {
     print;
     @temp = split /(\s+)/, $1;
@@ -1967,7 +2027,7 @@ while (<EXIMINFO>)
     %parm_lookups = @temp;
     }
 
-  if (/^Authenticators: (.*)/)
+  elsif (/^Authenticators: (.*)/)
     {
     print;
     @temp = split /(\s+)/, $1;
@@ -1975,7 +2035,7 @@ while (<EXIMINFO>)
     %parm_authenticators = @temp;
     }
 
-  if (/^Routers: (.*)/)
+  elsif (/^Routers: (.*)/)
     {
     print;
     @temp = split /(\s+)/, $1;
@@ -1987,7 +2047,7 @@ while (<EXIMINFO>)
   # that the basic transport name is set, and then the name with each of the
   # options.
 
-  if (/^Transports: (.*)/)
+  elsif (/^Transports: (.*)/)
     {
     print;
     @temp = split /(\s+)/, $1;
@@ -2021,7 +2081,6 @@ if (defined $parm_support{'Content_Scanning'})
   {
   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.
@@ -2099,30 +2158,74 @@ if (defined $parm_support{'Content_Scanning'})
         }
       }
 
+    # Read the ClamAV configuration file and find the socket interface.
+
     if ($clamconf ne "")
       {
+      my $socket_domain;
       open(IN, "$clamconf") || die "\n** Unable to open $clamconf: $!\n";
       while (<IN>)
         {
         if (/^LocalSocket\s+(.*)/)
           {
           $parm_clamsocket = $1;
+          $socket_domain = AF_UNIX;
           last;
           }
+        if (/^TCPSocket\s+(\d+)/)
+          {
+          if (defined $parm_clamsocket)
+            {
+            $parm_clamsocket .= " $1";
+            $socket_domain = AF_INET;
+            last;
+            }
+          else
+            {
+            $parm_clamsocket = " $1";
+            }
+          }
+        elsif (/^TCPAddr\s+(\S+)/)
+          {
+          if (defined $parm_clamsocket)
+            {
+            $parm_clamsocket = $1 . $parm_clamsocket;
+            $socket_domain = AF_INET;
+            last;
+            }
+          else
+            {
+            $parm_clamsocket = $1;
+            }
+          }
         }
       close(IN);
-      if (-e $parm_clamsocket)
+
+      if (defined $socket_domain)
         {
         print ":\n  The clamd socket is $parm_clamsocket\n";
         # This test for an active ClamAV is courtesy of Daniel Tiefnig.
         eval
           {
-          my $sun = sockaddr_un($parm_clamsocket) or die "** Failed packing '$parm_clamsocket'\n";
-          socket(SOCK, AF_UNIX, SOCK_STREAM, 0) or die "** Unable to open socket '$parm_clamsocket'\n";
-
+          my $socket;
+          if ($socket_domain == AF_UNIX)
+            {
+            $socket = sockaddr_un($parm_clamsocket) or die "** Failed packing '$parm_clamsocket'\n";
+            }
+          elsif ($socket_domain == AF_INET)
+            {
+            my ($ca_host, $ca_port) = split(/\s+/,$parm_clamsocket);
+            my $ca_hostent = gethostbyname($ca_host) or die "** Failed to get raw address for host '$ca_host'\n";
+            $socket = sockaddr_in($ca_port, $ca_hostent) or die "** Failed packing '$parm_clamsocket'\n";
+            }
+          else
+            {
+            die "** Unknown socket domain '$socket_domain' (should not happen)\n";
+            }
+          socket(SOCK, $socket_domain, SOCK_STREAM, 0) or die "** Unable to open socket '$parm_clamsocket'\n";
           local $SIG{ALRM} = sub { die "** Timeout while connecting to socket '$parm_clamsocket'\n"; };
           alarm(5);
-          connect(SOCK, $sun) or die "** Unable to connect to socket '$parm_clamsocket'\n";
+          connect(SOCK, $socket) or die "** Unable to connect to socket '$parm_clamsocket'\n";
           alarm(0);
 
           my $ofh = select SOCK; $| = 1; select $ofh;
@@ -2139,7 +2242,7 @@ if (defined $parm_support{'Content_Scanning'})
 
         if($@)
           {
-          warn $@;
+          print "  $@";
           print "  Assume ClamAV is not running\n";
           }
         else
@@ -2150,7 +2253,7 @@ if (defined $parm_support{'Content_Scanning'})
         }
       else
         {
-        print ", but the socket for clamd does not exist\n";
+        print ", but the socket for clamd could not be determined\n";
         print "Assume ClamAV is not running\n";
         }
       }
@@ -2347,6 +2450,24 @@ else
 print "IPv4 address is $parm_ipv4\n";
 print "IPv6 address is $parm_ipv6\n";
 
+# For munging test output, we need the reversed IP addresses.
+
+$parm_ipv4r = ($parm_ipv4 !~ /^\d/)? "" :
+  join(".", reverse(split /\./, $parm_ipv4));
+
+$parm_ipv6r = $parm_ipv6;             # Appropriate if not in use
+if ($parm_ipv6 =~ /^[\da-f]/)
+  {
+  my(@comps) = split /:/, $parm_ipv6;
+  my(@nibbles);
+  foreach $comp (@comps)
+    {
+    push @nibbles, sprintf("%lx", hex($comp) >> 8);
+    push @nibbles, sprintf("%lx", hex($comp) & 0xff);
+    }
+  $parm_ipv6r = join(".", reverse(@nibbles));
+  }
+
 # Find the host name, fully qualified.
 
 chomp($temp = `hostname`);
@@ -2448,6 +2569,16 @@ if (system("cp $parm_exim_dir/exinext eximdir") != 0)
   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   #
@@ -2502,12 +2633,25 @@ opendir(DIR, "scripts") || tests_exit(-1, "Failed to opendir(\"scripts\"): $!");
 @test_dirs = sort readdir(DIR);
 closedir(DIR);
 
+# Remove . and .. and CVS from the list.
+
+for ($i = 0; $i < @test_dirs; $i++)
+  {
+  my($d) = $test_dirs[$i];
+  if ($d eq "." || $d eq ".." || $d eq "CVS")
+    {
+    splice @test_dirs, $i, 1;
+    $i--;
+    }
+  }
+
+# Scan for relevant tests
+
 for ($i = 0; $i < @test_dirs; $i++)
   {
   my($testdir) = $test_dirs[$i];
   my($wantthis) = 1;
 
-  next if $testdir eq "." || $testdir eq "..";
   print ">>Checking $testdir\n" if $debug;
 
   # Skip this directory if the first test is equal or greater than the first
@@ -2804,6 +2948,7 @@ foreach $test (@test_list)
 
   undef %cache;
   $next_msgid = "aX";
+  $next_pid = 1234;
   $next_port = 1111;
   $message_skip = 0;
   $msglog_skip = 0;
@@ -2850,6 +2995,15 @@ foreach $test (@test_list)
       if (/^rmfiltertest/)     { $rmfiltertest = 1; next; }
       if (/^sortlog/)          { $sortlog = 1; next; }
 
+      if (/^need_largefiles/)
+        {
+        next if $have_largefiles;
+        print ">>> Large file support is needed for test $testno, but is not available: skipping\n";
+        $docheck = 0;      # don't check output
+        undef $_;          # pretend EOF
+        last;
+        }
+
       if (/^need_ipv4/)
         {
         next if $have_ipv4;