Logo AND Algorithmique Numérique Distribuée

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