Logo AND Algorithmique Numérique Distribuée

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