#!/usr/bin/perl -w
#
#  Simple test driver script.
#
#  Execute each file in the current directory which has a name of
# testFoo.
#
#  Warn if any of the scripts return a non-zero exit code - this
# is a failure.
#
# Steve
# ---
# www.steve.org.uk
#


use Getopt::Long;
use strict;


#
# Global flags set by the command line arguments - if present.
#
my $SHOW_HELP    = 0;
my $SHOW_VERSION = 0;
my $TEST_ALL     = 0;
my $TEST_QUIETLY = 0;

# The CVS revision of this file
my $VERSION_NUMBER = '$Revision: 1.1.1.1 $';






#  Parse the options.
&parseArguments();

#
#  Quiet implies all
if ( $TEST_QUIETLY )
{
    $TEST_ALL = 1;
}

#
#  Take any immdiate actions.
#
if ( $SHOW_HELP )
{
    &showHelp();
    exit;
}

if ( $SHOW_VERSION )
{
    &showVersion();
    exit;
}


#
#  The main part of the script.
#
my @TESTS = ();
if ( $TEST_ALL )
{
    @TESTS = &getAllTestScripts();
}
else
{
    #
    # Add each supplied filename/testname to the list of tests.
    while( my $file = shift )
    {
	push @TESTS, $file;
    }
}

#
#  Run the given array of tests.
#
&runTests(@TESTS);

exit;




#
#  Return an array of each of the test scripts we can run.
#
sub getAllTestScripts()
{
    my @files = sort( glob( "test*" ) );
    my @TESTS = ();

    foreach my $file  (@files)
    {
	# Skip autosaved files.
	next if ( $file =~ /(.*)~$/ );

	# Skip files with a data/out suffix, these are connonical
	# files to accompany tests - if needed.
	next if ( $file =~ /(.*)\.out$/i );
	next if ( $file =~ /(.*)\.data$/i );

	push @TESTS, $file;
    }

    return( @TESTS );
}



#
#  Run each filename given to us as a test, reporting appropriately.
#
sub runTests(@)
{
    my (@files ) = (@_);

    #  Counts of the tests past, failed, and ran.
    my $passed = 0;
    my $failed = 0;
    my $count  = 0;

    foreach my $file ( @files )
    {
	# Get the description of the test from the file itself
	my $desc = &getDescription( $file );

	if ( ! $TEST_QUIETLY )
	{
	    print "$count. $desc [";
	}

	# Make test file executable if it's not already.
	if ( ! -x "./$file" )
	{
	   system( "chmod", "755", $file ); 
	}

	# Run the test.
	my $ret  = &runCommand( "./" . $file );
	if ( $ret != 0 )
	{
	    $failed ++;

	    if ( $TEST_QUIETLY )
	    {
		print "$desc - FAILED\n";
	    }
	    else
	    {
		print "FAILED]\n";
	    }
	}
	else
	{
	    $passed ++;

	    if ( !$TEST_QUIETLY )
	    {
		print "OK]\n";
	    }
	}

	$count++;
    }


    #  Show some statistics.
    if  ($count > 0 )
    {
	my $totalTests = $passed + $failed;
	my $failPer    = (($failed / $totalTests) * 100 );

	#
	# Only want two digits.
	#
	if ( $failPer =~ /([0-9]+)\.([0-9][0-9]).*/ )
	{
	    $failPer = $1 . "." . $2;
	}

	print "Passed $passed/$totalTests tests  Failed: $failed/$totalTests (Failure rate $failPer\%)\n";
    }
}



#
#  Get the description of the test contained in a given file.
#
#  This is the second line of the file.
#
sub getDescription( $ )
{
  my ( $file ) = (@_);
  open(FILE, "<$file" ) or die "Cannot read test file: $file - $!\n";
  my @lines = <FILE>;
  close( FILE );

  my $desc = $lines[ 1 ];
  chomp( $desc );
  if ( $desc =~ /^\#([ \t]+)(.*)/ )
    {
      $desc = $2;
    }
  
  return( $desc );
}
 

#
#  Run a test command, pass the return code back to the caller.
#
#  [In our context a return code of '0' is passed, and anything else is
# a failure].
#
sub runCommand( $ )
{
  my ($command ) = (@_);

  my $retcode = system $command;
  $retcode = int($retcode/256);
  
  return( $retcode );
}



#
#  Handle our minimal command parsing.
#
sub parseArguments()
{

    GetOptions(
               "help", \$SHOW_HELP,
               "version", \$SHOW_VERSION,
	       "all", \$TEST_ALL,
	       "fail", \$TEST_QUIETLY
               );
}


#  Show help for this script.
#
sub showHelp()
{
    showVersion();
    print <<END_OF_USAGE;

Usage: runtests options [test1] [test2] .. [testN]

Options:
    --all              Run all tests.
    --fail             Only display failure messages.
    --version          Show the version number.
    --help             Show this help.
END_OF_USAGE
}


#
#  Show the version number of this script.
#
sub showVersion()
{
    my $revision = $VERSION_NUMBER;

    #
    # Extract the version from the CVS revision marker,
    # the only tricky bit is making sure the words "$" Revision " $"
    # don't apear here - because they'd be replaced - this
    # has confused me before.
    #
    if (  $VERSION_NUMBER =~ /\$([a-zA-Z:]+) ([0-9\.]+) \$/ )
    {
        $revision = $2;
    }

    print "runtests - version $revision - http://www.steve.org.uk/\n";

}



=head1 NAME

runtests - A simple script for running test scripts.

=head1 SYNOPSIS

    ./runtests --all

=head1 DESCRIPTION

This script is a simple driver for running a series of automated tests.

Each test script is assumed to be an executable script within the current
directory which will exit with a significant return code.  An exit code
of '0' means the test succeeded, whilst any other exit code denotes an
error.

Scripts are named testFoo, where 'Foo' may be a descriptive name describing
the nature of the test.

Each test should contain a comment as it's second line, which will be printed
when the test is executed, for example this would be the start of a test
script testNOP:

         #!/bin/sh
         # Test NOP - Make no test
         exit 0


=head2 Options

  The following options are supported:

    --all              Run all tests.
    --fail             Only display failure messages.
    --version          Show the version number.
    --help             Show this help.

=head1 AUTHOR

  Part of GNUMP3d, the MP3/OGG/Audio streaming server.

Steve - http://www.gnump3d.org/

=head1 SEE ALSO

L<gnump3d>

=cut
