#! /usr/bin/env perl

 require "xml-tags.pl";

BEGIN
{
    push(@INC, $ENV{GLOBUS_LOCATION}. '/lib/perl');
}

use IO::File;
use File::Path;
use File::Compare;
use Sys::Hostname;
use Getopt::Long;
use strict;

my @tests          = ();
my $testtmp        = "";
my $relativepath   = "";
my $testdatadir    = "";
my $CleanUp        = 0;
my $GlobusLocation = $ENV{GLOBUS_LOCATION};
my $Logging        = 1;

# Command Line Arguments
my $TestDirectory  = "";
my $ScriptName     = "";

# Script Driving Variables
my $ServiceHost    = "localhost";
my $ServicePort    = 8443;
my $CommandLine    = "";
my $RslName        = "";
my $Arguments      = "";
my $SchedulerType  = "";
my $Subject        = "";
my $TestName       = "";
my $Output         = "";
my $ScriptName     = "";
my %StdOutVar      = ();
my $GridFTPPort    = 2811;

# Test Result Flags
my $DontTest       = 0;
my $ShouldFail     = 0;
my $FaultType      = '';
my $ExitCode       = -1;

# Global Variables
my $TestCount      = 0;
my $PassCount      = 0;
my $indent         = 0;
my $xfh;

# Test RSL Subroutine
sub test_rsl
{
    my $testrsl = "$RslName.xml";
    my $additionalrslfile = "$testtmp/$RslName.xml";
    my $testin = "$testtmp/$RslName.in";
    my $testout = "$testtmp/$RslName.$Output.out";
    my $testerr = "$testtmp/$RslName.$Output.err";
    my $test_rsl_fp = new IO::File("$testdatadir/$testrsl", '<');
    my $out_rsl_fp = new IO::File($additionalrslfile, '>');
    my $compare_stdout = new IO::File("$testtmp/$RslName.rout", '>');
    my @all_rsl;
    my $rc;
    my $globusrunout = "$testtmp/$RslName.$Output.cout";
    my $globusrunerr = "$testtmp/$RslName.$Output.cerr";

    # Opening Test Tag
    $indent = xml_write_open_tag($xfh,$indent,"Test");

    # Make A New RSL That Contains Dynamic Information
    while(<$test_rsl_fp>)
    {
        # Swap in Globus Location
        s/\$GLOBUS_LOCATION/$GlobusLocation/;

        # Swap in Local Hostname/Port
        s/LOCALHOST_NAME/$ServiceHost/;
        s/LOCALHOST_PORT/$ServicePort/;

        # Swap In GridFTP Port
        s/GRIDFTP_PORT/$GridFTPPort/;

        # Swap In Dynamic Fields
        s/TESTIN_FILE/$testin/;
        s/TESTOUT_FILE/$testout/;
        s/TESTERR_FILE/$testerr/;
        s/TEMP_DIR/$testtmp/;

        if ($Output eq 'Local')
        {
            s/<!--LOCAL OUTPUT//;
            s/LOCAL OUTPUT-->//;
        }
        elsif ($Output eq 'Staging')
        {
            s/<!--STAGING OUTPUT//;
            s/STAGING OUTPUT-->//;
        }
        elsif ($Output eq 'Streaming')
        {
            s/<!--STREAMING OUTPUT//;
            s/STREAMING OUTPUT-->//;
        }

        # Save Temp Copy, Write Output File
        push(@all_rsl, $_);
        $out_rsl_fp->print($_);
    }

    if(defined $test_rsl_fp)
    {
        $test_rsl_fp->close();
    }
    if(defined $out_rsl_fp)
    {
        $out_rsl_fp->close();
    }


    # Make A New stdout Compare File That Contains The Dynamic Fields
    if(!open(MASTEROUTFILE, "< $testdatadir/$RslName.out"))
    {
        if($Logging)
        {
            print LOGFILE "Unable To Open $testdatadir/$RslName.out\n";
        }
    }
    else
    {
        # For Each Line In The Master stdout File . . .
        while(<MASTEROUTFILE>)
        {
            # Substitute Dynamic Fields In This Record
            my $record = $_;
            foreach(keys %StdOutVar)
            {
                my $from = $_;
                my $to = $StdOutVar{$_};
                $record =~ s/$from/$to/g;
            }

            # Write The Record To The Compare stdout File
            $compare_stdout->print($record);
        }

        # Close Files
        $compare_stdout->close();
        close(MASTEROUTFILE);
    }

    # If there's an input file for this test
    if (-r "$testdatadir/$RslName.in")
    {
        open(IN, "<$testdatadir/$RslName.in");
        open(INOUT, ">$testtmp/$RslName.in");

        while (<IN>)
        {
            print INOUT $_;
        }
        close(IN);
        close(INOUT);
    }

    # Create Empty Output Files (eliminates 404 Errors for html links)
    system("touch $testtmp/$RslName.$Output.out");
    system("touch $testtmp/$RslName.$Output.err");
    system("touch $testtmp/$RslName.$Output.cout");
    system("touch $testtmp/$RslName.$Output.cerr");

    # Build The globusrun String
    my $RunString = "globusrun-ws -submit -factory https://$ServiceHost:$ServicePort ";

    $RunString .= "-factory-type $SchedulerType -f $testtmp/$testrsl -S ";
    if ($Subject ne '') {
        $RunString .= "-subject \"$Subject\" ";
    }

    # Capture The Run String Sans stdout/stderr capture
    my $ShortRunString = $RunString;

    # Capture Debug Info and Fault Type For Log Files
    $RunString .= "-dbg -pft ";

    # Enable streaming if appropriate
    if ($Output eq 'Streaming')
    {
        $RunString .= "-s ";
    }

    # Capture Client stdout and stderr
    $RunString .= "1\>$globusrunout 2\>$globusrunerr";

    # Print The Run String
    print LOGFILE "-------------------------------\n";
    print LOGFILE "Run String: \"$RunString\"\n";
    print LOGFILE "-------------------------------\n";

    # Write The Run String
    $indent = xml_write_field($xfh,$indent,"RunString",$ShortRunString);

    # Pass RSL to globusrun-ws
    system($RunString);

    # Process Results According To Test Criteria
    if($DontTest)
    {
        # Report Success
        $rc = 0;
    }
    else
    {
        # Grab globusrun-ws Return Code (Returns 0 On Success)
        $rc = $? >> 8;

        # Flip $rc If The Test Should Fail
        if($ShouldFail)
        {
            if ($rc != 0 && $FaultType ne '')
            {
                my $cerr = new IO::File("<$globusrunerr");

                while (<$cerr>) {
                    chomp;
                    if ($_ =~ m/^Fault Type:\s+(.*)/) {
                        if ($FaultType eq $1) {
                            $rc = 0;
                            last;
                        }
                    }
                }
                $cerr->close();
            }
            elsif ($ExitCode != -1 && $rc == $ExitCode)
            {
                $rc = 0;
            }
            else
            {
                $rc = 1;
            }
        }
        elsif ($Output ne '')
        {
            # Diff stdout (Returns 0 On Success)
            if ($Output eq 'Streaming')
            {
                $rc = $rc || File::Compare::compare("$testtmp/$RslName.rout", $globusrunout);
            }
            else
            {
                $rc = $rc || File::Compare::compare("$testtmp/$RslName.rout", $testout);
            }

            # Diff stderr (Returns 0 On Success)
            if ($Output ne 'Streaming' && -r "$testdatadir/$RslName.err")
            {
                $rc = $rc || File::Compare::compare("$testdatadir/$RslName.err", $testerr);
            }
        }
    }

    # Reset Temporary Test Flags
    $DontTest       = 0;
    $ShouldFail     = 0;
    $Output         = '';
    $FaultType      = '';

    # Format Output
    my $OutString = sprintf "%-15s", $RslName;
    $OutString .= sprintf "%-25s", $TestName;
    my $HostString = $ServiceHost . ":" . $ServicePort;
    $OutString .= sprintf "%-26s", $HostString;
    $OutString .= sprintf "%-8s", $SchedulerType;

    # Format Output, Accumulate Pass Count
    if($rc == 0)
    {
        $PassCount++;
        $OutString .= "PASS\n";
    }
    else
    {
        $rc = 1;
        $OutString .= "FAIL\n";
    }

    # Write XML Fields
    $indent = xml_write_field($xfh,$indent,"TestName",$RslName);
    $indent = xml_write_field($xfh,$indent,"TestDescription",$TestName);
    $indent = xml_write_field($xfh,$indent,"ServiceHostPort",$HostString);
    $indent = xml_write_field($xfh,$indent,"Scheduler",$SchedulerType);
    $indent = xml_write_field($xfh,$indent,"TempDirectory",$relativepath);
    $indent = xml_write_field($xfh,$indent,"Result",$rc);

# Append stdout to LOGFILE
print LOGFILE "-------------------------------\n";
print LOGFILE "*** stdout follows ***\n";

if(open(TESTOUT, $testout)) {
    while(<TESTOUT>) {
        print LOGFILE $_;
        }
    close(TESTOUT);
    }
else {
    print LOGFILE "ERROR: Unable To Open $testout\n";
    }

# Append stderr to LOGFILE
print LOGFILE "-------------------------------\n";
print LOGFILE "*** stderr follows ***\n";
if(open(TESTERR, $testerr)) {
    while(<TESTOUT>) {
        print LOGFILE $_;
        }
    close(TESTERR);
    }
else {
    print LOGFILE "ERROR: Unable To Open $testerr\n";
}

print LOGFILE "-------------------------------\n";

    # Print The Output String
    print $OutString;

    # Closing Test Tag
    $indent = xml_write_close_tag($xfh,$indent,"Test");
}

# main()
# {
    my $no_CleanUp = 1;
    # Grab The Command Line Arguments
    $TestDirectory = shift;
    $ScriptName = shift;

    $testdatadir = $TestDirectory;

    # Grab The Tmp Directory
    $testtmp = make_tmpdir();

    GetOptions( 'subject=s' => \$Subject,
                'logging|l!' => \$Logging,
                'n' => \$no_CleanUp);

    # Check for Debug
    if($Logging) {
        print "Logging To $TestDirectory/$ScriptName.log\n";
        $Logging = 1;
    }

    # Check for No Cleanup (For Collecting stdout/stderr Reference Files)
    if($no_CleanUp) {
        print "Will Not Delete Temp Directory $testtmp\n";
        $CleanUp = 0;
    }

    # TestDirectory And ScriptName Are Mandatory
    if(!$TestDirectory or !$ScriptName) {
        print "Format: run-tests.pl TestDirctory ScriptName [-l][-n]\n";
        print "        -n = No Cleanup (To Collect stdout/stderr Ref Files)\n";
        print "        -l = Turn On Logging\n\n";
        exit(1);
        }

    # Always Open The Logfile - Its Used To Pass stdout/stderr To Caller
    if(!open(LOGFILE, "> $TestDirectory/$ScriptName.log")) {
        print "Can't Open Log File\n";
        $Logging = 0;
        exit(1);
        }

    # Open Script File
    if(!OpenScriptFile()) {
        if($Logging) {
            print LOGFILE "Unable To Open test-script\n";
            }

        # Return False
        exit(1);
        }

    # Open an Output File
    if(!open(OUTPUTFILE, "> $TestDirectory/$ScriptName.out\n")) {
        if($Logging) {
            print LOGFILE "Unable To Open Output File\n";
            }
        # Return False
        exit(1);
        }


    # Create and Open index.html for Appending
    my $xmlfile = "$testtmp/index.xml";
    $xfh = new IO::File;
    if(!$xfh->open(">> $xmlfile")) {
        print "Unable To Open $xmlfile\n";
        exit(1);
        }

    # Write Opening Tag and Date/Time Record
    $indent = xml_write_open_tag($xfh,$indent,"TestRun");
    my $datetime = localtime();
    $indent = xml_write_field($xfh,$indent,"DateTime",$datetime);

    # Count RSLs In The Script
    $TestCount = RslCount();

    # Execute The Script
    parse_testscript();

    # Print The Results
    print OUTPUTFILE "$PassCount of $TestCount Tests Succeeded\n";
    print "\n$PassCount of $TestCount RSL's Succeeded\n\n";

    # Close Files
    CloseScriptFile();
    close (OUTPUTFILE);

    # CLose The Logfile
    if($Logging) {
        close(LOGFILE);
        }

    # Capture Counts
    $indent = xml_write_field($xfh,$indent,"TestCount",$TestCount);
    $indent = xml_write_field($xfh,$indent,"PassCount",$PassCount);

    # Write Closing Tag, Close File
    $indent = xml_write_close_tag($xfh,$indent,"TestRun");

    # Report Success If All Tests Passed
    if($TestCount == $PassCount) {
        exit(0);
        }

    # Otherwise Report Error
    exit(1);

# }
# ~main()


# make_tmpdir
sub make_tmpdir
{
    my $root;
    my $suffix = 'gram_scheduler_test_';
    my $created = 0;
    my $tmpname;
    my @acceptable = split(//, "abcdefghijklmnopqrstuvwxyz".
                      "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
                      "0123456789");
    if(exists($ENV{TMPDIR}))
    {
        $root = $ENV{TMPDIR};
    }
    else
    {
        $root = "$ENV{GLOBUS_LOCATION}/tmp";
    }
    while($created == 0)
    {
    $relativepath = $suffix .
                $acceptable[rand() * $#acceptable] .
                $acceptable[rand() * $#acceptable] .
                $acceptable[rand() * $#acceptable] .
                $acceptable[rand() * $#acceptable] .
                $acceptable[rand() * $#acceptable] .
                $acceptable[rand() * $#acceptable];
     $tmpname = $root . "/" . $relativepath;
     $ENV{TEMP_DIR} = $tmpname;
     $relativepath = "./$relativepath";
     $created = mkdir($tmpname, 0700);
     if($created)
     {
         if(-l $tmpname or ! -d $tmpname or ! -o $tmpname)
         {
          $created = 0;
         }
     }
    }
    return $tmpname;
}

# Open THe Script File
sub OpenScriptFile
{
    if(!open(TESTSCRIPT,"$TestDirectory/$ScriptName")) {
        # Return False
        return 0;
        }
    # Return True;
    return 1;
}

# Close The Script File
sub CloseScriptFile
{
   close(TESTSCRIPT);
}

# Count RSLs In TestScript
sub RslCount
{
    my $RslCount = 0;

    # Count RSL Instances
    seek TESTSCRIPT, 0, 0;
    while(<TESTSCRIPT>)
    {
        if(/^RslFile/)
        {
            $RslCount++;
        }
    }

   # Return Count
   return $RslCount;
}

# Parse And Execute TestScript
sub parse_testscript
{
    # Temp Variables
    my @temp;

    # Parse The Script
    seek TESTSCRIPT, 0, 0;
    while(<TESTSCRIPT>)
    {
        # Kill EOL
        chomp($_);

        # Filter Comments
        if(!$_ or /^#/ or /^ /)
        {
            # Discard Empty Lines And Comments
        }

        # Service Host
        elsif(/^ServiceHost/)
        {
            # Capture Value
            @temp = split;
            $ServiceHost = $temp[1];

            # Fixup if localhost
            if($ServiceHost eq "localhost")
            {
                $ServiceHost = hostname;
            }

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "Service Host:    $ServiceHost\n";
            }
        }

        # Service Port
        elsif(/^ServicePort/)
        {
            # Capture Value
            @temp = split;
            $ServicePort = $temp[1];

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "Service Port:    $ServicePort\n";
            }
        }

        # GridFTP Port
        elsif(/^GridFTPPort/)
        {
            # Capture Value
            @temp = split;
            $GridFTPPort = $temp[1];

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "GridFTP Port:    $GridFTPPort\n";
            }
        }

        # Test Should Fail
        elsif(/^ShouldFail/)
        {
            @temp = split;
            $ShouldFail = 0;
            if($temp[1] eq "Yes" or $temp[1] eq "yes")
            {
                $ShouldFail = 1;
            }

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "ShouldFail:      $temp[1]\n";
            }
        }

        # Fault Type to expect
        elsif(/^FaultType/)
        {
            @temp = split;
            $FaultType = $temp[1];

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "FaultType:       $temp[1]\n";
            }
        }

        # Exit Code to expect from program
        elsif(/^ExitCode/)
        {
            @temp = split;
            $ExitCode = $temp[1];

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "ExitCode:       $temp[1]\n";
            }
        }

        # Test Nothing
        elsif(/^DontTest/)
        {
            @temp = split;
            $DontTest = 0;
            if($temp[1] eq "Yes" or $temp[1] eq "yes")
            {
                $DontTest = 1;
            }

            # Print
            if($Logging)
            {
                print LOGFILE "DontTest:        $temp[1]\n";
            }
        }

        # Command Line
        elsif(/^CommandLine/)
        {
            # Capture Value
            @temp = split;
            $CommandLine = $temp[1];

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "Command Line:    $CommandLine\n";
            }
        }

        # Output
        elsif (/^Output/)
        {
            # Capture Value
            @temp = split;
            $Output = $temp[1];

            if($Logging)
            {
                print LOGFILE "Output:        $Output\n";
            }
        }

        # RslFile
        elsif(/^RslFile/)
        {
            # Capture Value
            @temp = split;
            $RslName = $temp[1];

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "RSL Name:        $RslName\n";
            }

            # Put A Marker In The Log File
            print LOGFILE "-------------------------------\n";
            print LOGFILE "Executing RSL:  ./$TestDirectory/$RslName.xml\n";
            print LOGFILE "-------------------------------\n";

            # Pass The RSL To test_rsl For Eexecution
            test_rsl($RslName);
        }

        # SchedulerType
        elsif(/^SchedulerType/)
        {
            # Capture Value
            @temp = split;
            $SchedulerType = $temp[1];

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "Scheduler Type:  $SchedulerType\n";
            }
        }

        # Subject
        elsif(/^Subject/)
        {
            # Capture Value
            s/Subject//;
            s/^\s+//;
            $Subject = $_;

            # Substitute If The Argument Is An Environment Variable
            if($Subject =~ /^\$ENV{/) #}
            {
                my $env = eval $Subject;
                $Subject = $env;
            }

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "Subject:         $Subject\n";
            }
        }

        # Arguments
        elsif(/^Arguments/)
        {
            $Arguments = $_;

            # Strip "Arguments" Tag
            $Arguments =~ s/Arguments//;

            # Strip Leading Spaces
            $Arguments =~ s/^\s+//;

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "Arguments:       $Arguments\n";
            }
        }

        # Test  Name
        elsif(/^TestName/)
        {
            $TestName = $_;

            # Strip "TestName" Tag
            $TestName =~ s/TestName//;

            # Strip Leading Spaces
            $TestName =~ s/^\s+//;

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "TestName:        $TestName\n";
            }
        }

        # Script Name
        elsif(/^ScriptName/)
        {
            $ScriptName = $_;

            # Strip "ScriptName" Tag
            $ScriptName =~ s/ScriptName//;

            # Strip Leading Spaces
            $ScriptName =~ s/^\s+//;

            # Print (For Now)
            if($Logging)
            {
                print LOGFILE "ScriptName:      $ScriptName\n";
            }

            # Print Header For Output File
            print "------------------------------------------------\n";
            print OUTPUTFILE "$ScriptName\n";
            print "$ScriptName\n\n";
    
            # Capture The Relative Path To The Test Directory
            print OUTPUTFILE "$relativepath\n";
        }

        # Capture Standard Out Substitution Variables
        elsif(/^StdOutVar/)
        {
            my @temp = split;

            # See If The Argument Is An Environment Variable
            if($temp[2] =~ /^\$ENV{/) #}
            {
                my $env = eval $temp[2];
                $StdOutVar{$temp[1]} = $env;
            }

            # Handle As A Literal
            else
            {
                $StdOutVar{$temp[1]} = $temp[2];
            }

            # Print Args For Now
            if($Logging)
            {
                print LOGFILE "StdOutVar:       $temp[1]     $temp[2]\n";
            }
        }

        # None Of The Above
        else
        {
            if($Logging)
            {
                print LOGFILE "Other:           $_\n";
            }
        }
    }
}


END
{
    if(-d $testtmp and -o $testtmp)
    {
        if($CleanUp)
        {
            File::Path::rmtree($testtmp);
        }
    }
}

