#!/usr/bin/perl
#
#  This is a perl script to execute a single test driver program.  The script
#  will examine the return value of the test driver and capture STDOUT, STDERR
#  to files.  If the return value of the test driver is not zero, the test is
#  deemed a failure.  STDOUT and STDERR files will be place in a subdirectory
#  named temp and the files will be compared with verified STDOUT, STDERR files
#  in a subdirectory named verified.  If there are no STDOUT, STDERR files in
#  the verified directory, the captured STDOUT, STDERR files will be scanned
#  for words which indicate a failure.
#
#  Assumptions:  A verified subdirectory with STDOUT, STDERR files exists for
#                the test driver specified in the arguements if an exact
#                match of the streams STDOUT, STDERR is necessary.
#
#  Return values:
#                 Bit mapped values
#                 0    Test run successfull and all tests passed
#                 1    Verified directory did not exist
#                 8    STDOUT files did not compare
#                16    STDERR files did not compare
#                32    Test driver doesn't exist or is not executable
#                64    Test driver did not return zero
#
#  SYNOSIS:  runTest testDriverName
#
#  $Revison:  $  $Name: not supported by cvs2svn $
#  $Date: 2005-04-09 00:04:57 $
#
#  Copyright 2004 Maui High Performance Computering Center, University of Hawaii
#
################################################################################

# Provides functions for handling psS64 command line options
use Getopt::Long;

$verifiedDir = "verified";

# Assign variables based on the presence of command line options to the script
GetOptions(
    "reset!"       => \$reset,
    "resetStderr!" => \$resetStderr,
    "resetStdout!" => \$resetStdout,
    "verified=s"   => \$verifiedDir,
    "help!"        => \$help,
    "quiet!"       => \$quiet,
    "printpassfail!"     => \$verbose
);

if ($help || $#ARGV < 0) {
    print "Usage: runTest  [--help] [--verified=DIR] [--quiet] [--printpassfail] \\\n",
          "                [--reset] [--resetStderr] [--resetStdout] testfile(s)\n\n";
    exit(0);
}

if ($reset) {
    $resetStderr = 1;
    $resetStdout = 1;
}

# Initialize exit value
$exitValue = 0;

# Set up the PSLIB_ROOT environment variable if the user doesn't have it set
if ( $ENV{'PSLIB_ROOT'} ) {

	# Add PSLIB_ROOT/lib to LD_LIBRARY_PATH and DYLD_LIBRARY_PATH environment vars
	$ENV{'LD_LIBRARY_PATH'}   = "$ENV{'PSLIB_ROOT'}/src/.libs:$ENV{'LD_LIBRARY_PATH'}";
	$ENV{'DYLD_LIBRARY_PATH'} = "$ENV{'PSLIB_ROOT'}/src/.libs:$ENV{'DYLD_LIBRARY_PATH'}";
}

# Loop through the arguements passed to the script
foreach (@ARGV) {
    chomp;
    $testFile = $_;

    # Check if a temp directory already exists in the current work directory
    if ( !( -e "temp" ) ) {
        # Create temp directory to store STDOUT, STDERR files during test run
        `mkdir temp`;
    }

    # Check if a verified directory exists in the current work directory
    if ( !( -e $verifiedDir ) ) {

        # Display message that verified subdirectory doesn't exist
        print STDERR "        Verified directory doesn't exist.\n";

        # Exit script since the test cannot be run with verified files
        $exitValue = 1;
    }

    # Check if the test driver file exists and is executable
    if ( ( -e $testFile ) && ( -x $testFile ) ) {

        # Invoke the test driver
        system("./$testFile 1> temp/$testFile.stdout 2> temp/$testFile.stderr");

        # Check the return value of the test driver.  A value other than 0
        # indicates failure of the test driver unless the test driver is name
        # with a leading 'a' which indicates the test driver is expecting an
        # abort condition to terminate the driver.
        if (   ( $? != 0 && ( $testFile !~ /^A/i ) )
            || ( $? == 0 && ( $testFile =~ /^A/i ) ) )
        {

            # Display failure message with return value to user
            if ( $? != 0 && ( $testFile !~ /^A/i ) ) {
                print STDERR "Failed - Test Driver returned $?, expected 0.\n";
            }
            elsif ( $? == 0 && ( $testFile =~ /^A/i ) ) {
                print STDERR "Failed - Test Driver returned $?, expected abort.\n";
            }
            $exitValue |= 64;
        }

        # Test driver succeeded.

        # Create filtered version of stdout and stderr

        # Open the STDOUT file for reading
        open( OUTFILE, "< temp/$testFile.stdout" );

        # Open mod file to place filtered STDOUT
        open( MODFILE,  "> temp/$testFile.stdout.mod" );
        open( MODFILE2, "> $verifiedDir/$testFile.stdout" ) if $resetStdout;

        # Replace the variable date, time and host information with constants
        $hostname = `hostname`;
        chop $hostname;
        while (<OUTFILE>) {
            s/\s+\d+:\d+:\d+\w/<TIME>/g;
            s/\d+:\d+:\d+/<DATE>/g;
            s/$hostname\s*/<HOST>/g;
            s/: Line \d+/: Line <LINENO>/g;
            s/\(.*\:\d+\)/\(FILE\:LINENO\)/g;
            s/\s+[\_\-\/\.\w]+\:\d+/ FILE\:LINENO/g;
            s/allocate \d+ bytes at/allocate <N> bytes at/g;
            s/v\d+.\d+.\d+/vX.X.X/g;

            if (m/TestPoint:\s*([^\*]+)/) {
                $testfile = $1;
            }
            if (m/^---> TESTPOINT\s(\S+)/) {
                print "\t$testfile- $1\n" if ($verbose || $1 eq "FAILED");
                if ($1 eq "FAILED") {
                    print $testoutput;
                }
            }

            # Filter lines with malloc.  This is an artifact of memory testing
            # with the Mac testbed
            if ( !m/\*\*\*\smalloc/ ) {
                print MODFILE ($_);
                print MODFILE2 ($_) if $resetStdout;
            }
        }

        # Close mod file
        close(MODFILE);
        close(MODFILE2) if $resetStdout;

        # Close STDERR file
        close(OUTFILE);

        # Open the STDERR file for reading
        open( OUTFILE, "< temp/$testFile.stderr" );

        # Open mod file to place filtered STDERR
        open( MODFILE,  "> temp/$testFile.stderr.mod" );
        open( MODFILE2, "> $verifiedDir/$testFile.stderr" ) if $resetStderr;

        # Replace the variable date, time and host information with constants
        while (<OUTFILE>) {
            s/\s+\d+:\d+:\d+\w/<TIME>/g;
            s/\d+:\d+:\d+/<DATE>/g;
            s/$hostname\s*/<HOST>/g;
            s/: Line \d+/: Line <LINENO>/g;
            s/\(.*\:\d+\)/\(FILE\:LINENO\)/g;
            s/\s+[\_\-\/\.\w]+\:\d+/ FILE\:LINENO/g;
            s/allocate \d+ bytes at/allocate <N> bytes at/g;
            s/v\d+.\d+.\d+/vX.X.X/g;

            if (m/\*\*\*\*\*\* TESTPOINT \*\*\*\*\*\*/) {
                $testoutput = $_;
            }
            $testoutput += $_;

            if (m/ TestPoint:\s*([^\*]+)/) {
                $testfile = $1;
            }
            if (m/^---> TESTPOINT\s(\S+)/) {
                print "\t$testfile- $1\n" if ($verbose || $1 eq "FAILED");
                if ($1 eq "FAILED") {
                    print $testoutput;
                }
            }

            # Filter lines with malloc.  This is an artifact of memory testing
            # with the Mac testbed

            if ( !m/\*\*\*\smalloc/ ) {
                print MODFILE ($_);
                print MODFILE2 ($_) if $resetStderr;
            }
        }

        # Close mod file
        close(MODFILE);
        close(MODFILE2) if $resetStderr;

        # Close STDERR file
        close(OUTFILE);

        # Compare STDOUT capture with verified file
        $exitValue = &compareStream("$verifiedDir/$testFile.stdout");

        # Check exit value to determine if verified file doesn't exist
        if ( $exitValue & 2 ) {

            # STDOUT verified doesn't exist.  Search STDOUT capture
            # for strings indicating error or failure
            $exitValue |= &errorStrSearch("$testFile.stdout");
        }

        # Compare STDERR capture with verified file
        $exitValue |= &compareStream("$verifiedDir/$testFile.stderr");

        # Check exit value to determine if verified file doesn't exist
        if ( $exitValue & 4 ) {

            # STDERR verified doesn't exist.  Search STDERR capture
            # for strings indicating error or failure
            $exitValue |= &errorStrSearch("$testFile.stderr");
        }
   }
    else {

        # Since test driver doesn't exist or is not executable then display
        # message to user.
        print STDERR "\tNeed to specify an executable test file.\n";

        # Exit value set to indicate test driver doesn't exist or not executable
        $exitValue |= 32;
    }
}

# Exit for the script with exit value
# Ignore the first three bit flags in exitValue since there are not indicators
# of a failed test
if ( ( $exitValue >> 3 ) != 0 ) {
    print STDERR "Test failed - return status = $exitValue\n\n";
}
else {
    exit(0);
}
exit($exitValue);

################################################################################
#
#  SUBROUTINE: errorStrSearch
#
#      Description:  This subroutine will search the file specified in its
#                    parameter for error strings and if they do exists
#                    the appropriate value will be returned.
#
#      Parameter(s): fileName - input file to search for strings
#
#      Return:   0  -  No error strings found
#               64  -  Error strings found in STDOUT
#              128  -  Error strings found in STDERR
#
###############################################################################

sub errorStrSearch {
    local ($fileName)  = @_;
    local ($returnVal) = 0;

    # Open the captured file
    open( CAPT_FILE, "<temp/$fileName" );

    # Scan through all the lines of the file searching for error strings
    while (<CAPT_FILE>) {
        if (   m/FAIL/i
            || m/FAULT/i
            || m/ERROR/i
            || m/Not Found/i
            || m/SIGNAL/i
            || m/NO SUCH FILE/i
            || m/\|E\|/i
            || m/\|A\|/i )
        {
            print STDERR "\tFailed - File $fileName contains error strings.\n";
            if ( $fileName =~ m/out/ ) {
                $returnVal = 64;
            }
            elsif ( $fileName =~ m/err/ ) {
                $returnVal = 128;
            }
            last;
        }
    }

    # Close the capture file
    close(CAPT_FILE);

    # Return the return value
    return ($returnVal);
}

################################################################################
#
#  SUBROUTINE: compareStream
#
#      Description:  This subroutine will compare the captured stream file with
#                    a file in the verified directory if one exists.
#
#      Parameter(s): streamFile - input file to compare
#
#      Return:   0  -  Compare successful
#                2  -  STDOUT verified file doesn't exist
#                4  -  STDERR verified file doesn't exist
#                8  -  STDOUT verified file doesn't compare
#               16  -  STDERR verified file doesn't compare
#
###############################################################################

sub compareStream {
    local ($streamFile) = @_;
    local ($returnVal)  = 0;
    local ($tempFile)   = "";

    # Check for existence of verified STD stream files
    if ( !( -e $streamFile ) ) {

        # Set exit value bit 1 to indicate proper failure
        if ( $streamFile =~ /out$/ ) {
            $returnVal |= 2;
        }
        elsif ( $streamFile =~ /err$/ ) {
            $returnVal |= 4;
        }
    }
    else {

        # Verified STD stream file exists

        # Create name of the temp file to compare
        $tempFile = $streamFile;
        $tempFile =~ s/$verifiedDir/temp/;
        $tempFile = $tempFile . ".mod";

        # Perform difference on the STD stream files
        $diffstdout = `diff $streamFile $tempFile`;

        # Check the return value of the difference
        if ( $? != 0 ) {

            # Difference of STD stream files failed

            # Check for STDOUT in file name to convey appropirate return value
            if ( $streamFile =~ /out$/ ) {

                # Display message of the failure of difference to user
                print STDERR "\tFailed - STDOUT differences\n";

                # Exit value to indicate STDOUT did not compare
                $returnVal |= 8;
            }
            elsif ( $streamFile =~ /err$/ ) {

                # Display message of the failure of difference to user
                print STDERR "\tFailed - STDERR differences\n";

                # Exit value to indicate STDERR did not compare
                $returnVal |= 16;
            }
        }
    }

    # Return the result of the compare
    return ($returnVal);
}

