417 lines
14 KiB
Perl
417 lines
14 KiB
Perl
#!/usr/bin/env perl
|
|
#=======================================================================
|
|
#
|
|
# Parse a cime5 cs.status file to give summary output
|
|
#
|
|
# Usage:
|
|
#
|
|
# ./parse_cime.cs.status <cs.status.id filename>
|
|
#
|
|
# Erik Kluzek
|
|
# Sep/19/2016
|
|
#
|
|
#=======================================================================
|
|
use Cwd;
|
|
use strict;
|
|
#use diagnostics;
|
|
use English;
|
|
use Getopt::Long;
|
|
use IO::File;
|
|
|
|
#-----------------------------------------------------------------------------------------------
|
|
# Set the directory that contains this scripts. If the command was issued using a
|
|
# relative or absolute path, that path is in $ProgDir. Otherwise assume the
|
|
# command was issued from the current working directory.
|
|
|
|
sub GetNameNDir {
|
|
(my $ProgName = $0) =~ s!(.*)/!!; # name of this script
|
|
my $ProgDir = $1; # name of directory containing this script -- may be a
|
|
# relative or absolute path, or null if the script is in
|
|
# the user's PATH
|
|
my $cmdline = "@ARGV"; # Command line arguments to script
|
|
my $cwd = getcwd(); # current working directory
|
|
my $scrdir; # absolute pathname of directory that contains this script
|
|
my $nm = "$ProgName::"; # name to use if script dies
|
|
if ($ProgDir) {
|
|
$scrdir = absolute_path($ProgDir);
|
|
} else {
|
|
$scrdir = $cwd;
|
|
}
|
|
return( $ProgName, $scrdir );
|
|
}
|
|
|
|
|
|
#-----------------------------------------------------------------------------------------------
|
|
|
|
sub usage {
|
|
my $ProgName = shift;
|
|
die <<EOF;
|
|
SYNOPSIS
|
|
$ProgName <cs.status file> [options]
|
|
REQUIRED OPTIONS
|
|
<cs.status files> cime5 cs.status. file(s) that will be run and parsed
|
|
At least one file needs to be given, but you can also
|
|
give a list of space seperated files.
|
|
OPTIONS
|
|
-die_on_duplicate Die if find a duplicate testname
|
|
-summarize [or -s] Summarize results into lists of tests in categories (pend, pass, fail etc.)
|
|
-sum_results_perline Summarize results categories of each test into one line
|
|
-help [or -h] Print usage to STDOUT.
|
|
-verbose [or -v] Make output more verbose.
|
|
|
|
-summarize and -sum_results_perline can NOT both be asked for as they contrdict each other.
|
|
EOF
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------------------------
|
|
|
|
|
|
sub process_cmdline {
|
|
# Process command-line options.
|
|
my $ProgName = shift;
|
|
|
|
my %opts = (
|
|
csstatusfiles_ref => undef,
|
|
sumintocats => 0,
|
|
sumperline => 0,
|
|
dieondup => 0,
|
|
help => 0,
|
|
verbose => 0,
|
|
);
|
|
|
|
GetOptions(
|
|
"h|help" => \$opts{'help'},
|
|
"s|summarize" => \$opts{'sumintocats'},
|
|
"die_on_duplicate" => \$opts{'dieondup'},
|
|
"sum_results_perline" => \$opts{'sumperline'},
|
|
"v|verbose" => \$opts{'verbose'},
|
|
) or usage($ProgName);
|
|
|
|
# Give usage message.
|
|
usage($ProgName) if $opts{'help'};
|
|
|
|
# If bad input
|
|
if ( $opts{'sumintocats'} && $opts{'sumperline'} ) {
|
|
print "ERROR: options -summarize and -sum_results_perline contradict each other, choose one or the other or neither\n";
|
|
usage($ProgName);
|
|
}
|
|
|
|
# Get cs.status filenames
|
|
$opts{'csstatusfiles_ref'} = \@ARGV;
|
|
|
|
my $files_ref = $opts{'csstatusfiles_ref'};
|
|
|
|
if ( $#$files_ref == -1 ) {
|
|
print "ERROR: cs.status filename(s) was (were) NOT input\n";
|
|
usage($ProgName);
|
|
}
|
|
|
|
foreach my $file ( @$files_ref ) {
|
|
if ( ! -x $file ) {
|
|
print "ERROR: cs.status filename does NOT exist: $file\n";
|
|
usage($ProgName);
|
|
}
|
|
}
|
|
|
|
return( %opts );
|
|
}
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
sub absolute_path {
|
|
#
|
|
# Convert a pathname into an absolute pathname, expanding any . or .. characters.
|
|
# Assumes pathnames refer to a local filesystem.
|
|
# Assumes the directory separator is "/".
|
|
#
|
|
my $path = shift;
|
|
my $cwd = getcwd(); # current working directory
|
|
my $abspath; # resulting absolute pathname
|
|
|
|
# Strip off any leading or trailing whitespace. (This pattern won't match if
|
|
# there's embedded whitespace.
|
|
$path =~ s!^\s*(\S*)\s*$!$1!;
|
|
|
|
# Convert relative to absolute path.
|
|
|
|
if ($path =~ m!^\.$!) { # path is "."
|
|
return $cwd;
|
|
} elsif ($path =~ m!^\./!) { # path starts with "./"
|
|
$path =~ s!^\.!$cwd!;
|
|
} elsif ($path =~ m!^\.\.$!) { # path is ".."
|
|
$path = "$cwd/..";
|
|
} elsif ($path =~ m!^\.\./!) { # path starts with "../"
|
|
$path = "$cwd/$path";
|
|
} elsif ($path =~ m!^[^/]!) { # path starts with non-slash character
|
|
$path = "$cwd/$path";
|
|
}
|
|
|
|
my ($dir, @dirs2);
|
|
my @dirs = split "/", $path, -1; # The -1 prevents split from stripping trailing nulls
|
|
# This enables correct processing of the input "/".
|
|
|
|
# Remove any "" that are not leading.
|
|
for (my $i=0; $i<=$#dirs; ++$i) {
|
|
if ($i == 0 or $dirs[$i] ne "") {
|
|
push @dirs2, $dirs[$i];
|
|
}
|
|
}
|
|
@dirs = ();
|
|
|
|
# Remove any "."
|
|
foreach $dir (@dirs2) {
|
|
unless ($dir eq ".") {
|
|
push @dirs, $dir;
|
|
}
|
|
}
|
|
@dirs2 = ();
|
|
|
|
# Remove the "subdir/.." parts.
|
|
foreach $dir (@dirs) {
|
|
if ( $dir !~ /^\.\.$/ ) {
|
|
push @dirs2, $dir;
|
|
} else {
|
|
pop @dirs2; # remove previous dir when current dir is ..
|
|
}
|
|
}
|
|
if ($#dirs2 == 0 and $dirs2[0] eq "") { return "/"; }
|
|
$abspath = join '/', @dirs2;
|
|
return( $abspath );
|
|
}
|
|
|
|
sub run_csstatus {
|
|
# run a cs.status file and parse it's output
|
|
my ( $csstatusfilename, $verbose, $csstatus_ref, $dieondup ) = @_;
|
|
|
|
if ( ! -x $csstatusfilename ) {
|
|
die "ERROR: cs.status file does NOT exist or can not execute: $csstatusfilename\n";
|
|
}
|
|
my $csdate = undef;
|
|
if ( $csstatusfilename =~ /cs.status.([0-9_]+)/ ) {
|
|
$csdate = $1;
|
|
chomp( $csdate );
|
|
}
|
|
if ( $verbose ) { print "Parse file: $csstatusfilename\n"; }
|
|
my @lines = `$csstatusfilename`;
|
|
while ( my $line = shift(@lines) ) {
|
|
if ( $line =~ /([^ ]+) \(Overall: ([^ ,]+)\)/ ) {
|
|
my $test = $1;
|
|
my $over = $2;
|
|
my $fails = ""; my $passes = ""; my $pendings = "";
|
|
my $newline;
|
|
my $bfail = 0;
|
|
if ( $verbose ) { print "$test\n"; }
|
|
do {
|
|
$newline = shift(@lines);
|
|
if ( $newline =~ /FAIL[ ]+$test ([^ ]+)/ ) {
|
|
$fails .= " $1";
|
|
chomp( $fails );
|
|
if ( $1 eq "BASELINE" ) {
|
|
if ( $newline =~ /ERROR BFAIL baseline directory/ ) {
|
|
$bfail = 1;
|
|
}
|
|
}
|
|
} elsif ( $newline =~ /PASS[ ]+$test ([^ ]+)/ ) {
|
|
$passes .= " $1";
|
|
chomp( $passes );
|
|
} elsif ( $newline =~ /PEND[ ]+$test ([^ ]+)/ ) {
|
|
$pendings .= " $1";
|
|
chomp( $pendings );
|
|
} elsif ( (! $newline) || ($newline =~ /Overall:/) ) {
|
|
} else {
|
|
if ( $verbose ) { print "ERROR: parsing line: $newline\n"; }
|
|
}
|
|
} until ( (! $newline) || ($newline =~ /Overall:/) );
|
|
if ( $newline ) {
|
|
unshift( @lines, $newline );
|
|
}
|
|
if ( $over eq "NLFAIL" ) {
|
|
$over = "PASS";
|
|
}
|
|
elsif ( $over eq "NLCOMP" ) {
|
|
$over = "PASS";
|
|
}
|
|
elsif ( $over eq "DIFF" ) {
|
|
if ( $bfail ) {
|
|
$over = "FAIL_BDNE";
|
|
} else {
|
|
$over = "DIFF";
|
|
}
|
|
}
|
|
if ( exists($$csstatus_ref{$test}) ) {
|
|
if ( $dieondup ) {
|
|
die "ERROR: Already had a test that matches this one: $test\n";
|
|
}
|
|
next;
|
|
}
|
|
$$csstatus_ref{$test}{'over'} = $over;
|
|
$$csstatus_ref{$test}{'FAIL'} = $fails;
|
|
$$csstatus_ref{$test}{'PASS'} = $passes;
|
|
$$csstatus_ref{$test}{'PEND'} = $pendings;
|
|
if ( ! $newline ) { last; }
|
|
} else {
|
|
if ( $verbose ) { print( "WARNING: Didn't parse following line:\n$line" ); }
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
sub print_status {
|
|
# Print status info for each test
|
|
|
|
my %csstatus = @_;
|
|
|
|
foreach my $key ( keys(%csstatus) ) {
|
|
foreach my $type ( "PASS", "FAIL", "PEND" ) {
|
|
if ( $csstatus{$key}{$type} ne "" ) {
|
|
foreach my $phase ( split( / /, $csstatus{$key}{$type}) ) {
|
|
if ( $phase =~ /[^ ]+/ ) {
|
|
printf( "%-10s %-90s %s\n", $type, $key, $phase );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub print_sumperline {
|
|
# Print summary info for each test
|
|
|
|
my %csstatus = @_;
|
|
|
|
foreach my $key ( keys(%csstatus) ) {
|
|
printf( "%-10s %-90s Passing: %s\n", $csstatus{$key}{'over'}, $key, $csstatus{$key}{'PASS'} );
|
|
if ( $csstatus{$key}{'FAIL'} ne "" ) {
|
|
printf( "%-10s %-90s %s\n", "FAIL", $key, $csstatus{$key}{'FAIL'} );
|
|
}
|
|
if ( $csstatus{$key}{'PEND'} ne "" ) {
|
|
printf( "%-10s %-90s %s\n", "PEND", $key, $csstatus{$key}{'PEND'} );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
sub print_categories {
|
|
# Seperate tests into categories
|
|
|
|
my $scrdir = shift(@_);
|
|
my %csstatus = @_;
|
|
|
|
my $expectedfailfile = "$scrdir/components/clm/cime_config/testdefs/ExpectedTestFails.xml";
|
|
if ( ! -f $expectedfailfile ) {
|
|
$expectedfailfile = "$scrdir/cime_config/testdefs/ExpectedTestFails.xml";
|
|
}
|
|
my @passes;
|
|
my @fails;
|
|
my @pendings;
|
|
my @compares_diff;
|
|
my @compares_diff_nobase;
|
|
my @keys = sort( keys(%csstatus) );
|
|
foreach my $key ( @keys ) {
|
|
if ( $csstatus{$key}{'over'} eq "PASS" ) {
|
|
push( @passes, $key );
|
|
} elsif ( $csstatus{$key}{'over'} eq "DIFF" ) {
|
|
push( @passes, $key );
|
|
push( @compares_diff, $key );
|
|
} elsif ( $csstatus{$key}{'over'} eq "FAIL_BDNE" ) {
|
|
push( @passes, $key );
|
|
push( @compares_diff_nobase, $key );
|
|
} elsif ( $csstatus{$key}{'over'} eq "FAIL" ) {
|
|
push( @fails, $key );
|
|
} elsif ( $csstatus{$key}{'over'} eq "PEND" ) {
|
|
push( @pendings, $key );
|
|
} else {
|
|
print( "WARNING: unclassified overall status: $key, $csstatus{$key}{'over'}\n" );
|
|
}
|
|
}
|
|
print( "================================================================================\n" );
|
|
print( "Test summary\n" );
|
|
printf( "%d Total tests\n", $#keys+1 );
|
|
printf( "%d Tests passed\n", $#passes+1 );
|
|
printf( "%d Tests compare different to baseline\n", $#compares_diff+1 );
|
|
printf( "%d Tests are new where there is no baseline\n", $#compares_diff_nobase+1 );
|
|
printf( "%d Tests pending\n", $#pendings+1 );
|
|
printf( "%d Tests failed\n", $#fails+1 );
|
|
print( "================================================================================\n" );
|
|
|
|
if ( $#passes >= 0 ) {
|
|
print( "================================================================================\n" );
|
|
print( "These tests passed\n" );
|
|
print( "================================================================================\n" );
|
|
foreach my $key ( @passes ) {
|
|
my $expect = "";
|
|
`grep $key $expectedfailfile > /dev/null`;
|
|
if ( $? == 0 ) { $expect = "FAILED PREVIOUSLY"; }
|
|
print( "$key\t\t\t$expect\n" );
|
|
}
|
|
}
|
|
if ( $#compares_diff >= 0 ) {
|
|
print( "================================================================================\n" );
|
|
print( "These tests compare different to the baseline\n" );
|
|
print( "================================================================================\n" );
|
|
foreach my $key ( @compares_diff ) {
|
|
print( "$key\n" );
|
|
}
|
|
}
|
|
if ( $#compares_diff_nobase >= 0 ) {
|
|
print( "================================================================================\n" );
|
|
print( "These tests don't have a baseline to compare to\n" );
|
|
print( "================================================================================\n" );
|
|
foreach my $key ( @compares_diff_nobase ) {
|
|
print( "$key\n" );
|
|
}
|
|
}
|
|
if ( $#pendings >= 0 ) {
|
|
print( "================================================================================\n" );
|
|
print( "These tests are pending (some tests may fail in the pending state)\n" );
|
|
print( "================================================================================\n" );
|
|
foreach my $key ( @pendings ) {
|
|
my $expect = "";
|
|
`grep $key $expectedfailfile > /dev/null`;
|
|
if ( $? == 0 ) { $expect = "EXPECTED"; }
|
|
print( "$key\t\t$expect\n" );
|
|
}
|
|
}
|
|
if ( $#fails >= 0 ) {
|
|
print( "================================================================================\n" );
|
|
print( "These tests failed\n" );
|
|
print( "================================================================================\n" );
|
|
foreach my $key ( @fails ) {
|
|
my $expect = "";
|
|
`grep $key $expectedfailfile > /dev/null`;
|
|
if ( $? == 0 ) { $expect = "EXPECTED"; }
|
|
print( "$key\t\t$expect\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------------------------
|
|
|
|
sub main {
|
|
# main subroutine
|
|
my ($ProgName, $scrdir) = &GetNameNDir( );
|
|
my $pwd = `pwd`;
|
|
chomp( $pwd );
|
|
my %opts = &process_cmdline( $ProgName );
|
|
my %csstatus;
|
|
my $files_ref = $opts{'csstatusfiles_ref'};
|
|
foreach my $file ( @$files_ref ) {
|
|
&run_csstatus( "$pwd/$file", $opts{'verbose'}, \%csstatus, $opts{'dieondup'} );
|
|
}
|
|
if ( $opts{'verbose'} ) {
|
|
print "Print summary of testing:\n";
|
|
}
|
|
if ( $opts{'sumintocats'} ) {
|
|
&print_categories( $scrdir, %csstatus );
|
|
} elsif ( $opts{'sumperline'} ) {
|
|
&print_sumperline( %csstatus );
|
|
} else {
|
|
&print_status( %csstatus );
|
|
}
|
|
}
|
|
|
|
# Invoke the main subroutine
|
|
&main();
|
|
|