Logo AND Algorithmique Numérique Distribuée

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