Rev 5412 | Blame | Compare with Previous | Last modification | View Log | RSS feed
######################################################################### Copyright (c) VIX TECHNOLOGY (AUST) LTD## Module name : UtfFilter_androidStudio.pm# Module type : JATS Utility# Environment(s): jats## Description : JATS UTF Filter - internal# Jats UTF plugin module to format the Android Studio JUNIT output# into a standard XML for JATS to import into the# Release Manager database.## Usage : See TECHGP-00316 Post Processing Unit Tests Results##......................................................................#package UtfFilter_androidstudio;require 5.008_002;use diagnostics;use strict;use warnings;use Getopt::Long;use Time::HiRes;use File::Find;use File::Spec;use Cwd;use JatsError;use JatsSystem;## Globals#my $dirName;my $className;my $options;my %Type2Name = ( 'P' => '/build/test-results/release','D' => '/build/test-results/debug' );#------------------------------------------------------------------------------# Function : processUtf## Description : perform the actions required by this module as described above# Static method within the package## Inputs : $name - Name of the class (Static method call)# $options - Ref to HASH of Data Items## Output : The result XML file.## Returns : 0 - some tests failed# 1 - all tests passed#sub processUtf {($className, $options) = @_;$dirName = $Type2Name{$options->{TYPE}} ;Error ("Cannot convert build type to test path: $options->{TYPE}") unless defined $dirName;my ($passed, @instance) = createBuildInstance($options->{TARGET});# Use common routine to create XML filejats_runutf::writeXmlResults($options, \@instance );return $passed;}#------------------------------------------------------------------------------# Function : createBuildInstance## Description : Reads an JUNIT-like test results file and returns an array with an# entry per test. Each entry is a Hash as described below.## Inputs : $target - the build target platform. Cannot be undef## Output : None## Returns : A pair of values ($passed, $test_results)# $passed - 1 if there were no failed tests# 0 if there were failed tests# @test_results - A data structure consisting of an array of# hashes# Each entry in the hash holds:# NAME - the class and method name of the test (or as# much information as is necessary to uniquely# identify the test)# OUTCOME - one of 'UNKNOWN', 'PASS', 'FAIL', 'ERROR'.# Being the test result.# DURATION - the time (in seconds) that the test took to run# MESSAGE - if the test did not pass, there is more# information here. Most likely a stack dump.#sub createBuildInstance {my ($target) = @_;Error("Must provide a target") unless defined($target);my @filename = findResultsFile();my ($passed, @test_results) = parseTestRun($target, @filename);return ($passed, @test_results);}#------------------------------------------------------------------------------# Function : findResultsFile## Description : Find all files matching the pattern '^Test-.*\.xml' below# the current folder.## Inputs : none## Output : none## Returns : The complete path and filenames of the matching file.#sub findResultsFile {my @testResultsFile;find(sub {if (/^test-.*\.xml/i) {## Need the file to be in the right directory too#if ($File::Find::dir =~ m/$dirName$/){push @testResultsFile, File::Spec->rel2abs($_);}}}, '.');Error("Could not find any Unit Test Results files.","Check that it has a filename of 'TEST-*.xml'","Scan starts at: " . cwd())unless @testResultsFile;Verbose("Processing AndroidStudio Results files: ".join(', ', @testResultsFile)."\n");return sort @testResultsFile;}#------------------------------------------------------------------------------# Function : parseTestRun## Description :## Inputs : $target - the build target platform to assign to each test# @filenames - the list of junit results.xml files to parse## Output : none## Returns : a pair of values ($passed, @test_results)# $passed - 1 if all tests passed# 0 if some tests failed# @test_results - A list of TestResult's (see above)#sub parseTestRun {my ($target, @filenames) = @_;my ($passed, @test_results);my ($project_name, $package_name, $package_version, $timestamp);$passed = 1;while (my $filename = shift @filenames) {open( my $infile, "<$filename" ) || Error ( "Cannot read from $filename", $! );# Read the file, line by linewhile ( <$infile> ) {# Extract one test case## This may progress the file pointer if <testcase>...</testcase># is multilinemy @test_case = getTestCase($_, $infile) if /\<testcase/;# Parse the test case creating a hashmy %test_run = parseTestCase( @test_case) if (@test_case);# Save the test result in the arraypush(@test_results, {%test_run}) if (%test_run);# Record that there was at least one failed test$passed = 0 if (%test_run && ($test_run{OUTCOME} ne 'PASS'));}close ($infile); # Just so we don't hog file handles}return ($passed, @test_results);}#------------------------------------------------------------------------------# Function : containsClosingTag## Description : Handles the determination of checking for the closing# '</testcase>' or '/>' tag.## Inputs : $line - the current line in the results.xml file.## Output : none## Returns : 1 - closing tag found# 0 - o closing tag found#sub containsClosingTag {my ($line) = @_;return 1 if ($line =~ /\<\/testcase\>/);return 1 if ($line =~ /\/\>$/);return 0;}#------------------------------------------------------------------------------# Function : getTestCase## Description : Reads from the file, and advances the file pointer, until the# closing '</testcase>' or '/>' tag is read.## Inputs : $line - the current line in the results.xml file. This line# will contain '<testcase'.# $file - the file handle of the results.xml file.## Output : none## Returns : A string array of all lines read, including the start and end# 'testcase' tag.#sub getTestCase {my ($line, $file) = @_;my (@result);# Save the first line, containing the opening <testcase> tagpush(@result, $line);# No more to do if it's all on one linereturn @result if containsClosingTag($line);# Save subsequent lines up to and including the closing </testcase> tagwhile (<$file>){push (@result, $_);last if /\<\/testcase\>/;# don't check for '/>' here as we're multi-line.}return @result;}#------------------------------------------------------------------------------# Function : getDetails## Description :## Inputs : $line - a line of XML containing all the attributes of the# <testcase> tag.## Output : none## Returns : A tuple of values ($name, $duration, $outcome)# $name - The test name, concatenated from the 'classname' and# 'name' attributes.# $duration - The test duration, in seconds, from the 'time'# attribute.# $outcome - The test outcome (= 'PASS') if we know it (i.e. the# closing '</testcase>' or '/>' tag is on the same# line).# Otherwise, if we don't know it, return undef.#sub getDetails {my ($line) = @_;# Pattern to extract a thing between two quotes (' or ").my ($xml_value) = qr/["\']([^"\']*)["\']/;my ($name, $duration, $outcome);if ($line =~ /\sname=${xml_value}\s*classname=${xml_value}\s*time=${xml_value}/) {$name = $2. '::'. $1;# Convert float into milliseconds$duration = $3;$duration *= 1000;$duration = int($duration + 0.5);$outcome = 'PASS' if containsClosingTag($line);}return ($name, $duration, $outcome);}#------------------------------------------------------------------------------# Function : parseMessage## Description : parse the given element tag, and return its contents.## Inputs : $pattern - The XML element name from which to extract the# message.# $line - The line with the open tag. E.g.# <error ...># @lines - all lines until, and including, the closing tag. E.g.# ...# ...# </error>## Output : none## Returns : The value of the matched element.#sub parseMessage {my ($pattern, $line, @lines) = @_;my ($message);if ($line =~ /\<${pattern} /) {my $temp_message = $line;# consume until </$pattern>while ($line = shift @lines) {$temp_message .= $line;last if $line =~ /\<\/${pattern}\>/;}# Extract between '<pattern ...>' and '</pattern>'$temp_message =~ m/\<${pattern}[^>]*>([^\<]*)\<\/${pattern}>/;$message = $1;}return ($message);}#------------------------------------------------------------------------------# Function : parseTestCase## Description : Takes a <testCase> element and parses it into a hash.## Inputs : @lines - The lines from the file from the opening, to the# closing <testCase> tag (inclusive).## Output : none## Returns : A hash with the following keys:# NAME - the test method name.# DURATION - the test duration, in seconds.# OUTCOME - one of 'PASS', 'FAILED', 'ERROR'#sub parseTestCase {my %testRun;while (my $line = shift @_) {my ($name, $duration, $outcome, $message);($name, $duration, $outcome) = getDetails($line);if (defined($name) && defined($duration)) {$testRun{NAME} = $name;$testRun{DURATION} = $duration;$testRun{OUTCOME} = $outcome if (defined($outcome));next;}last if containsClosingTag($line);($message) = parseMessage(qr/error/ , $line, @_);if (defined($message)) {$testRun{OUTCOME} = 'ERROR';$testRun{MESSAGE} = $message;next;}($message) = parseMessage(qr/failure/, $line, @_);if (defined($message)) {$testRun{OUTCOME} = 'FAILURE';$testRun{MESSAGE} = $message;next;}}return %testRun;}1;