#!/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
#                 2    Verified STDOUT file did not exist
#                 4    Verified STDERR file 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    STDOUT captured contains error strings
#               128    STDERR captured contains error strings
#               256    Test driver did not return zero
#
#  SYNOSIS:  runTest testDriverName
#
#  $Revison:  $  $Name: not supported by cvs2svn $
#  $Date: 2004-08-06 21:50:13 $
#
#  Copyright 2004 Maui High Performance Computering Center, University of Hawaii
#
################################################################################

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

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

if ($help) {
    print "Usage: runTest [--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'}) {
    # Use the directory directly above test
    $PSLIB_ROOT = `cd ..;pwd`;
    # Remove newline for the end of path returned
    chomp($PSLIB_ROOT);
    # Set the environment variable
    $ENV{'PSLIB_ROOT'} = $PSLIB_ROOT;
    # Display message that PSLIB_ROOT not found and set to 
    print("PSLIB_ROOT not found: set to $PSLIB_ROOT\n");
}

# Add PSLIB_ROOT/lib to LD_LIBRARY_PATH and DYLD_LIBRARY_PATH environment vars
$ENV{'LD_LIBRARY_PATH'} = "$ENV{'PSLIB_ROOT'}/lib:$ENV{'LD_LIBRARY_PATH'}";
$ENV{'DYLD_LIBRARY_PATH'} = "$ENV{'PSLIB_ROOT'}/lib:$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`;
    # Display message of new directory created
    print("Creating temp directory\n");
    }
    
    # Check if a verified directory exists in the current work directory
    if ( !( -e "verified" )) {
        # Display message that verified subdirectory doesn't exist
        print("        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) ) {
    
        # Display message the test driver is being executed
        print("--- Executing test driver: $testFile\n");
    
        # 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("        Failed - Test Driver expected 0 return value (Return value $?)\n");
            } elsif ( $? == 0 && ( $testFile =~ /^A/i) ) {
                print("        Failed - Test Driver expected abort (Return value $?)\n");
            }
            $exitValue |= 256;
        } else {
    
            # 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, "> verified/$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/<HOST>/g;
                s/: Line \d+/: Line <LINENO>/g;
                s/\:\d+/\:<LINENO>/g;
                # 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, "> verified/$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/<HOST>/g;
                s/: Line \d+/: Line <LINENO>/g;
                s/\:\d+/\:<LINENO>/g;
                # 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("verified/$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("verified/$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("        Need to specify an executable test file within directory.\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("Test failed - return status = $exitValue\n\n");
} else {
    print("Test successful\n\n");
}
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 ) {
            if ( $fileName =~ m/out/ ) {
                print("        Failed - File $fileName contains error strings.\n");
                $returnVal = 64;
            } elsif ( $fileName =~ m/err/ ) {
                print("        Failed - File $fileName contains error strings.\n");
                $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) ) {
            
        # Display message to user that verified STDOUT file doesn't exist
        print("        File $streamFile doesn't exist.\n");
            
        # 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/verified/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("        Failed - STDOUT difference\n$diffstdout\n");
  
               # Exit value to indicate STDOUT did not compare
               $returnVal |= 8;
            } elsif ( $streamFile =~ /err$/ ) {
               # Display message of the failure of difference to user
               print("        Failed - STDERR difference\n$diffstdout\n");

               # Exit value to indicate STDERR did not compare
               $returnVal |= 16;
            }
        }
    }
    
    # Return the result of the compare
    return($returnVal);
}

