Logo AND Algorithmique Numérique Distribuée

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