mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
de816a0f57
Integration expect log testing was originally used as a rough-and-ready way to make sure that certain code paths were being executed before the unit tests existed. Now that we have 100% unit test coverage (with expect log testing) the value of the integration expect tests seems minimal at best.
But they do cause numerous issues:
- Maintenance of the expect code and replacements that are required to keep logs reproducible.
- Even a trivial change can cause massive churn in the expect logs, e.g. d9088b2
. These changes should be minutely audited but since the expect logs have little value now it is seldom worth the effort.
- The OS version used to do expect testing (RHEL7) can only be used to test one version of PostgreSQL. This makes it hard to balance the PostgreSQL version testing between OS versions.
- When a commit affects expect logs it is not clear (especially for new developers) how to regenerate them and our contributing guide is silent on the issue.
The goal is to migrate the integration tests to C and expect testing is not part of that plan. It seems best to get rid of them now.
427 lines
14 KiB
Perl
427 lines
14 KiB
Perl
####################################################################################################################################
|
|
# RunTest.pm - All tests are inherited from this object
|
|
####################################################################################################################################
|
|
package pgBackRestTest::Common::RunTest;
|
|
|
|
####################################################################################################################################
|
|
# Perl includes
|
|
####################################################################################################################################
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
use English '-no_match_vars';
|
|
|
|
use Exporter qw(import);
|
|
our @EXPORT = qw();
|
|
use File::Basename qw(dirname);
|
|
|
|
use pgBackRestDoc::Common::Exception;
|
|
use pgBackRestDoc::Common::Log;
|
|
use pgBackRestDoc::Common::String;
|
|
use pgBackRestDoc::ProjectInfo;
|
|
|
|
use pgBackRestTest::Common::BuildTest;
|
|
use pgBackRestTest::Common::DefineTest;
|
|
use pgBackRestTest::Common::ExecuteTest;
|
|
use pgBackRestTest::Common::Storage;
|
|
use pgBackRestTest::Common::StoragePosix;
|
|
use pgBackRestTest::Common::VmTest;
|
|
use pgBackRestTest::Common::Wait;
|
|
|
|
####################################################################################################################################
|
|
# Constant to use when bogus data is required
|
|
####################################################################################################################################
|
|
use constant BOGUS => 'bogus';
|
|
push @EXPORT, qw(BOGUS);
|
|
|
|
####################################################################################################################################
|
|
# The current test run that is executing. Only a single run should ever occur in a process to prevent various cleanup issues from
|
|
# affecting the next run. Of course multiple subtests can be executed in a single run.
|
|
####################################################################################################################################
|
|
my $oTestRun;
|
|
my $oStorage;
|
|
|
|
####################################################################################################################################
|
|
# new
|
|
####################################################################################################################################
|
|
sub new
|
|
{
|
|
my $class = shift; # Class name
|
|
|
|
# Create the class hash
|
|
my $self = {};
|
|
bless $self, $class;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');
|
|
|
|
# Initialize run counter
|
|
$self->{iRun} = 0;
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'self', value => $self, trace => true}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# initModule
|
|
#
|
|
# Empty init sub in case the ancestor class does not declare one.
|
|
####################################################################################################################################
|
|
sub initModule {}
|
|
|
|
####################################################################################################################################
|
|
# initTest
|
|
#
|
|
# Empty init sub in case the ancestor class does not declare one.
|
|
####################################################################################################################################
|
|
sub initTest {}
|
|
|
|
####################################################################################################################################
|
|
# cleanTest
|
|
#
|
|
# Delete all files in test directory.
|
|
####################################################################################################################################
|
|
sub cleanTest
|
|
{
|
|
my $self = shift;
|
|
|
|
executeTest('rm -rf ' . $self->testPath() . '/*');
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# cleanModule
|
|
#
|
|
# Empty final sub in case the ancestor class does not declare one.
|
|
####################################################################################################################################
|
|
sub cleanModule {}
|
|
|
|
####################################################################################################################################
|
|
# process
|
|
####################################################################################################################################
|
|
sub process
|
|
{
|
|
my $self = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
(
|
|
my $strOperation,
|
|
$self->{strVm},
|
|
$self->{iVmId},
|
|
$self->{strBasePath},
|
|
$self->{strTestPath},
|
|
$self->{strBackRestExe},
|
|
$self->{strBackRestExeHelper},
|
|
$self->{strPgBinPath},
|
|
$self->{strPgVersion},
|
|
$self->{strModule},
|
|
$self->{strModuleTest},
|
|
$self->{iyModuleTestRun},
|
|
$self->{bOutput},
|
|
$self->{bDryRun},
|
|
$self->{bCleanup},
|
|
$self->{strLogLevelTestFile},
|
|
$self->{strPgUser},
|
|
$self->{strGroup},
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '->process', \@_,
|
|
{name => 'strVm'},
|
|
{name => 'iVmId'},
|
|
{name => 'strBasePath'},
|
|
{name => 'strTestPath'},
|
|
{name => 'strBackRestExe'},
|
|
{name => 'strBackRestExeHelper'},
|
|
{name => 'strPgBinPath', required => false},
|
|
{name => 'strPgVersion', required => false},
|
|
{name => 'strModule'},
|
|
{name => 'strModuleTest'},
|
|
{name => 'iModuleTestRun', required => false},
|
|
{name => 'bOutput'},
|
|
{name => 'bDryRun'},
|
|
{name => 'bCleanup'},
|
|
{name => 'strLogLevelTestFile'},
|
|
{name => 'strPgUser'},
|
|
{name => 'strGroup'},
|
|
);
|
|
|
|
# Init will only be run on first test, clean/init on subsequent tests
|
|
$self->{bFirstTest} = true;
|
|
|
|
# Initialize test storage
|
|
$oStorage = new pgBackRestTest::Common::Storage(
|
|
$self->testPath(), new pgBackRestTest::Common::StoragePosix({bFileSync => false, bPathSync => false}));
|
|
|
|
# Init, run, and clean the test(s)
|
|
$self->initModule();
|
|
$self->run();
|
|
$self->cleanModule();
|
|
|
|
# Make sure the correct number of tests ran
|
|
my $hModuleTest = testDefModuleTest($self->{strModule}, $self->{strModuleTest});
|
|
|
|
if ($hModuleTest->{&TESTDEF_TOTAL} != $self->runCurrent())
|
|
{
|
|
confess &log(ASSERT, "expected $hModuleTest->{&TESTDEF_TOTAL} tests to run but $self->{iRun} ran");
|
|
}
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'self', value => $self, trace => true}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# begin
|
|
####################################################################################################################################
|
|
sub begin
|
|
{
|
|
my $self = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$strDescription,
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '->begin', \@_,
|
|
{name => 'strDescription'},
|
|
);
|
|
|
|
# Increment the run counter;
|
|
$self->{iRun}++;
|
|
|
|
# Return if this test should not be run
|
|
if (@{$self->{iyModuleTestRun}} != 0 && !grep(/^$self->{iRun}$/i, @{$self->{iyModuleTestRun}}))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
# Output information about test to run
|
|
&log(INFO, 'run ' . sprintf('%03d', $self->runCurrent()) . ' - ' . $strDescription);
|
|
|
|
if ($self->isDryRun())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!$self->{bFirstTest})
|
|
{
|
|
$self->cleanTest();
|
|
}
|
|
|
|
$self->initTest();
|
|
$self->{bFirstTest} = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# testResult
|
|
####################################################################################################################################
|
|
sub testResult
|
|
{
|
|
my $self = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$fnSub,
|
|
$strExpected,
|
|
$strDescription,
|
|
$iWaitSeconds,
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '::testResult', \@_,
|
|
{name => 'fnSub', trace => true},
|
|
{name => 'strExpected', required => false, trace => true},
|
|
{name => 'strDescription', trace => true},
|
|
{name => 'iWaitSeconds', optional => true, default => 0, trace => true},
|
|
);
|
|
|
|
&log(INFO, ' ' . $strDescription);
|
|
my $strActual;
|
|
my $bWarnValid = true;
|
|
|
|
my $oWait = waitInit($iWaitSeconds);
|
|
my $bDone = false;
|
|
|
|
# Clear the cache for this test
|
|
logFileCacheClear();
|
|
|
|
my @stryResult;
|
|
|
|
do
|
|
{
|
|
eval
|
|
{
|
|
@stryResult = ref($fnSub) eq 'CODE' ? $fnSub->() : $fnSub;
|
|
|
|
if (@stryResult <= 1)
|
|
{
|
|
$strActual = ${logDebugBuild($stryResult[0])};
|
|
}
|
|
else
|
|
{
|
|
$strActual = ${logDebugBuild(\@stryResult)};
|
|
}
|
|
|
|
return true;
|
|
}
|
|
or do
|
|
{
|
|
if (!isException(\$EVAL_ERROR))
|
|
{
|
|
confess "unexpected standard Perl exception" . (defined($EVAL_ERROR) ? ": ${EVAL_ERROR}" : '');
|
|
}
|
|
|
|
confess &logException($EVAL_ERROR);
|
|
};
|
|
|
|
if ($strActual ne (defined($strExpected) ? $strExpected : "[undef]"))
|
|
{
|
|
if (!waitMore($oWait))
|
|
{
|
|
confess
|
|
"expected:\n" . (defined($strExpected) ? "\"${strExpected}\"" : '[undef]') .
|
|
"\nbut actual was:\n" . (defined($strActual) ? "\"${strActual}\"" : '[undef]');
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$bDone = true;
|
|
}
|
|
} while (!$bDone);
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'result', value => \@stryResult, trace => true}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# testRunName
|
|
#
|
|
# Create module/test names by upper-casing the first letter and then inserting capitals after each -.
|
|
####################################################################################################################################
|
|
sub testRunName
|
|
{
|
|
my $strName = shift;
|
|
my $bInitCapFirst = shift;
|
|
|
|
$bInitCapFirst = defined($bInitCapFirst) ? $bInitCapFirst : true;
|
|
my $bFirst = true;
|
|
|
|
my @stryName = split('\-', $strName);
|
|
$strName = undef;
|
|
|
|
foreach my $strPart (@stryName)
|
|
{
|
|
$strName .= ($bFirst && $bInitCapFirst) || !$bFirst ? ucfirst($strPart) : $strPart;
|
|
$bFirst = false;
|
|
}
|
|
|
|
return $strName;
|
|
}
|
|
|
|
push @EXPORT, qw(testRunName);
|
|
|
|
####################################################################################################################################
|
|
# testRun
|
|
####################################################################################################################################
|
|
sub testRun
|
|
{
|
|
# Assign function parameters, defaults, and log debug info
|
|
my
|
|
(
|
|
$strOperation,
|
|
$strModule,
|
|
$strModuleTest,
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '::testRun', \@_,
|
|
{name => 'strModule', trace => true},
|
|
{name => 'strModuleTest', trace => true},
|
|
);
|
|
|
|
# Error if the test run is already defined - only one run per process is allowed
|
|
if (defined($oTestRun))
|
|
{
|
|
confess &log(ASSERT, 'a test run has already been created in this process');
|
|
}
|
|
|
|
my $strModuleName =
|
|
'pgBackRestTest::Module::' . testRunName($strModule) . '::' . testRunName($strModule) . testRunName($strModuleTest) .
|
|
'Test';
|
|
|
|
$oTestRun = eval("require ${strModuleName}; ${strModuleName}->import(); return new ${strModuleName}();")
|
|
or do {confess $EVAL_ERROR};
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'oRun', value => $oTestRun, trace => true}
|
|
);
|
|
}
|
|
|
|
push @EXPORT, qw(testRun);
|
|
|
|
####################################################################################################################################
|
|
# testRunGet
|
|
####################################################################################################################################
|
|
sub testRunGet
|
|
{
|
|
return $oTestRun;
|
|
}
|
|
|
|
push @EXPORT, qw(testRunGet);
|
|
|
|
####################################################################################################################################
|
|
# storageTest - get the storage for the current test
|
|
####################################################################################################################################
|
|
sub storageTest
|
|
{
|
|
return $oStorage;
|
|
}
|
|
|
|
push(@EXPORT, qw(storageTest));
|
|
|
|
####################################################################################################################################
|
|
# Getters
|
|
####################################################################################################################################
|
|
sub archBits {return vmArchBits(shift->{strVm})}
|
|
sub backrestExe {return shift->{strBackRestExe}}
|
|
sub backrestExeHelper {return shift->{strBackRestExeHelper}}
|
|
sub basePath {return shift->{strBasePath}}
|
|
sub dataPath {return shift->basePath() . '/test/data'}
|
|
sub doCleanup {return shift->{bCleanup}}
|
|
sub logLevelTestFile {return shift->{strLogLevelTestFile}}
|
|
sub group {return shift->{strGroup}}
|
|
sub isDryRun {return shift->{bDryRun}}
|
|
sub module {return shift->{strModule}}
|
|
sub moduleTest {return shift->{strModuleTest}}
|
|
sub pgBinPath {return shift->{strPgBinPath}}
|
|
sub pgUser {return shift->{strPgUser}}
|
|
sub pgVersion {return shift->{strPgVersion}}
|
|
sub runCurrent {return shift->{iRun}}
|
|
sub stanza {return 'db'}
|
|
sub testPath {return shift->{strTestPath}}
|
|
sub vm {return shift->{strVm}}
|
|
sub vmId {return shift->{iVmId}}
|
|
|
|
1;
|