blob: 09a5cdcb80326f76e73f708ddb8b7652c11e271e [file] [log] [blame]
############################################################################
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
package TestDriver;
###########################################################################
# Class: TestDriver
# A base class for TestDrivers.
#
use TestDriverFactory;
use TestReport;
use File::Path;
use Parallel::ForkManager;
use FileHandle;
use File::Copy;
use File::Basename;
my $passedStr = 'passed';
my $failedStr = 'failed';
my $abortedStr = 'aborted';
my $skippedStr = 'skipped';
my $dependStr = 'failed_dependency';
# A constant to be used as a key in hash:
my $keyGlobalSetupConditionalDone = 'globalSetupConditionaldone';
################################################################################
# Sub: appendToLength
# static mathod to make a string not shorter than N characters in length:
# appends spaces unlit the desired length achieved.
#
# Paramaters:
# str - the string
# len - the min deired length
#
# Returns: the modified string
#
sub appendToLength($$) {
my ($str, $len) = @_;
while (length($str) < $len) {
$str .= " ";
}
return $str;
}
##############################################################################
# Sub: printResults
# Static function, can be used by test_harness.pl
# Print the results so far, given the testStatuses hash.
#
# Paramaters:
# testStatuses - reference to hash of test status results.
# log - reference to file handle to print results to.
# prefix - A title to prefix to the results
#
# Returns:
# None.
#
sub printResults
{
my ($testStatuses, $log, $prefix, $confFile, $groupName) = @_;
my ($pass, $fail, $abort, $depend, $skipped) = (0, 0, 0, 0, 0);
foreach (keys(%$testStatuses)) {
($testStatuses->{$_} eq $passedStr) && $pass++;
($testStatuses->{$_} eq $failedStr) && $fail++;
($testStatuses->{$_} eq $abortedStr) && $abort++;
($testStatuses->{$_} eq $dependStr) && $depend++;
($testStatuses->{$_} eq $skippedStr) && $skipped++;
}
my $context = "";
if (defined($confFile) && (length(confFile) > 0)) {
$context = " [$confFile-$groupName]";
}
# XXX why comma is added there?
my $msg = appendToLength($prefix . ",", 18)
. " PASSED: " . appendToLength($pass,4)
. " FAILED: " . appendToLength($fail,4)
. " SKIPPED: " . appendToLength($skipped,4)
. " ABORTED: " . appendToLength($abort,4)
. " FAILED DEPENDENCY: " . appendToLength($depend,4)
. $context;
print $log "$msg\n";
print "$msg\n";
}
##############################################################################
# Puts all the k-v pairs from sourceHash to the targetHash.
# Returns: void
# parameters:
# 1: targetHash,
# 2: sourceHash.
sub putAll($$)
{
my ($targetHash, $sourceHash) = @_;
while (my ($key, $value) = each(%$sourceHash)) {
$targetHash->{ $key } = $value;
}
}
##############################################################################
# appends one file to another.
# parameters:
# 1: sourceFileName,
# 2: targetFileName.
# Returns: void
sub appendFile($$) {
my ($source, $target) = @_;
dbg("Appending file [" . Cwd::realpath($source) . "] >> [" . Cwd::realpath($target) . "]\n");
$sourceHandle = FileHandle->new("<$source");
if (! defined $sourceHandle) {
die "Cannot open source file [$source].";
}
$targetHandle = FileHandle->new(">>$target");
if (defined $targetHandle) {
copy($sourceHandle, $targetHandle);
$targetHandle->close();
$sourceHandle->close();
} else {
die "cannot open target file [$target].";
}
}
##############################################################################
# Diagnostic sub to print a hash contents
# Paramaters:
# 1: the hash reference;
# Returns: void
sub dbgDumpHash($;$)
{
if ($ENV{'E2E_DEBUG'} eq 'true') {
my ($myhash, $msg) = @_;
print "Dump of hash $msg:\n";
while (my ($key, $value) = each(%$myhash)) {
print " [$key] = [$value]\n";
}
}
}
##############################################################################
# Diagnostic sub to print a debug output.
# This is useful when debugging the harness perl scripts.
# Paramaters:
# 1*: object(s) to be printed, typically one string;
# Returns: void
sub dbg(@)
{
if ($ENV{'E2E_DEBUG'} eq 'true') {
print @_;
}
}
##############################################################################
# Sub: printGroupResultsXml
# Print the results for the group using junit xml schema using values from the testStatuses hash.
#
# Paramaters:
# $report - the report object to use to generate the report
# $groupName - the name of the group to report totals for
# $testStatuses - the hash containing the results for the tests run so far
# $totalDuration- The total time it took to run the group of tests
#
# Returns:
# None.
#
sub printGroupResultsXml
{
my ( $report, $groupName, $testStatuses, $totalDuration) = @_;
$totalDuration=0 if ( !$totalDuration );
my ($pass, $fail, $abort, $depend) = (0, 0, 0, 0);
foreach my $key (keys(%$testStatuses)) {
if ( $key =~ /^$groupName/ ){
($testStatuses->{$key} eq $passedStr) && $pass++;
($testStatuses->{$key} eq $failedStr) && $fail++;
($testStatuses->{$key} eq $abortedStr) && $abort++;
($testStatuses->{$key} eq $dependStr) && $depend++;
}
}
my $total= $pass + $fail + $abort;
$report->totals( $groupName, $total, $fail, $abort, $totalDuration );
}
##############################################################################
# Sub: new
# Constructor, should only be called by TestDriverFactory.
#
# Paramaters:
# None
#
# Returns:
# None.
sub new
{
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
bless($self, $class);
$self->{'wrong_execution_mode'} = "_xyz_wrong_execution_mode_zyx_";
return $self;
}
##############################################################################
# Sub: globalSetup
# Set up before any tests from the group are run. This gives each individual driver a chance to do
# setup. This function will only be called once, before all the tests are
# run. A driver need not implement it. It is a virtual function.
#
# This method invoked unconditionally (always), even if there are no test to run in the group. See
# also #globalSetupConditional() description.
#
# Paramaters:
# globalHash - Top level hash from config file (does not have any group
# or test information in it).
# log - log file handle
#
# Returns:
# None
#
sub globalSetup
{
}
##############################################################################
# Sub: globalSetupConditional
# Set up before any tests from the test config file (in sequential mode) or test group (in parallel mode) are run.
# Executes after #globalSetup(). Executes *only* if there is at least one test to run. Introduced for performance
# optimization in parallel execution mode.
# It is a virtual function. Subclasses may override it.
#
# Paramaters:
# globalHash - Top level hash from config file (does not have any group
# or test information in it).
# log - log file handle
#
# Returns:
# None
#
sub globalSetupConditional
{
}
###############################################################################
# Sub: globalCleanup
# Clean up after all tests have run. This gives each individual driver a chance to do
# cleanup. This function will only be called once, after all the tests are
# run. A driver need not implement it. It is a virtual function.
# This method invoked unconditionally, even if no test in the group was executed.
# See #globalCleanupConditional() method description.
#
# Paramaters:
# globalHash - Top level hash from config file (does not have any group
# or test information in it).
# log - log file handle
#
# Returns:
# None
sub globalCleanup
{
}
###############################################################################
# Sub: globalCleanupConditional
# Clean up after all tests have run, before #globalCleanup(). Invoked iff #globalSetupConditional()
# was previously invoked gor this config file (sequential mode) or test group (parallel mode).
# It is a virtual function. Subclasses may override it.
#
# Paramaters:
# globalHash - Top level hash from config file (does not have any group
# or test information in it).
# log - log file handle
#
# Returns:
# None
sub globalCleanupConditional() {
}
###############################################################################
# Sub: runTest
# Run a test. This is a pure virtual function.
#
# Parameters:
# testcmd - reference to hash with meta tags and command to run tests.
# Interpretation of the tags and the command are up to the subclass.
# log - reference to a stream pointer for the logs
#
# Returns:
# @returns reference to hash. Contents of hash are defined by the subclass.
#
sub runTest
{
die "$0 INFO : This is a virtual function!";
}
###############################################################################
# Sub: generateBenchmark
# Generate benchmark results. This is a pure virtual function.
#
# Parameters:
# benchmarkcmd - reference to hash with meta tags and command to
# generate benchmark. Interpretation of the tags and the command are up to
# log - reference to a stream pointer for the logs
# the subclass.
#
# Returns:
# @returns reference to hash. Contents of hash are defined by the subclass.
#
sub generateBenchmark
{
die "$0 INFO: This is a virtual function!";
}
###############################################################################
# Sub: compare
# Compare the results of the test run with the generated benchmark results.
# This is a pure virtual function.
#
# Parameters:
# benchmarkcmd - reference to hash with meta tags and command to
# testResult - reference to hash returned by runTest.
# benchmarkResult - reference to hash returned by generateBenchmark.
# log - reference to a stream pointer for the logs
# testHash - reference to hash with meta tags and commands
#
# Returns:
# @returns reference true if results are the same, false otherwise. "the
# same" is defined by the subclass.
#
sub compare
{
die "$0 INFO: This is a virtual function!";
}
###############################################################################
# Sub: recordResults
# Record results of the test run. The required fields are filled in by the
# test harness. This call gives an individual driver a chance to fill in
# additional fields of cmd, cmd_id, expected_results, and actual_results.
# this function does not have to be implemened.
# This is a virtual function.
#
# Parameters:
# status - status of test passing, true or false
# testResult - reference to hash returned by runTest.
# benchmarkResult - reference to hash returned by generateBenchmark.
# dbinfo - reference to hash that will be used to fill in db.
# log - reference to hash that will be used to fill in db.
#
# Returns:
# None
#
sub recordResults
{
}
###############################################################################
# Sub: cleanup
# Clean up after a test. This gives each individual driver a chance to do
# cleanup. A driver need not implement it. It is a virtual function.
#
# Parameters:
# status - status of test passing, true or false
# testHash - reference to hash that was passed to runTest() and
# generateBenchmark().
# testResult - reference to hash returned by runTest.
# benchmarkResult - reference to hash returned by generateBenchmark.
# log - reference to a stream pointer for the logs
#
# Returns:
# None
#
sub cleanup
{
}
###############################################################################
# Sub: run
# Run all the tests in the configuration file.
#
# Parameters:
# testsToRun - reference to array of test groups and ids to run
# testsToMatch - reference to array of test groups and ids to match.
# If a test group_num matches any of these regular expressions it will be run.
# cfg - reference to contents of cfg file
# log - reference to a stream pointer for the logs
# dbh - reference database connection
# testStatuses- reference to hash of test statuses
# confFile - config file name
# startat - test to start at.
# logname - name of the xml log for reporting results
#
# Returns:
# @returns nothing
# failed.
#
sub run
{
my ($self, $testsToRun, $testsToMatch, $cfg, $log, $dbh, $testStatuses,
$confFile, $startat, $logname, $resources ) = @_;
my $subName = (caller(0))[3];
my $msg="";
# Rather than make each driver handle our multi-level cfg, we'll flatten
# the hashes into one for it.
my %globalHash;
my $runAll = ((scalar(@$testsToRun) == 0) && (scalar(@$testsToMatch) == 0));
# Read the global keys
foreach (keys(%$cfg)) {
next if $_ eq 'groups';
$globalHash{$_} = $cfg->{$_};
}
my $report=0;
my $properties= new Properties(0, $globalHash{'propertiesFile'});
my $fileForkFactor = int($ENV{'FORK_FACTOR_FILE'});
my $groupForkFactor = int($ENV{'FORK_FACTOR_GROUP'});
# NB: this is to distinguish the sequential mode from parallel one:
my $productForkFactor = $fileForkFactor * $groupForkFactor;
my $pm;
if ($groupForkFactor > 1) {
print $log "Group fork factor: $groupForkFactor\n";
# Create the fork manager:
$pm = new Parallel::ForkManager($groupForkFactor);
# this is a callback method that will run in the main process on each job subprocess completion:
$pm -> run_on_finish (
sub {
my ($pid, $exit_code, $identification, $exit_signal, $core_dump, $data_structure_reference) = @_;
# see what the child sent us, if anything
if (defined($data_structure_reference)) {
dbg("Group subprocess [$identification] finished, pid=${pid}, sent back: [$data_structure_reference].\n");
dbgDumpHash($data_structure_reference, "The hash passed in in the run_on_finish callback:");
putAll($testStatuses, $data_structure_reference);
dbgDumpHash($testStatuses, "The statuses after merge in the run_on_finish callback:");
} else {
print "ERROR: Group subprocess [$identification] did not send back anything. Exit code = $exit_code\n";
}
my $subLogAgain = "$logname-$identification";
appendFile($subLogAgain,$logname);
}
);
} else {
# Do the global setup:
$self->globalSetup(\%globalHash, $log);
}
my $localStartAt = $startat;
foreach my $group (@{$cfg->{'groups'}}) {
my $groupName = $group->{'name'};
my $subLog;
my $subLogName;
if ($groupForkFactor > 1) {
# use group name as the Job id:
my $jobId = $groupName;
$subLogName = "$logname-$jobId";
open $subLog, ">$subLogName" or die "FATAL ERROR $0 at ".__LINE__." : Can't open $subLogName, $!\n";
dbg("**** Logging to [$subLogName].\n");
# PARALLEL SECTION START: ===============================================================================
$pm->start($jobId) and next;
dbg("Started test group job \"$jobId\"\n");
dbg("Doing setup for test group [$groupName]...\n");
# Set the group-specific ID:
# NB: note that '$globalHash' here is an object cloned for this subprocess.
# So, there is no concurrency issue in using '$globalHash' there:
$globalHash{'job-id'} = $globalHash{'job-id'} . "-" . $jobId;
# Do the global setup which is specific for *this group*:
$self->globalSetup(\%globalHash, $subLog);
} else {
$subLog = $log;
$subLogName = $logname;
}
# Run the group of tests.
# NB: the processing of $localStartAt parameter happens only if the groupForkFactor < 1.
my $sawStart = $self -> runTestGroup($groupName, $subLog, $confFile, \%globalHash, $group, $runAll, $testsToRun, $testsToMatch, $localStartAt, $testStatuses, $productForkFactor, $resources);
if ((defined $localStartAt) && $sawStart) {
undef $localStartAt;
}
if ($groupForkFactor > 1) {
# do the clanups that are specific for *this group*.
dbg("Doing cleanup for test group [$groupName]...\n");
# NB: invoke it in such way to emphasize the fact that this method is not virtual:
globalCleanupConditionalIf($self, \%globalHash, $subLog);
$self->globalCleanup(\%globalHash, $subLog);
dbg("Finishing test group [$groupName].\n");
dbgDumpHash($testStatuses, "The satatuses hash at the fork section end");
# NB: send the "testStatuses" hash object reference (which is local to this subprocess) to the parent process:
$subLog -> close();
# TODO: may also consider the #runTestGroup block exit status and use it there.
$pm -> finish(0, $testStatuses);
# PARALLEL SECTION END. ===============================================================================
}
} # foreach $group
if ($groupForkFactor > 1) {
$pm->wait_all_children;
} else {
# Do the global cleanups:
# NB: invoke it in such way to emphasize the fact that this method is not virtual:
globalCleanupConditionalIf($self, \%globalHash, $log);
$self->globalCleanup(\%globalHash, $log);
}
}
# Servce method to conditionally perform the virtual #globalCleanupConditional().
# NB: This sub should be "final" in Java terms,
# subclasses should not override it.
sub globalCleanupConditionalIf() {
my ($self, $globalHash, $log) = @_;
if (defined($globalHash->{$keyGlobalSetupConditionalDone})) {
$self -> globalCleanupConditional($globalHash, $log);
}
}
################################################################################
# Separated sub to run a test group.
# Parameters: (same named values from #run(...) sub with the same meaning).
# Returns: 'true' if the test defined by '$startat' was found, and 'false' otherwise.
# (If the '$startat' is null, always returns true.)
sub runTestGroup() {
my ($self, $groupName, $subLog, $confFile, $globalHash, $group, $runAll, $testsToRun, $testsToMatch, $startat, $testStatuses, $productForkFactor, $resources) = @_;
my $subName = (caller(0))[3];
print $subLog "INFO $subName at ".__LINE__.": Running TEST GROUP(".$groupName.")\n";
my $sawstart = !(defined $startat);
my %groupHash = %$globalHash;
$groupHash{'group'} = $groupName;
# Read the group keys
foreach (keys(%$group)) {
next if $_ eq 'tests';
$groupHash{$_} = $group->{$_};
}
my $groupDuration=0;
my $duration=0;
# Run each test in the group:
foreach my $test (@{$group->{'tests'}}) {
# Check if we're supposed to run this one or not.
if (!$runAll) {
# check if we are supposed to run this test or not.
my $foundIt = 0;
foreach (@$testsToRun) {
if (/^$groupHash{'group'}(_[0-9]+)?$/) {
if (not defined $1) {
# In this case it's just the group name, so we'll
# run every test in the group
$foundIt = 1;
last;
} else {
# maybe, it at least matches the group
my $num = "_" . $test->{'num'};
if ($num eq $1) {
$foundIt = 1;
last;
}
}
}
}
foreach (@$testsToMatch) {
my $protoName = $groupHash{'group'} . "_" . $test->{'num'};
if ($protoName =~ /$_/) {
if (not defined $1) {
# In this case it's just the group name, so we'll
# run every test in the group
$foundIt = 1;
last;
} else {
# maybe, it at least matches the group
my $num = "_" . $test->{'num'};
if ($num eq $1) {
$foundIt = 1;
last;
}
}
}
}
next unless $foundIt;
}
# This is a test, so run it.
my %testHash = %groupHash;
my $tmpTestHash = \%testHash;
foreach (keys(%$test)) {
$testHash{$_} = $test->{$_};
}
my $testName = $testHash{'group'} . "_" . $testHash{'num'};
dbg("################### Executing test [$testName]...\n");
# if ( $groupExecuted{ $group->{'name'} }== 0 ){
# $groupExecuted{ $group->{'name'} }=1;
#
# my $xmlDir= $globalHash{'localxmlpathbase'}."/run".$globalHash->{'UID'};
# mkpath( [ $xmlDir ] , 1, 0777) if ( ! -e $xmlDir );
#
# my $filename = $group->{'name'}.".xml";
# $report = new TestReport ( $properties, "$xmlDir/$filename" );
# $report->purge();
# }
# Check that ignore isn't set for this file, group, or test
if (defined $testHash{'ignore'}) {
print $subLog "Ignoring test $testName, ignore message: " .
$testHash{'ignore'} . "\n";
next;
}
# Have we not reached the starting point yet?
if (!$sawstart) {
if ($testName eq $startat) {
$sawstart = 1;
} else {
next;
}
}
# Check that this test doesn't depend on an earlier test or tests
# that failed. Don't abort if that test wasn't run, just assume the
# user knew what they were doing and set it up right.
my $skipThisOne = 0;
foreach (keys(%testHash)) {
if (/^depends_on/ && defined($testStatuses->{$testHash{$_}}) &&
$testStatuses->{$testHash{$_}} ne $passedStr) {
print $subLog "Skipping test $testName, it depended on " .
"$testHash{$_} which returned a status of " .
"$testStatuses->{$testHash{$_}}\n";
$testStatuses->{$testName} = $dependStr;
$skipThisOne = 1;
last;
}
}
if ($skipThisOne) {
if ($productForkFactor > 1) {
printResults($testStatuses, $subLog, "Results so far", basename($confFile), $groupName);
} else {
printResults($testStatuses, $subLog, "Results so far");
}
next;
}
print $subLog "\n******************************************************\n";
print $subLog "\nTEST: $confFile::$testName\n";
print $subLog "******************************************************\n";
print $subLog "Beginning test $testName at " . time . "\n";
# At this point we're going to run the test for sure.
# So, do the preparation for that, if not yet done:
if (!defined($globalHash->{$keyGlobalSetupConditionalDone})) {
$self -> globalSetupConditional($globalHash, $subLog);
# this preparation should be done only *once* per each $globalHash instance,
# so, set special flag to prevent #globalSetupConditional from being executed again:
$globalHash->{$keyGlobalSetupConditionalDone} = 'true';
}
my %dbinfo = (
'testrun_id' => $testHash{'trid'},
'test_type' => $testHash{'driver'},
#'test_file' => $testHash{'file'},
'test_file' => $confFile,
'test_group' => $testHash{'group'},
'test_num' => $testHash{'num'},
);
my $beginTime = time;
my $endTime = 0;
my ($testResult, $benchmarkResult);
eval {
$testResult = $self->runTest(\%testHash, $subLog, $resources);
$endTime = time;
$benchmarkResult = $self->generateBenchmark(\%testHash, $subLog);
my $result =
$self->compare($testResult, $benchmarkResult, $subLog, \%testHash, $resources);
$msg = "INFO: $subName() at ".__LINE__.":Test $testName";
if ($result eq $self->{'wrong_execution_mode'}) {
$msg .= " SKIPPED";
$testStatuses->{$testName} = $skippedStr;
} elsif ($result) {
$msg .= " SUCCEEDED";
$testStatuses->{$testName} = $passedStr;
} else {
$msg .= " FAILED";
$testStatuses->{$testName} = $failedStr;
}
$msg .= " at " . time . "\nEnding test $testName at " . time ."\n";
print $subLog $msg;
$duration = $endTime - $beginTime;
$dbinfo{'duration'} = $duration;
$self->recordResults($result, $testResult
, $benchmarkResult, \%dbinfo, $subLog);
};
if ($@) {
$msg= "ERROR $subName at : ".__LINE__." Failed to run test $testName <$@>\n";
$msg .= "\nEnding test $testName at " . time ."\n";
#print $msg;
print $subLog $msg;
$testStatuses->{$testName} = $abortedStr;
$dbinfo{'duration'} = $duration;
}
eval {
$dbinfo{'status'} = $testStatuses->{$testName};
if($dbh) {
$dbh->insertTestCase(\%dbinfo);
}
};
if ($@) {
chomp $@;
warn "Failed to insert test case info, error <$@>\n";
}
$self->cleanup($testStatuses->{$testName}, \%testHash, $testResult,
$benchmarkResult, $subLog);
#$report->testcase( $group->{'name'}, $testName, $duration, $msg, $testStatuses->{$testName}, $testResult ) if ( $report );
$report->testcase( $group->{'name'}, $testName, $duration, $msg, $testStatuses->{$testName} ) if ( $report );
$groupDuration = $groupDuration + $duration;
if ($productForkFactor > 1) {
printResults( $testStatuses, $subLog, "Results so far", basename($confFile), $groupName );
} else {
printResults( $testStatuses, $subLog, "Results so far" );
}
} # for each test
if ( $report ) {
$report->systemOut( $subLogName, $group->{'name'});
printGroupResultsXml( $report, $group->{'name'}, $testStatuses, $groupDuration );
}
$report = 0;
return $sawstart;
}
# TODO These should be removed
sub tmpIPCRun(){
my $self = shift;
my $subName = (caller(0))[3];
my $runningSubName= shift;
my $refCmd = shift;
my @cmd = @$refCmd;
my $log = shift;
my $msg = shift;
print $log "$0::$subName INFO Running ( @cmd )\n";
my $result= `@cmd`;
if ( $@ ) {
my $msg= "$0::$subName FATAL Failed to run from $runningSubName $msg < $@ >\n$result\n";
print $log $msg;
die "$msg";
}
return $?;
}
sub tmpIPCRunSplitStdoe {
my $self = shift;
my $subName = (caller(0))[3];
my $runningSubName= shift;
my $refCmd = shift;
my @cmd = @$refCmd;
my $dir = shift;
my $log = shift;
my $msg = shift;
my $die = shift;
my $failed = 0;
my $outfilename = $dir."out.tmp";
my $errfilename = $dir."err.tmp";
print $log "$0::$subName INFO Running from $runningSubName ( @cmd 1>$outfilename 2>$errfilename )\n";
#make sure files are writeable
open( TMP, ">$outfilename" ) || die "$0::$subName FATAL: Cannot open $outfilename for writing\n";
close( TMP );
open( TMP, ">$errfilename" ) || die "$0::$subName FATAL: Cannot open $errfilename for writing\n";
close( TMP );
#RUN CMD
my $msg;
print $log `@cmd 1>$outfilename 2>$errfilename`;
my $failed=0;
if ( $@ ) {
$msg= "$0::$subName FATAL < $@ >\n";
$failed++;
}
#READ FILES
my $stdout="";
my $stderr="";;
open( TMP, "$outfilename" ) || die "$0::$subName FATAL: Cannot open $outfilename for reading\n";
while ( <TMP> ){ $stdout .= $_; }
close( TMP );
open( TMP, "$errfilename" ) || die "$0::$subName FATAL: Cannot open $errfilename for reading\n";
while ( <TMP> ){
$stderr .= $_;
}
close( TMP );
#DIE IF Test Failed, otherwise return stdout and stderr
if ( $failed ){
$msg = "$0::$subName FATAL: Faied from $runningSubName \nSTDOUT:".$result{'stdout'}."\nSTDERR:".$result{'stderr'}."\n" if ( $failed );
print $log "$msg";
die $msg if ( $die != "1" ); #die by default
return ( -1, $stdout, $stderr );
}
return ( $?, $stdout, $stderr);
}
sub tmpIPCRunJoinStdoe {
my $self = shift;
my $subName = (caller(0))[3];
my $runningSubName= shift;
my $refCmd = shift;
my @cmd = @$refCmd;
my $outfilename= shift;
my $log = shift;
my $msg = shift;
my $die = shift;
#make sure files are writeable
open( TMP, ">$outfilename" ) || die "$0::$subName FATAL: Cannot open $outfilename for writing\n";
close( TMP );
#RUN CMD
my $msg;
my $failed=0;
print $log "$0::$subName INFO Running ( @cmd 2>&1$outfilename 2>/dev/null )\n";
print $log `@cmd 2>&1 > $outfilename 2>/dev/null`;
if ( $@ ) {
$failed++;
$msg= "$0::$subName FATAL < $@ >\n";
}
#READ FILES
my $stdoe="";
open( TMP, "$outfilename" ) || die "$0::$subName FATAL: Cannot open $outfilename for reading\n";
while ( <TMP> ){ $stdoe .= $_; }
close( TMP );
if ( $failed ){
print $log "$msg";
die $msg if ( $die != "1" ); #die by default
return ( -1 );
}
return ( $? );
}
1;