Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Skip harmless ASan warnings.
[simgrid.git] / teshsuite / smpi / mpich3-test / runtests
1 #! /usr/bin/env perl
2 # -*- Mode: perl; -*-
3 #
4 # This script is the beginnings of a script to run a sequence of test
5 # programs.  See the MPICH document for a description of the test
6 # strategy and requirements.
7 #
8 # Description
9 #   Tests are controlled by a file listing test programs; if the file is
10 #   a directory, then all of the programs in the directory and subdirectories
11 #   are run
12 #
13 #   To run a test, the following steps are executed
14 #   Build the executable:
15 #      make programname
16 #   Run the executable
17 #      mpiexec -n <np> ./programname >out 2>err
18 #   Check the return code (non zero is failure)
19 #   Check the stderr output (non empty is failure)
20 #   Check the stdout output (No Errors or Test passed are the only valid
21 #      output)
22 #   Remove executable, out, err files
23 #
24 # The format of a list file is
25 # programname number-of-processes
26 # If number-of-processes is missing, $np_default is used (this is 2 but can
27 # be overridden with -np=new-value)
28 #
29 # Special feature:
30 # Because these tests can take a long time to run, there is an
31 # option to cause the tests to stop is a "stopfile" is found.
32 # The stopfile can be created by a separate, watchdog process, to ensure that
33 # tests end at a certain time.
34 # The name of this file is (by default) .stoptest
35 # in the  top-level run directory.  The environment variable
36 #    MPITEST_STOPTEST
37 # can specify a different file name.
38 #
39 # Import the mkpath command
40 use File::Path;
41
42 # Global variables
43 $MPIMajorVersion = "2";
44 $MPIMinorVersion = "2";
45 $mpiexec = "smpirun";    # Name of mpiexec program (including path, if necessary)
46 $testIsStrict = "true";
47 $MPIhasMPIX   = "no";
48 $np_arg  = "-np";         # Name of argument to specify the number of processes
49 $err_count = 0;          # Number of programs that failed.
50 $total_run = 0;          # Number of programs tested
51 $total_seen = 0;         # Number of programs considered for testing
52 $np_default = 2;         # Default number of processes to use
53 $np_max     = -1;        # Maximum number of processes to use (overrides any
54                          # value in the test list files.  -1 is Infinity
55 $defaultTimeLimit = 180; # default timeout
56
57 $srcdir = ".";           # Used to set the source dir for testlist files
58
59 $curdir = ".";           # used to track the relative current directory
60
61 # Output forms
62 $xmloutput = 0;          # Set to true to get xml output (also specify file)
63 $closeXMLOutput = 1;     # Set to false to leave XML output file open to
64                          # accept additional data
65 $verbose = 1;            # Set to true to get more output
66 $showProgress = 0;       # Set to true to get a "." with each run program.
67 $newline = "\r\n";       # Set to \r\n for Windows-friendly, \n for Unix only
68 $batchRun = 0;           # Set to true to batch the execution of the tests
69                          # (i.e., run them together, then test output,
70                          # rather than build/run/check for each test)
71 $testCount = 0;          # Used with batchRun to count tests.
72 $batrundir = ".";        # Set to the directory into which to run the examples
73
74 $execarg="";
75 $wrapparg="";
76
77 $enabled_privatization = 1; # disable tests that need SMPI privatization to run
78 # TAP (Test Anything Protocol) output
79 my $tapoutput = 0;
80 my $tapfile = '';
81 my $tapfullfile = '';
82
83 $debug = 1;
84
85 $depth = 0;              # This is used to manage multiple open list files
86
87 # Build flags
88 $remove_this_pgm = 0;
89 $clean_pgms      = 0;
90
91 my $program_wrapper = '';
92
93 #---------------------------------------------------------------------------
94 # Get some arguments from the environment
95 #   Currently, only the following are understood:
96 #   VERBOSE
97 #   RUNTESTS_VERBOSE  (an alias for VERBOSE in case you want to
98 #                      reserve VERBOSE)
99 #   RUNTESTS_SHOWPROGRESS
100 #   MPITEST_STOPTEST
101 #   MPITEST_TIMEOUT
102 #   MPITEST_PROGRAM_WRAPPER (Value is added after -np but before test
103 #                            executable.  Tools like valgrind may be inserted
104 #                            this way.)
105 #---------------------------------------------------------------------------
106 if ( defined($ENV{"VERBOSE"}) || defined($ENV{"V"}) || defined($ENV{"RUNTESTS_VERBOSE"}) ) {
107     $verbose = 1;
108 }
109 if ( defined($ENV{"RUNTESTS_SHOWPROGRESS"} ) ) {
110     $showProgress = 1;
111 }
112 if (defined($ENV{"MPITEST_STOPTEST"})) {
113     $stopfile = $ENV{"MPITEST_STOPTEST"};
114 }
115 else {
116     $stopfile = `pwd` . "/.stoptest";
117     $stopfile =~ s/\r*\n*//g;    # Remove any newlines (from pwd)
118 }
119
120 if (defined($ENV{"MPITEST_TIMEOUT"})) {
121     $defaultTimeLimit = $ENV{"MPITEST_TIMEOUT"};
122 }
123
124 # Define this to leave the XML output file open to receive additional data
125 if (defined($ENV{'NOXMLCLOSE'}) && $ENV{'NOXMLCLOSE'} eq 'YES') {
126     $closeXMLOutput = 0;
127 }
128
129 if (defined($ENV{'MPITEST_PROGRAM_WRAPPER'})) {
130     $program_wrapper = $ENV{'MPITEST_PROGRAM_WRAPPER'};
131 }
132
133 if (defined($ENV{'MPITEST_BATCH'})) {
134     if ($ENV{'MPITEST_BATCH'} eq 'YES' || $ENV{'MPITEST_BATCH'} eq 'yes') {
135         $batchRun = 1;
136     } elsif ($ENV{'MPITEST_BATCH'} eq 'NO' || $ENV{'MPITEST_BATCH'} eq 'no') {
137         $batchRun = 0;
138     }
139     else {
140         print STDERR "Unrecognized value for MPITEST_BATCH = $ENV{'MPITEST_BATCH'}\n";
141     }
142 }
143 if (defined($ENV{'MPITEST_BATCHDIR'})) {
144     $batrundir = $ENV{'MPITEST_BATCHDIR'};
145 }
146
147 #---------------------------------------------------------------------------
148 # Process arguments and override any defaults
149 #---------------------------------------------------------------------------
150 foreach $_ (@ARGV) {
151     if (/--?mpiexec=(.*)/) {
152         # Use mpiexec as given - it may be in the path, and
153         # we don't want to bother to try and find it.
154         $mpiexec = $1;
155     }
156     elsif (/--?np=(.*)/)   { $np_default = $1; }
157     elsif (/--?maxnp=(.*)/) { $np_max = $1; }
158     elsif (/--?tests=(.*)/) { $listfiles = $1; }
159     elsif (/--?srcdir=(.*)/) { $srcdir = $1;
160         $mpiexec="$mpiexec  -platform ${srcdir}/../../../../examples/platforms/small_platform_with_routers.xml -hostfile ${srcdir}/../../hostfile_coll --log=root.thr:critical --cfg=smpi/host-speed:1e9  --cfg=smpi/async-small-thresh:65536"; }
161     elsif (/--?verbose/) { $verbose = 1; }
162     elsif (/--?showprogress/) { $showProgress = 1; }
163     elsif (/--?debug/) { $debug = 1; }
164     elsif (/--?batch/) { $batchRun = 1; }
165     elsif (/--?batchdir=(.*)/) { $batrundir = $1; }
166     elsif (/--?timeoutarg=(.*)/) { $timeoutArgPattern = $1; }
167     elsif (/--?execarg=(.*)/) { $execarg = "$execarg $1"; }
168     elsif (/--?privatization=(.*)/) { 
169 print STDERR "privatization called\n";
170 $enabled_privatization = $1; }
171     elsif (/VALGRIND_COMMAND=(.*)/) { 
172         $valgrind = $1; }
173     elsif (/VALGRIND_OPTIONS=(.*)/) {
174          $wrapparg = "-wrapper \"$valgrind $1\""; }
175     elsif (/--?xmlfile=(.*)/) {
176         $xmlfile   = $1;
177         if (! ($xmlfile =~ /^\//)) {
178             $thisdir = `pwd`;
179             chop $thisdir;
180             $xmlfullfile = $thisdir . "/" . $xmlfile ;
181         }
182         else {
183             $xmlfullfile = $xmlfile;
184         }
185         $xmloutput = 1;
186         open( XMLOUT, ">$xmlfile" ) || die "Cannot open $xmlfile\n";
187         my $date = `date "+%Y-%m-%d-%H-%M"`;
188         $date =~ s/\r?\n//;
189         # MPISOURCE can be used to describe the source of MPI for this
190         # test.
191         print XMLOUT "<?xml version='1.0' ?>$newline";
192         print XMLOUT "<?xml-stylesheet href=\"TestResults.xsl\" type=\"text/xsl\" ?>$newline";
193         print XMLOUT "<MPITESTRESULTS>$newline";
194         print XMLOUT "<DATE>$date</DATE>$newline";
195         print XMLOUT "<MPISOURCE></MPISOURCE>$newline";
196     }
197     elsif (/--?noxmlclose/) {
198         $closeXMLOutput = 0;
199     }
200     elsif (/--?tapfile=(.*)/) {
201         $tapfile = $1;
202         if ($tapfile !~ m|^/|) {
203             $thisdir = `pwd`;
204             chomp $thisdir;
205             $tapfullfile = $thisdir . "/" . $tapfile ;
206         }
207         else {
208             $tapfullfile = $tapfile;
209         }
210         $tapoutput = 1;
211         open( TAPOUT, ">$tapfile" ) || die "Cannot open $tapfile\n";
212         my $date = `date "+%Y-%m-%d-%H-%M"`;
213         $date =~ s/\r?\n//;
214         print TAPOUT "TAP version 13\n";
215         print TAPOUT "# MPICH test suite results (TAP format)\n";
216         print TAPOUT "# date ${date}\n";
217         # we do not know at this point how many tests will be run, so do
218         # not print a test plan line like "1..450" until the very end
219     }
220 }
221
222 # Perform any post argument processing
223 if ($batchRun) {
224     if (! -d $batrundir) {
225         mkpath $batrundir || die "Could not create $batrundir\n";
226     }
227     open( BATOUT, ">$batrundir/runtests.batch" ) || die "Could not open $batrundir/runtests.batch\n";
228 }
229 else {
230     # We must have mpiexec
231     if ("$mpiexec" eq "") {
232         print STDERR "No mpiexec found!\n";
233         exit(1);
234     }
235 }
236
237 #
238 # Process any files
239 if ($listfiles eq "") {
240     if ($batchRun) {
241         print STDERR "An implicit list of tests is not permitted in batch mode\n";
242         exit(1);
243     }
244     else {
245         &ProcessImplicitList;
246     }
247 }
248 elsif (-d $listfiles) {
249     print STDERR "Testing by directories not yet supported\n";
250 }
251 else {
252     &RunList( $listfiles );
253 }
254
255 if ($xmloutput && $closeXMLOutput) {
256     print XMLOUT "</MPITESTRESULTS>$newline";
257     close XMLOUT;
258 }
259
260 if ($tapoutput) {
261     print TAPOUT "1..$total_seen\n";
262     close TAPOUT;
263 }
264
265 # Output a summary:
266 if ($batchRun) {
267     print "Programs created along with a runtest.batch file in $batrundir\n";
268     print "Run that script and then use checktests to summarize the results\n";
269 }
270 else {
271     if ($err_count) {
272         print "$err_count tests failed out of $total_run\n";
273         print "Failing tests : $failed_tests\n";
274         if ($xmloutput) {
275             print "Details in $xmlfullfile\n";
276         }
277     }
278     else {
279         print " All $total_run tests passed!\n";
280     }
281     if ($tapoutput) {
282         print "TAP formatted results in $tapfullfile\n";
283     }
284 }
285 exit ($err_count > 0);
286 #\f
287 # ---------------------------------------------------------------------------
288 # Routines
289 #
290 # Enter a new directory and process a list file.
291 #  ProcessDir( directory-name, list-file-name )
292 sub ProcessDir {
293     my $dir = $_[0]; $dir =~ s/\/$//;
294     my $listfile = $_[1];
295     my $savedir = `pwd`;
296     my $savecurdir = $curdir;
297     my $savesrcdir = $srcdir;
298
299     chop $savedir;
300     if (substr($srcdir,0,3) eq "../") {
301       $srcdir = "../$srcdir";
302     }
303
304     print "Processing directory $dir\n" if ($verbose || $debug);
305     chdir $dir;
306     if ($dir =~ /\//) {
307         print STDERR "only direct subdirectories allowed in list files";
308     }
309     $curdir .= "/$dir";
310
311     &RunList( $listfile );
312     print "\n" if $showProgress; # Terminate line from progress output
313     chdir $savedir;
314     $curdir = $savecurdir;
315     $srcdir = $savesrcdir;
316 }
317 # ---------------------------------------------------------------------------
318 # Run the programs listed in the file given as the argument.
319 # This file describes the tests in the format
320 #  programname number-of-processes [ key=value ... ]
321 # If the second value is not given, the default value is used.
322 #
323 sub RunList {
324     my $LIST = "LIST$depth"; $depth++;
325     my $listfile = $_[0];
326     my $ResultTest = "";
327     my $InitForRun = "";
328     my $listfileSource = $listfile;
329
330     print "Looking in $curdir/$listfile\n" if $debug;
331     if (! -s "$listfile" && -s "$srcdir/$curdir/$listfile" ) {
332         $listfileSource = "$srcdir/$curdir/$listfile";
333     }
334     open( $LIST, "<$listfileSource" ) ||
335         die "Could not open $listfileSource\n";
336     while (<$LIST>) {
337         # Check for stop file
338         if (-s $stopfile) {
339             # Exit because we found a stopfile
340             print STDERR "Terminating test because stopfile $stopfile found\n";
341             last;
342         }
343         # Skip comments
344         s/#.*//g;
345         # Remove any trailing newlines/returns
346         s/\r?\n//;
347         # Remove any leading whitespace
348         s/^\s*//;
349         # Some tests require that support routines are built first
350         # This is specified with !<dir>:<target>
351         if (/^\s*\!([^:]*):(.*)/) {
352             # Hack: just execute in a subshell.  This discards any
353             # output.
354             `cd $1 && make $2`;
355             next;
356         }
357         # List file entries have the form:
358         # program [ np [ name=value ... ] ]
359         # See files errhan/testlist, init/testlist, and spawn/testlist
360         # for examples of using the key=value form
361         my @args = split(/\s+/,$_);
362         my $programname = $args[0];
363         my $np = "";
364         my $ResultTest = "";
365         my $InitForRun = "";
366         my $timeLimit  = "";
367         my $progArgs   = "";
368         my $mpiexecArgs = "$execarg";
369         my $requiresStrict = "";
370         my $requiresMPIX   = "";
371         my $progEnv    = "";
372         my $mpiVersion = "";
373         my $needs_privatization = 0;
374         my $xfail = "";
375         if ($#args >= 1) { $np = $args[1]; }
376         # Process the key=value arguments
377         for (my $i=2; $i <= $#args; $i++) {
378             if ($args[$i] =~ /([^=]+)=(.*)/) {
379                 my $key = $1;
380                 my $value = $2;
381                 if ($key eq "resultTest") {
382                     $ResultTest = $value;
383                 }
384                 elsif ($key eq "init") {
385                     $InitForRun = $value;
386                 }
387                 elsif ($key eq "timeLimit") {
388                     $timeLimit = $value;
389                 }
390                 elsif ($key eq "arg") {
391                     $progArgs = "$progArgs $value";
392                 }
393                 elsif ($key eq "mpiexecarg") {
394                     $mpiexecArgs = "$mpiexecArgs $value";
395                 }
396                 elsif ($key eq "env") {
397                     $progEnv = "$progEnv $value";
398                 }
399                 elsif ($key eq "mpiversion") {
400                     $mpiVersion = $value;
401                 }
402                 elsif ($key eq "needs_privatization") {
403                     $needs_privatization = $value;
404                 }
405                 elsif ($key eq "strict") {
406                     $requiresStrict = $value
407                 }
408                 elsif ($key eq "mpix") {
409                     $requiresMPIX = $value
410                 }
411                 elsif ($key eq "xfail") {
412                     if ($value eq "") {
413                         print STDERR "\"xfail=\" requires an argument\n";
414                     }
415                     $xfail = $value;
416                 }
417                 else {
418                     print STDERR "Unrecognized key $key in $listfileSource\n";
419                 }
420             }
421         }
422
423         # skip empty lines
424         if ($programname eq "") { next; }
425
426         # if privatization is disabled, and if the test needs it, ignore it
427         if ($needs_privatization == 1 &&
428                 $enabled_privatization != 1) {
429                 SkippedTest($programname, $np, $workdir, "requires SMPI privatization");
430                 next; 
431         }
432
433         if ($np eq "") { $np = $np_default; }
434         if ($np_max > 0 && $np > $np_max) { $np = $np_max; }
435
436         # allows us to accurately output TAP test numbers without disturbing the
437         # original totals that have traditionally been reported
438         #
439         # These "unless" blocks are ugly, but permit us to honor skipping
440         # criteria for directories as well without counting directories as tests
441         # in our XML/TAP output.
442         unless (-d $programname) {
443             $total_seen++;
444         }
445
446         # If a minimum MPI version is specified, check against the
447         # available MPI.  If the version is unknown, we ignore this
448         # test (thus, all tests will be run).
449         if ($mpiVersion ne "" && $MPIMajorVersion ne "unknown" &&
450             $MPIMinorVersion ne "unknown") {
451             my ($majorReq,$minorReq) = split(/\./,$mpiVersion);
452             if ($majorReq > $MPIMajorVersion or
453                 ($majorReq == $MPIMajorVersion && $minorReq > $MPIMinorVersion))
454             {
455                 unless (-d $programname) {
456                     SkippedTest($programname, $np, $workdir, "requires MPI version $mpiVersion");
457                 }
458                 next;
459             }
460         }
461         # Check whether strict is required by MPI but not by the
462         # test (use strict=false for tests that use non-standard extensions)
463         if (lc($requiresStrict) eq "false" && lc($testIsStrict) eq "true") {
464             unless (-d $programname) {
465                 SkippedTest($programname, $np, $workdir, "non-strict test, strict MPI mode requested");
466             }
467             next;
468         }
469
470         if (lc($testIsStrict) eq "true") {
471             # Strict MPI testing was requested, so assume that a non-MPICH MPI
472             # implementation is being tested and the "xfail" implementation
473             # assumptions do not hold.
474             $xfail = '';
475         }
476
477         if (lc($requiresMPIX) eq "true" && lc($MPIHasMPIX) eq "no") {
478             unless (-d $programname) {
479                 SkippedTest($programname, $np, $workdir, "tests MPIX extensions, MPIX testing disabled");
480             }
481             next;
482         }
483
484         if (-d $programname) {
485             # If a directory, go into the that directory and
486             # look for a new list file
487             &ProcessDir( $programname, $listfile );
488         }
489         else {
490             $total_run++;
491             if (&BuildMPIProgram( $programname, $xfail ) == 0) {
492                 if ($batchRun == 1) {
493                     &AddMPIProgram( $programname, $np, $ResultTest,
494                                     $InitForRun, $timeLimit, $progArgs,
495                                     $progEnv, $mpiexecArgs, $xfail );
496                 }
497                 else {
498                     &RunMPIProgram( $programname, $np, $ResultTest,
499                                     $InitForRun, $timeLimit, $progArgs,
500                                     $progEnv, $mpiexecArgs, $xfail );
501                 }
502             }
503             elsif ($xfail ne '') {
504                 # We expected to run this program, so failure to build
505                 # is an error
506                 $found_error = 1;
507                 $err_count++;
508             }
509             if ($batchRun == 0) {
510                 &CleanUpAfterRun( $programname );
511             }
512         }
513     }
514     close( $LIST );
515 }
516 #
517 # This routine tries to run all of the files in the current
518 # directory
519 sub ProcessImplicitList {
520     # The default is to run every file in the current directory.
521     # If there are no built programs, build and run every file
522     # WARNING: This assumes that anything executable should be run as
523     # an MPI test.
524     $found_exec = 0;
525     $found_src  = 0;
526     open (PGMS, "ls -1 |" ) || die "Cannot list directory\n";
527     while (<PGMS>) {
528         s/\r?\n//;
529         $programname = $_;
530         if (-d $programname) { next; }  # Ignore directories
531         if ($programname eq "runtests") { next; } # Ignore self
532         if ($programname eq "checktests") { next; } # Ignore helper
533         if ($programname eq "configure") { next; } # Ignore configure script
534         if ($programname eq "config.status") { next; } # Ignore configure helper
535         if (-x $programname) { $found_exec++; }
536         if ($programname =~ /\.[cf]$/) { $found_src++; }
537     }
538     close PGMS;
539
540     if ($found_exec) {
541         print "Found executables\n" if $debug;
542         open (PGMS, "ls -1 |" ) || die "Cannot list programs\n";
543         while (<PGMS>) {
544             # Check for stop file
545             if (-s $stopfile) {
546                 # Exit because we found a stopfile
547                 print STDERR "Terminating test because stopfile $stopfile found\n";
548                 last;
549             }
550             s/\r?\n//;
551             $programname = $_;
552             if (-d $programname) { next; }  # Ignore directories
553             if ($programname eq "runtests") { next; } # Ignore self
554             if (-x $programname) {
555                 $total_run++;
556                 &RunMPIProgram( $programname, $np_default, "", "", "", "", "", "", "" );
557             }
558         }
559         close PGMS;
560     }
561     elsif ($found_src) {
562         print "Found source files\n" if $debug;
563         open (PGMS, "ls -1 *.c |" ) || die "Cannot list programs\n";
564         while (<PGMS>) {
565             if (-s $stopfile) {
566                 # Exit because we found a stopfile
567                 print STDERR "Terminating test because stopfile $stopfile found\n";
568                 last;
569             }
570             s/\r?\n//;
571             $programname = $_;
572             # Skip messages from ls about no files
573             if (! -s $programname) { next; }
574             $programname =~ s/\.c//;
575             $total_run++;
576             if (&BuildMPIProgram( $programname, "") == 0) {
577                 &RunMPIProgram( $programname, $np_default, "", "", "", "", "", "", "" );
578             }
579             else {
580                 # We expected to run this program, so failure to build
581                 # is an error
582                 $found_error = 1;
583                 $err_count++;
584             }
585             &CleanUpAfterRun( $programname );
586         }
587         close PGMS;
588     }
589 }
590 # Run the program.
591 # ToDo: Add a way to limit the time that any particular program may run.
592 # The arguments are
593 #    name of program, number of processes, name of routine to check results
594 #    init for testing, timelimit, and any additional program arguments
595 # If the 3rd arg is not present, the a default that simply checks that the
596 # return status is 0 and that the output is " No Errors" is used.
597 sub RunMPIProgram {
598     my ($programname,$np,$ResultTest,$InitForTest,$timeLimit,$progArgs,$progEnv,$mpiexecArgs,$xfail) = @_;
599     my $found_error   = 0;
600     my $found_noerror = 0;
601     my $inline = "";
602
603     &RunPreMsg( $programname, $np, $curdir );
604
605     unlink "err";
606
607     # Set a default timeout on tests (3 minutes for now)
608     my $timeout = $defaultTimeLimit;
609     if (defined($timeLimit) && $timeLimit =~ /^\d+$/) {
610         $timeout = $timeLimit;
611     }
612     $ENV{"MPIEXEC_TIMEOUT"} = $timeout;
613
614     # Run the optional setup routine. For example, the timeout tests could
615     # be set to a shorter timeout.
616     if ($InitForTest ne "") {
617         &$InitForTest();
618     }
619     print STDOUT "Env includes $progEnv\n" if $verbose;
620     print STDOUT "$mpiexec $wrapparg $mpiexecArgs $np_arg $np $program_wrapper ./$programname $progArgs\n" if $verbose;
621     print STDOUT "." if $showProgress;
622     # Save and restore the environment if necessary before running mpiexec.
623     if ($progEnv ne "") {
624         %saveEnv = %ENV;
625         foreach $val (split(/\s+/, $progEnv)) {
626             if ($val =~ /([^=]+)=(.*)/) {
627                 $ENV{$1} = $2;
628             }
629             else {
630                 print STDERR "Environment variable/value $val not in a=b form\n";
631             }
632         }
633     }
634     open ( MPIOUT, "$mpiexec $wrapparg $np_arg $np $mpiexecArgs $program_wrapper ./$programname $progArgs 2>&1 |" ) ||
635         die "Could not run ./$programname\n";
636     if ($progEnv ne "") {
637         %ENV = %saveEnv;
638     }
639     if ($ResultTest ne "") {
640         # Read and process the output
641         ($found_error, $inline) = &$ResultTest( MPIOUT, $programname );
642     }
643     else {
644         if ($verbose) {
645             $inline = "$mpiexec $wrapparg $np_arg $np $program_wrapper ./$programname\n";
646         }
647         else {
648             $inline = "";
649         }
650         while (<MPIOUT>) {
651             print STDOUT $_ if $verbose;
652             # Skip FORTRAN STOP
653             if (/FORTRAN STOP/) { next; }
654             $inline .= $_;
655             if (/^==[0-9]+== ?WARNING: ASan doesn't fully support/) {
656                 next;
657             }
658             if (/^\s*No [Ee]rrors\s*$/ && $found_noerror == 0) {
659                 $found_noerror = 1;
660             }
661             if (! /^\s*No [Ee]rrors\s*$/ && !/^\s*Test Passed\s*$/) {
662                 print STDERR "Unexpected output in $programname: $_";
663                 if (!$found_error) {
664                     $found_error = 1;
665                     $failed_tests .= $programname;
666                     $failed_tests .= " ";
667                     $err_count ++;
668                 }
669             }
670         }
671         if ($found_noerror == 0) {
672             print STDERR "Program $programname exited without No Errors\n";
673             if (!$found_error) {
674                 $found_error = 1;
675                 $failed_tests .= $programname;
676                 $failed_tests .= " ";
677                 $err_count ++;
678             }
679         }
680         $rc = close ( MPIOUT );
681         if ($rc == 0) {
682             # Only generate a message if we think that the program
683             # passed the test.
684             if (!$found_error) {
685                 $run_status = $?;
686                 $signal_num = $run_status & 127;
687                 if ($run_status > 255) { $run_status >>= 8; }
688                 print STDERR "Program $programname exited with non-zero status $run_status\n";
689                 if ($signal_num != 0) {
690                     print STDERR "Program $programname exited with signal $signal_num\n";
691                 }
692                 $failed_tests .= $programname;
693                 $failed_tests .= " ";
694                 $found_error = 1;
695                 $err_count ++;
696             }
697         }
698     }
699     if ($found_error) {
700         &RunTestFailed( $programname, $np, $curdir, $inline, $xfail );
701     }
702     else {
703         &RunTestPassed( $programname, $np, $curdir, $xfail );
704     }
705     &RunPostMsg( $programname, $np, $curdir );
706 }
707
708 # This version simply writes the mpiexec command out, with the output going
709 # into a file, and recording the output status of the run.
710 sub AddMPIProgram {
711     my ($programname,$np,$ResultTest,$InitForTest,$timeLimit,$progArgs,$progEnv,$mpiexecArgs, $xfail) = @_;
712
713     if (! -x $programname) {
714         print STDERR "Could not find $programname!";
715         return;
716     }
717
718     if ($ResultTest ne "") {
719         # This test really needs to be run manually, with this test
720         # Eventually, we can update this to include handleing in checktests.
721         print STDERR "Run $curdir/$programname with $np processes and use $ResultTest to check the results\n";
722         return;
723     }
724
725     # Set a default timeout on tests (3 minutes for now)
726     my $timeout = $defaultTimeLimit;
727     if (defined($timeLimit) && $timeLimit =~ /^\d+$/) {
728         # On some systems, there is no effective time limit on
729         # individual mpi program runs.  In that case, we may
730         # want to treat these also as "run manually".
731         $timeout = $timeLimit;
732     }
733     print BATOUT "export MPIEXEC_TIMEOUT=$timeout\n";
734
735     # Run the optional setup routine. For example, the timeout tests could
736     # be set to a shorter timeout.
737     if ($InitForTest ne "") {
738         &$InitForTest();
739     }
740
741     # For non-MPICH versions of mpiexec, a timeout may require a different
742     # environment variable or command line option (e.g., for Cray aprun,
743     # the option -t <sec> must be given, there is no environment variable
744     # to set the timeout.
745     $extraArgs = "";
746     if (defined($timeoutArgPattern) && $timeoutArgPattern ne "") {
747         my $timeArg = $timeoutArgPattern;
748         $timeoutArg =~ s/<SEC>/$timeout/;
749         $extraArgs .= $timeoutArg
750     }
751
752     print STDOUT "Env includes $progEnv\n" if $verbose;
753     print STDOUT "$mpiexec $np_arg $np $extraArgs $program_wrapper ./$programname $progArgs\n" if $verbose;
754     print STDOUT "." if $showProgress;
755     # Save and restore the environment if necessary before running mpiexec.
756     if ($progEnv ne "") {
757         # Need to fix:
758         # save_NAME_is_set=is old name set
759         # save_NAME=oldValue
760         # export NAME=newvalue
761         # (run)
762         # export NAME=oldValue (if set!)
763         print STDERR "Batch output does not permit changes to environment\n";
764     }
765     # The approach here is to move the test codes to a single directory from
766     # which they can be run; this avoids complex code to change directories
767     # and ensure that the output goes "into the right place".
768     $testCount++;
769     rename $programname, "$batrundir/$programname";
770     print BATOUT "echo \"# $mpiexec $np_arg $np $extraArgs $mpiexecArgs $program_wrapper $curdir/$programname $progArgs\" > runtests.$testCount.out\n";
771     # Some programs expect to run in the same directory as the executable
772     print BATOUT "$mpiexec $np_arg $np $extraArgs $mpiexecArgs $program_wrapper ./$programname $progArgs >> runtests.$testCount.out 2>&1\n";
773     print BATOUT "echo \$? > runtests.$testCount.status\n";
774 }
775
776 #
777 # Return value is 0 on success, non zero on failure
778 sub BuildMPIProgram {
779     my $programname = shift;
780     if (! -x $programname) {
781         die "Could not find $programname. Aborting.\n";
782     }
783     return 0;
784     # THE FOLLOWING IS DISABLED.
785     my $xfail = shift;
786     my $rc = 0;
787     if ($verbose) { print STDERR "making $programname\n"; }
788     if (! -x $programname) { $remove_this_pgm = 1; }
789     else { $remove_this_pgm = 0; }
790     my $output = `make $programname 2>&1`;
791     $rc = $?;
792     if ($rc > 255) { $rc >>= 8; }
793     if (! -x $programname) {
794         print STDERR "Failed to build $programname; $output\n";
795         if ($rc == 0) {
796             $rc = 1;
797         }
798         # Add a line to the summary file describing the failure
799         # This will ensure that failures to build will end up
800         # in the summary file (which is otherwise written by the
801         # RunMPIProgram step)
802         &RunPreMsg( $programname, $np, $curdir );
803         &RunTestFailed( $programname, $np, $curdir, "Failed to build $programname; $output", $xfail );
804         &RunPostMsg( $programname, $np, $curdir );
805     }
806     return $rc;
807 }
808
809 sub CleanUpAfterRun {
810     my $programname = $_[0];
811
812     # Check for that this program has exited.  If it is still running,
813     # issue a warning and leave the application.  Of course, this
814     # check is complicated by the lack of a standard access to the
815     # running processes for this user in Unix.
816     @stillRunning = &FindRunning( $programname );
817
818     if ($#stillRunning > -1) {
819         print STDERR "Some programs ($programname) may still be running:\npids = ";
820         for (my $i=0; $i <= $#stillRunning; $i++ ) {
821             print STDERR $stillRunning[$i] . " ";
822         }
823         print STDERR "\n";
824         # Remind the user that the executable remains; we leave it around
825         # to allow the programmer to debug the running program, for which
826         # the executable is needed.
827         print STDERR "The executable ($programname) will not be removed.\n";
828     }
829     else {
830         if ($remove_this_pgm && $clean_pgms) {
831             unlink $programname, "$programname.o";
832         }
833         $remove_this_pgm = 0;
834     }
835 }
836 # ----------------------------------------------------------------------------
837 sub FindRunning {
838     my $programname = $_[0];
839     my @pids = ();
840
841     my $logname = $ENV{'USER'};
842     my $pidloc = 1;
843     my $rc = open PSFD, "ps auxw -U $logname 2>&1 |";
844
845     if ($rc == 0) {
846         $rc = open PSFD, "ps -fu $logname 2>&1 |";
847     }
848     if ($rc == 0) {
849         print STDERR "Could not execute ps command\n";
850         return @pids;
851     }
852
853     while (<PSFD>) {
854         if (/$programname/) {
855             @fields = split(/\s+/);
856             my $pid = $fields[$pidloc];
857             # Check that we've found a numeric pid
858             if ($pid =~ /^\d+$/) {
859                 $pids[$#pids + 1] = $pid;
860             }
861         }
862     }
863     close PSFD;
864
865     return @pids;
866 }
867 # ----------------------------------------------------------------------------
868 #
869 # TestStatus is a special test that reports success *only* when the
870 # status return is NONZERO
871 sub TestStatus {
872     my $MPIOUT = $_[0];
873     my $programname = $_[1];
874     my $found_error = 0;
875
876     my $inline = "";
877     while (<$MPIOUT>) {
878         #print STDOUT $_ if $verbose;
879         # Skip FORTRAN STOP
880         if (/FORTRAN STOP/) { next; }
881         $inline .= $_;
882         # ANY output is an error. We have the following output
883         # exception for the Hydra process manager.
884         if (/=*/) { last; }
885         if (! /^\s*$/) {
886             print STDERR "Unexpected output in $programname: $_";
887             if (!$found_error) {
888                 $found_error = 1;
889                 $err_count ++;
890             }
891         }
892     }
893     $rc = close ( MPIOUT );
894     if ($rc == 0) {
895         $run_status = $?;
896         $signal_num = $run_status & 127;
897         if ($run_status > 255) { $run_status >>= 8; }
898     }
899     else {
900         # This test *requires* non-zero return codes
901         if (!$found_error) {
902             $found_error = 1;
903             $err_count ++;
904         }
905         $inline .= "$mpiexec returned a zero status but the program returned a nonzero status\n";
906     }
907     return ($found_error,$inline);
908 }
909 #
910 # TestTimeout is a special test that reports success *only* when the
911 # status return is NONZERO and there are no processes left over.
912 # This test currently checks only for the return status.
913 sub TestTimeout {
914     my $MPIOUT = $_[0];
915     my $programname = $_[1];
916     my $found_error = 0;
917
918     my $inline = "";
919     while (<$MPIOUT>) {
920         #print STDOUT $_ if $verbose;
921         # Skip FORTRAN STOP
922         if (/FORTRAN STOP/) { next; }
923         $inline .= $_;
924         if (/[Tt]imeout/) { next; }
925         # Allow 'signaled with Interrupt' (see gforker mpiexec)
926         if (/signaled with Interrupt/) { next; }
927         # Allow 'job ending due to env var MPIEXEC_TIMEOUT' (mpd)
928         if (/job ending due to env var MPIEXEC_TIMEOUT/) { next; }
929         # Allow 'APPLICATION TIMED OUT' (hydra)
930         if (/\[mpiexec@.*\] APPLICATION TIMED OUT/) { last; }
931         # ANY output is an error (other than timeout)
932         if (! /^\s*$/) {
933             print STDERR "Unexpected output in $programname: $_";
934             if (!$found_error) {
935                 $found_error = 1;
936                 $err_count ++;
937             }
938         }
939     }
940     $rc = close ( MPIOUT );
941     if ($rc == 0) {
942         $run_status = $?;
943         $signal_num = $run_status & 127;
944         if ($run_status > 255) { $run_status >>= 8; }
945     }
946     else {
947         # This test *requires* non-zero return codes
948         if (!$found_error) {
949             $found_error = 1;
950             $err_count ++;
951         }
952         $inline .= "$mpiexec returned a zero status but the program returned a nonzero status\n";
953     }
954     #
955     # Here should go a check of the processes
956     # open( PFD, "ps -fu $LOGNAME | grep -v grep | grep $programname |" );
957     # while (<PFD>) {
958     #
959     # }
960     # close PFD;
961     return ($found_error,$inline);
962 }
963 #
964 # TestErrFatal is a special test that reports success *only* when the
965 # status return is NONZERO; it ignores error messages
966 sub TestErrFatal {
967     my $MPIOUT = $_[0];
968     my $programname = $_[1];
969     my $found_error = 0;
970
971     my $inline = "";
972     while (<$MPIOUT>) {
973         #print STDOUT $_ if $verbose;
974         # Skip FORTRAN STOP
975         if (/FORTRAN STOP/) { next; }
976         $inline .= $_;
977         # ALL output is allowed.
978     }
979     $rc = close ( MPIOUT );
980     if ($rc == 0) {
981         $run_status = $?;
982         $signal_num = $run_status & 127;
983         if ($run_status > 255) { $run_status >>= 8; }
984     }
985     else {
986         # This test *requires* non-zero return codes
987         if (!$found_error) {
988             $found_error = 1;
989             $err_count ++;
990         }
991         $inline .= "$mpiexec returned a zero status but the program returned a nonzero status\n";
992     }
993     return ($found_error,$inline);
994 }
995
996 # ----------------------------------------------------------------------------
997 # Output routines:
998 #  RunPreMsg( programname, np, workdir ) - Call before running a program
999 #  RunTestFailed, RunTestPassed - Call after test
1000 #  RunPostMsg               - Call at end of each test
1001 #
1002 sub RunPreMsg {
1003     my ($programname,$np,$workdir) = @_;
1004     if ($xmloutput) {
1005         print XMLOUT "<MPITEST>$newline<NAME>$programname</NAME>$newline";
1006         print XMLOUT "<NP>$np</NP>$newline";
1007         print XMLOUT "<WORKDIR>$workdir</WORKDIR>$newline";
1008     }
1009 }
1010 sub RunPostMsg {
1011     my ($programname, $np, $workdir) = @_;
1012     if ($xmloutput) {
1013         print XMLOUT "</MPITEST>$newline";
1014     }
1015 }
1016 sub RunTestPassed {
1017     my ($programname, $np, $workdir, $xfail) = @_;
1018     if ($xmloutput) {
1019         print XMLOUT "<STATUS>pass</STATUS>$newline";
1020     }
1021     if ($tapoutput) {
1022         my $xfailstr = '';
1023         if ($xfail ne '') {
1024             $xfailstr = " # TODO $xfail";
1025         }
1026         print TAPOUT "ok ${total_run} - $workdir/$programname ${np}${xfailstr}\n";
1027     }
1028 }
1029 sub RunTestFailed {
1030     my $programname = shift;
1031     my $np = shift;
1032     my $workdir = shift;
1033     my $output = shift;
1034     my $xfail = shift;
1035
1036     if ($xmloutput) {
1037         my $xout = $output;
1038         # basic escapes that wreck the XML output
1039         $xout =~ s/</\*AMP\*lt;/g;
1040         $xout =~ s/>/\*AMP\*gt;/g;
1041         $xout =~ s/&/\*AMP\*amp;/g;
1042         $xout =~ s/\*AMP\*/&/g;
1043         # TODO: Also capture any non-printing characters (XML doesn't like them
1044         # either).
1045         print XMLOUT "<STATUS>fail</STATUS>$newline";
1046         print XMLOUT "<TESTDIFF>$newline$xout</TESTDIFF>$newline";
1047     }
1048
1049     if ($tapoutput) {
1050         my $xfailstr = '';
1051         if ($xfail ne '') {
1052             $xfailstr = " # TODO $xfail";
1053         }
1054         print TAPOUT "not ok ${total_run} - $workdir/$programname ${np}${xfailstr}\n";
1055         print TAPOUT "  ---\n";
1056         print TAPOUT "  Directory: $workdir\n";
1057         print TAPOUT "  File: $programname\n";
1058         print TAPOUT "  Num-procs: $np\n";
1059         print TAPOUT "  Date: \"" . localtime . "\"\n";
1060
1061         # The following would be nice, but it leads to unfortunate formatting in
1062         # the Jenkins web output for now.  Using comment lines instead, since
1063         # they are easier to read/find in a browser.
1064 ##        print TAPOUT "  Output: |\n";
1065 ##        # using block literal format, requires that all chars are printable
1066 ##        # UTF-8 (or UTF-16, but we won't encounter that)
1067 ##        foreach my $line (split m/\r?\n/, $output) {
1068 ##            chomp $line;
1069 ##            # 4 spaces, 2 for TAP indent, 2 more for YAML block indent
1070 ##            print TAPOUT "    $line\n";
1071 ##        }
1072
1073         print TAPOUT "  ...\n";
1074
1075         # Alternative to the "Output:" YAML block literal above.  Do not put any
1076         # spaces before the '#', this causes some TAP parsers (including Perl's
1077         # TAP::Parser) to treat the line as "unknown" instead of a proper
1078         # comment.
1079         print TAPOUT "## Test output (expected 'No Errors'):\n";
1080         foreach my $line (split m/\r?\n/, $output) {
1081             chomp $line;
1082             print TAPOUT "## $line\n";
1083         }
1084     }
1085 }
1086
1087 sub SkippedTest {
1088     my $programname = shift;
1089     my $np = shift;
1090     my $workdir = shift;
1091     my $reason = shift;
1092
1093     # simply omit from the XML output
1094
1095     if ($tapoutput) {
1096         print TAPOUT "ok ${total_seen} - $workdir/$programname $np  # SKIP $reason\n";
1097     }
1098 }
1099
1100 # ----------------------------------------------------------------------------
1101 # Alternate init routines
1102 sub InitQuickTimeout {
1103     $ENV{"MPIEXEC_TIMEOUT"} = 10;
1104 }