1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-09-16 09:06:18 +02:00

Split test modules into separate files.

Makes the code more maintainable. Tests are dynamically loaded by name rather than requiring an if-else block.
This commit is contained in:
David Steele
2016-12-23 08:22:59 -05:00
parent 56144c99c0
commit 6b2666a9d7
45 changed files with 5918 additions and 5822 deletions

View File

@@ -169,6 +169,14 @@
</release-item>
</release-feature-list>
</release-core-list>
<release-test-list>
<release-refactor-list>
<release-item>
<p>Split test modules into separate files to make the code more maintainable. Tests are dynamically loaded by name rather than requiring an if-else block.</p>
</release-item>
</release-refactor-list>
</release-test-list>
</release>
<release date="2016-12-12" version="1.12" title="Page Checksums, Configuration, and Bug Fixes">

View File

@@ -0,0 +1,165 @@
####################################################################################################################################
# BackupArchiveGetTest.pm - Tests for archive-get command
####################################################################################################################################
package pgBackRestTest::Backup::BackupArchiveGetTest;
use parent 'pgBackRestTest::Backup::BackupCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use pgBackRest::ArchiveInfo;
use pgBackRest::BackupInfo;
use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Backup::BackupCommonTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
my $strArchiveChecksum = '72b9da071c13957fb4ca31f05dbd5c644297c2f7';
my $iArchiveMax = 3;
my $strArchiveTestFile = $self->dataPath() . '/backup.wal2_' . WAL_VERSION_94 . '.bin';
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
for (my $bCompress = false; $bCompress <= true; $bCompress++)
{
for (my $bExists = false; $bExists <= true; $bExists++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin("rmt ${bRemote}, cmp ${bCompress}, exists ${bExists}")) {next}
# Create hosts, file object, and config
my ($oHostDbMaster, $oHostDbStandby, $oHostBackup, $oFile) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote, bCompress => $bCompress});
# Create the xlog path
my $strXlogPath = $oHostDbMaster->dbBasePath() . '/pg_xlog';
filePathCreate($strXlogPath, undef, false, true);
# Create the test path for pg_control and copy pg_control for stanza-create
filePathCreate(($oHostDbMaster->dbBasePath() . '/' . DB_PATH_GLOBAL), undef, false, true);
executeTest(
'cp ' . $self->dataPath() . '/backup.pg_control_' . WAL_VERSION_94 . '.bin ' . $oHostDbMaster->dbBasePath() . '/' .
DB_FILE_PGCONTROL);
my $strCommand =
$oHostDbMaster->backrestExe() .
' --config=' . $oHostDbMaster->backrestConfig() .
' --stanza=' . $self->stanza() . ' archive-get';
# Fail on missing archive info file
$oHostDbMaster->executeSimple(
$strCommand . " 000000010000000100000001 ${strXlogPath}/000000010000000100000001",
{iExpectedExitStatus => ERROR_FILE_MISSING, oLogTest => $self->expect()});
# Create the archive info file
$oHostBackup->stanzaCreate('create required data for stanza', {strOptionalParam => '--no-' . OPTION_ONLINE});
if (defined($self->expect()))
{
$self->expect()->supplementalAdd($oFile->pathGet(PATH_BACKUP_ARCHIVE) . '/archive.info');
}
if ($bExists)
{
# Loop through archive files
my $strArchiveFile;
for (my $iArchiveNo = 1; $iArchiveNo <= $iArchiveMax; $iArchiveNo++)
{
# Construct the archive filename
if ($iArchiveNo > 255)
{
confess 'backup total * archive total cannot be greater than 255';
}
$strArchiveFile = uc(sprintf('0000000100000001%08x', $iArchiveNo));
&log(INFO, ' archive ' .sprintf('%02x', $iArchiveNo) .
" - ${strArchiveFile}");
my $strSourceFile = "${strArchiveFile}-${strArchiveChecksum}";
if ($bCompress)
{
$strSourceFile .= '.gz';
}
# Change the directory permissions to enable file creation
executeTest('sudo chmod 770 ' . dirname($oFile->pathGet(PATH_BACKUP_ARCHIVE, PG_VERSION_94 . "-1")));
filePathCreate(
dirname(
$oFile->pathGet(PATH_BACKUP_ARCHIVE, PG_VERSION_94 . "-1/${strSourceFile}")), '0770', true, true);
$oFile->copy(
PATH_DB_ABSOLUTE, $strArchiveTestFile, # Source file $strArchiveTestFile
PATH_BACKUP_ARCHIVE, PG_VERSION_94 . # Destination file
"-1/${strSourceFile}",
false, # Source is not compressed
$bCompress, # Destination compress based on test
undef, undef, # Unused params
'0660', # Mode
true); # Create path if it does not exist
my $strDestinationFile = "${strXlogPath}/${strArchiveFile}";
$oHostDbMaster->executeSimple(
$strCommand . ($bRemote && $iArchiveNo == 1 ? ' --cmd-ssh=/usr/bin/ssh' : '') .
" ${strArchiveFile} ${strDestinationFile}",
{oLogTest => $self->expect()});
# Check that the destination file exists
if ($oFile->exists(PATH_DB_ABSOLUTE, $strDestinationFile))
{
if ($oFile->hash(PATH_DB_ABSOLUTE, $strDestinationFile) ne $strArchiveChecksum)
{
confess "archive file hash does not match ${strArchiveChecksum}";
}
}
else
{
confess 'archive file is not in destination';
}
}
}
else
{
$oHostDbMaster->stop();
$oHostDbMaster->executeSimple(
$strCommand . " 000000090000000900000009 ${strXlogPath}/RECOVERYXLOG",
{iExpectedExitStatus => ERROR_STOP, oLogTest => $self->expect()});
$oHostDbMaster->start();
$oHostDbMaster->executeSimple(
$strCommand . " 000000090000000900000009 ${strXlogPath}/RECOVERYXLOG",
{iExpectedExitStatus => 1, oLogTest => $self->expect()});
}
}
}
}
}
1;

View File

@@ -0,0 +1,321 @@
####################################################################################################################################
# BackupArchivePushTest.pm - Tests for archive-push command
####################################################################################################################################
package pgBackRestTest::Backup::BackupArchivePushTest;
use parent 'pgBackRestTest::Backup::BackupCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use pgBackRest::ArchiveInfo;
use pgBackRest::BackupInfo;
use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Backup::BackupCommonTest;
####################################################################################################################################
# archiveCheck
#
# Check that a WAL segment is present in the repository.
####################################################################################################################################
sub archiveCheck
{
my $self = shift;
my $oFile = shift;
my $strArchiveFile = shift;
my $strArchiveChecksum = shift;
my $bCompress = shift;
# Build the archive name to check for at the destination
my $strArchiveCheck = PG_VERSION_94 . "-1/${strArchiveFile}-${strArchiveChecksum}";
if ($bCompress)
{
$strArchiveCheck .= '.gz';
}
my $oWait = waitInit(5);
my $bFound = false;
do
{
$bFound = $oFile->exists(PATH_BACKUP_ARCHIVE, $strArchiveCheck);
}
while (!$bFound && waitMore($oWait));
if (!$bFound)
{
confess 'unable to find ' . $strArchiveCheck;
}
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
my $strArchiveChecksum = '72b9da071c13957fb4ca31f05dbd5c644297c2f7';
my $iArchiveMax = 3;
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
for (my $bCompress = false; $bCompress <= true; $bCompress++)
{
for (my $bArchiveAsync = false; $bArchiveAsync <= true; $bArchiveAsync++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin("rmt ${bRemote}, cmp ${bCompress}, arc_async ${bArchiveAsync}")) {next}
# Create hosts, file object, and config
my ($oHostDbMaster, $oHostDbStandby, $oHostBackup, $oFile) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote, bCompress => $bCompress, bArchiveAsync => $bArchiveAsync});
# Create the xlog path
my $strXlogPath = $oHostDbMaster->dbBasePath() . '/pg_xlog';
filePathCreate($strXlogPath, undef, false, true);
# Create the test path for pg_control
filePathCreate(($oHostDbMaster->dbBasePath() . '/' . DB_PATH_GLOBAL), undef, false, true);
# Copy pg_control for stanza-create
executeTest(
'cp ' . $self->dataPath() . '/backup.pg_control_' . WAL_VERSION_94 . '.bin ' . $oHostDbMaster->dbBasePath() . '/' .
DB_FILE_PGCONTROL);
my $strCommand =
$oHostDbMaster->backrestExe() . ' --config=' . $oHostDbMaster->backrestConfig() .
' --no-fork --stanza=db archive-push';
# Test missing archive.info file
&log(INFO, ' test archive.info missing');
my ($strArchiveFile1, $strSourceFile1) = $self->archiveGenerate($oFile, $strXlogPath, 1, 1, WAL_VERSION_94);
$oHostDbMaster->executeSimple($strCommand . " ${strSourceFile1}",
{iExpectedExitStatus => ERROR_FILE_MISSING, oLogTest => $self->expect()});
# Create the archive info file
$oHostBackup->stanzaCreate('create required data for stanza',
{strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
if ($bArchiveAsync)
{
my $strDuplicateWal =
($bRemote ? $oHostDbMaster->spoolPath() :
$oHostBackup->repoPath()) .
'/archive/' . $self->stanza() . "/out/${strArchiveFile1}-1e34fa1c833090d94b9bb14f2a8d3153dca6ea27";
fileRemove($strDuplicateWal);
}
# Loop through backups
for (my $iBackup = 1; $iBackup <= 3; $iBackup++)
{
my $strArchiveFile;
# Loop through archive files
for (my $iArchive = 1; $iArchive <= $iArchiveMax; $iArchive++)
{
my $strSourceFile;
# Construct the archive filename
my $iArchiveNo = (($iBackup - 1) * $iArchiveMax + ($iArchive - 1)) + 1;
if ($iArchiveNo > 255)
{
confess 'backup total * archive total cannot be greater than 255';
}
($strArchiveFile, $strSourceFile) =
$self->archiveGenerate($oFile, $strXlogPath, 2, $iArchiveNo, WAL_VERSION_94);
&log(INFO, ' backup ' . sprintf('%02d', $iBackup) .
', archive ' .sprintf('%02x', $iArchive) .
" - ${strArchiveFile}");
my $strArchiveTmp = undef;
if ($iBackup == 1 && $iArchive == 2)
{
# Should succeed when temp file already exists
&log(INFO, ' test archive when tmp file exists');
$strArchiveTmp =
$oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' .
PG_VERSION_94 . '-1/' . substr($strArchiveFile, 0, 16) . "/${strArchiveFile}.pgbackrest.tmp";
executeTest('sudo chmod 770 ' . dirname($strArchiveTmp));
fileStringWrite($strArchiveTmp, 'JUNK');
if ($bRemote)
{
executeTest('sudo chown ' . $oHostBackup->userGet() . " ${strArchiveTmp}");
}
}
$oHostDbMaster->executeSimple(
$strCommand . ($bRemote && $iBackup == $iArchive ? ' --cmd-ssh=/usr/bin/ssh' : '') .
" ${strSourceFile}",
{oLogTest => $self->expect()});
# Make sure the temp file no longer exists
if (defined($strArchiveTmp))
{
my $oWait = waitInit(5);
my $bFound = true;
do
{
$bFound = fileExists($strArchiveTmp);
}
while ($bFound && waitMore($oWait));
if ($bFound)
{
confess "${strArchiveTmp} should have been removed by archive command";
}
}
if ($iArchive == $iBackup)
{
# load the archive info file and munge it for testing by breaking the database version
$oHostBackup->infoMunge(
$oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE),
{&INFO_ARCHIVE_SECTION_DB => {&INFO_ARCHIVE_KEY_DB_VERSION => '8.0'}});
&log(INFO, ' test db version mismatch error');
$oHostDbMaster->executeSimple(
$strCommand . " ${strSourceFile}",
{iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH, oLogTest => $self->expect()});
# Break the system id
$oHostBackup->infoMunge(
$oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE),
{&INFO_ARCHIVE_SECTION_DB => {&INFO_BACKUP_KEY_SYSTEM_ID => 5000900090001855000}});
&log(INFO, ' test db system-id mismatch error');
$oHostDbMaster->executeSimple(
$strCommand . " ${strSourceFile}",
{iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH, oLogTest => $self->expect()});
# Restore the file to its original condition
$oHostBackup->infoRestore($oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
# Fail because the process was killed
if ($iBackup == 1 && !$bCompress)
{
&log(INFO, ' test stop');
if ($bArchiveAsync)
{
my $oExecArchive =
$oHostDbMaster->execute(
$strCommand . ' --test --test-delay=5 --test-point=' .
lc(TEST_ARCHIVE_PUSH_ASYNC_START) . "=y ${strSourceFile}",
{oLogTest => $self->expect(), iExpectedExitStatus => ERROR_TERM});
$oExecArchive->begin();
$oExecArchive->end(TEST_ARCHIVE_PUSH_ASYNC_START);
$oHostDbMaster->stop({bForce => true});
$oExecArchive->end();
}
else
{
$oHostDbMaster->stop({strStanza => $oHostDbMaster->stanza()});
}
$oHostDbMaster->executeSimple(
$strCommand . " ${strSourceFile}",
{oLogTest => $self->expect(), iExpectedExitStatus => ERROR_STOP});
$oHostDbMaster->start({strStanza => $bArchiveAsync ? undef : $self->stanza()});
}
# Should succeed because checksum is the same
&log(INFO, ' test archive duplicate ok');
$oHostDbMaster->executeSimple($strCommand . " ${strSourceFile}", {oLogTest => $self->expect()});
# Now it should break on archive duplication (because checksum is different
&log(INFO, ' test archive duplicate error');
($strArchiveFile, $strSourceFile) =
$self->archiveGenerate($oFile, $strXlogPath, 1, $iArchiveNo, WAL_VERSION_94);
$oHostDbMaster->executeSimple(
$strCommand . " ${strSourceFile}",
{iExpectedExitStatus => ERROR_ARCHIVE_DUPLICATE, oLogTest => $self->expect()});
if ($bArchiveAsync)
{
my $strDuplicateWal =
($bRemote ? $oHostDbMaster->spoolPath() :
$oHostBackup->repoPath()) .
'/archive/' . $self->stanza() . "/out/${strArchiveFile}-1e34fa1c833090d94b9bb14f2a8d3153dca6ea27";
fileRemove($strDuplicateWal);
}
# Test .partial archive
&log(INFO, ' test .partial archive');
($strArchiveFile, $strSourceFile) =
$self->archiveGenerate($oFile, $strXlogPath, 2, $iArchiveNo, WAL_VERSION_94, true);
$oHostDbMaster->executeSimple(
$strCommand . " --no-" . OPTION_REPO_SYNC . " ${strSourceFile}", {oLogTest => $self->expect()});
$self->archiveCheck($oFile, $strArchiveFile, $strArchiveChecksum, $bCompress);
# Test .partial archive duplicate
&log(INFO, ' test .partial archive duplicate');
$oHostDbMaster->executeSimple($strCommand . " ${strSourceFile}", {oLogTest => $self->expect()});
# Test .partial archive with different checksum
&log(INFO, ' test .partial archive with different checksum');
($strArchiveFile, $strSourceFile) =
$self->archiveGenerate($oFile, $strXlogPath, 1, $iArchiveNo, WAL_VERSION_94, true);
$oHostDbMaster->executeSimple(
$strCommand . " ${strSourceFile}",
{iExpectedExitStatus => ERROR_ARCHIVE_DUPLICATE, oLogTest => $self->expect()});
if ($bArchiveAsync)
{
my $strDuplicateWal =
($bRemote ? $oHostDbMaster->spoolPath() : $oHostBackup->repoPath()) .
'/archive/' . $self->stanza() . "/out/${strArchiveFile}-1e34fa1c833090d94b9bb14f2a8d3153dca6ea27";
fileRemove($strDuplicateWal);
}
}
$self->archiveCheck($oFile, $strArchiveFile, $strArchiveChecksum, $bCompress);
}
# Might be nice to add tests for .backup files here (but this is already tested in full backup)
}
if (defined($self->expect()))
{
$self->expect()->supplementalAdd($oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
}
}
}
}
}
1;

View File

@@ -0,0 +1,122 @@
####################################################################################################################################
# BackupArchiveStopTest.pm - Tests for archive-push command to make sure aync queue limits are implemented correctly
####################################################################################################################################
package pgBackRestTest::Backup::BackupArchiveStopTest;
use parent 'pgBackRestTest::Backup::BackupCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use pgBackRest::ArchiveInfo;
use pgBackRest::BackupInfo;
use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Backup::BackupCommonTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
my $strArchiveTestFile = $self->dataPath() . '/backup.wal2_' . WAL_VERSION_94 . '.bin';
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
for (my $bCompress = false; $bCompress <= true; $bCompress++)
{
for (my $iError = 0; $iError <= $bRemote; $iError++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin("rmt ${bRemote}, cmp ${bCompress}, error " . ($iError ? 'connect' : 'version'))) {next}
# Create hosts, file object, and config
my ($oHostDbMaster, $oHostDbStandby, $oHostBackup, $oFile) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote, bCompress => $bCompress, bArchiveAsync => true});
# Create the xlog path
my $strXlogPath = $oHostDbMaster->dbBasePath() . '/pg_xlog';
filePathCreate($strXlogPath, undef, false, true);
# Create the test path for pg_control and copy pg_control for stanza-create
filePathCreate(($oHostDbMaster->dbBasePath() . '/' . DB_PATH_GLOBAL), undef, false, true);
executeTest(
'cp ' . $self->dataPath() . '/backup.pg_control_' . WAL_VERSION_94 . '.bin ' . $oHostDbMaster->dbBasePath() . '/' .
DB_FILE_PGCONTROL);
# Create the archive info file
$oHostBackup->stanzaCreate('create required data for stanza', {strOptionalParam => '--no-' . OPTION_ONLINE});
# Push a WAL segment
$oHostDbMaster->archivePush($strXlogPath, $strArchiveTestFile, 1);
# Break the database version of the archive info file
if ($iError == 0)
{
$oHostBackup->infoMunge(
$oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE),
{&INFO_ARCHIVE_SECTION_DB => {&INFO_ARCHIVE_KEY_DB_VERSION => '8.0'}});
}
# Push two more segments with errors to exceed archive-max-mb
$oHostDbMaster->archivePush(
$strXlogPath, $strArchiveTestFile, 2, $iError ? ERROR_HOST_CONNECT : ERROR_ARCHIVE_MISMATCH);
$oHostDbMaster->archivePush(
$strXlogPath, $strArchiveTestFile, 3, $iError ? ERROR_HOST_CONNECT : ERROR_ARCHIVE_MISMATCH);
# Now this segment will get dropped
$oHostDbMaster->archivePush($strXlogPath, $strArchiveTestFile, 4);
# Fix the database version
if ($iError == 0)
{
$oHostBackup->infoRestore($oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
}
# Remove the stop file
fileRemove($oHostDbMaster->spoolPath() . '/stop/db-archive.stop');
# Check the dir to be sure that segment 2 and 3 were not pushed yet
executeTest(
'ls -1R ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . "-1/0000000100000001",
{oLogTest => $self->expect(), bRemote => $bRemote});
# Push segment 5
$oHostDbMaster->archivePush($strXlogPath, $strArchiveTestFile, 5, undef, false);
# Check that 5 is pushed
executeTest(
'ls -1R ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . "-1/0000000100000001",
{oLogTest => $self->expect(), bRemote => $bRemote});
# Call push without a segment
$oHostDbMaster->archivePush($strXlogPath);
# Check the dir to be sure that segment 2 and 3 were pushed
executeTest(
'ls -1R ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . "-1/0000000100000001",
{oLogTest => $self->expect(), bRemote => $bRemote});
}
}
}
}
1;

View File

@@ -1,7 +1,8 @@
####################################################################################################################################
# BackupCommonTest.pm - Common code for backup unit tests
# BackupCommonTest.pm - Common code for backup tests
####################################################################################################################################
package pgBackRestTest::Backup::BackupCommonTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
@@ -15,20 +16,38 @@ use Exporter qw(import);
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRestTest::Backup::Common::HostBackupTest;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Backup::Common::HostDbCommonTest;
use pgBackRestTest::Backup::Common::HostDbTest;
use pgBackRestTest::Backup::Common::HostDbSyntheticTest;
use pgBackRestTest::Common::Host::HostBackupTest;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::Host::HostDbCommonTest;
use pgBackRestTest::Common::Host::HostDbTest;
use pgBackRestTest::Common::Host::HostDbSyntheticTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::CommonTest;
####################################################################################################################################
# backupTestSetup
# Constants
####################################################################################################################################
sub backupTestSetup
use constant WAL_VERSION_94 => '94';
push @EXPORT, qw(WAL_VERSION_94);
####################################################################################################################################
# init
####################################################################################################################################
sub init
{
# Set file and path modes
pathModeDefaultSet('0700');
fileModeDefaultSet('0600');
}
####################################################################################################################################
# setup
####################################################################################################################################
sub setup
{
my $self = shift;
my $bSynthetic = shift;
my $oLogTest = shift;
my $oConfigParam = shift;
@@ -45,7 +64,7 @@ sub backupTestSetup
{
$strBackupDestination = defined($$oConfigParam{strBackupDestination}) ? $$oConfigParam{strBackupDestination} : HOST_BACKUP;
$oHostBackup = new pgBackRestTest::Backup::Common::HostBackupTest(
$oHostBackup = new pgBackRestTest::Common::Host::HostBackupTest(
{strBackupDestination => $strBackupDestination, bSynthetic => $bSynthetic, oLogTest => $oLogTest});
$oHostGroup->hostAdd($oHostBackup);
}
@@ -60,12 +79,12 @@ sub backupTestSetup
if ($bSynthetic)
{
$oHostDbMaster = new pgBackRestTest::Backup::Common::HostDbSyntheticTest(
$oHostDbMaster = new pgBackRestTest::Common::Host::HostDbSyntheticTest(
{strBackupDestination => $strBackupDestination, oLogTest => $oLogTest});
}
else
{
$oHostDbMaster = new pgBackRestTest::Backup::Common::HostDbTest(
$oHostDbMaster = new pgBackRestTest::Common::Host::HostDbTest(
{strBackupDestination => $strBackupDestination, oLogTest => $oLogTest});
}
@@ -76,7 +95,7 @@ sub backupTestSetup
if (defined($$oConfigParam{bStandby}) && $$oConfigParam{bStandby})
{
$oHostDbStandby = new pgBackRestTest::Backup::Common::HostDbTest(
$oHostDbStandby = new pgBackRestTest::Common::Host::HostDbTest(
{strBackupDestination => $strBackupDestination, bStandby => true, oLogTest => $oLogTest});
$oHostGroup->hostAdd($oHostDbStandby);
@@ -130,6 +149,33 @@ sub backupTestSetup
return $oHostDbMaster, $oHostDbStandby, $oHostBackup, $oFile;
}
push @EXPORT, qw(backupTestSetup);
####################################################################################################################################
# archiveGenerate
#
# Generate an WAL segment for testing.
####################################################################################################################################
sub archiveGenerate
{
my $self = shift;
my $oFile = shift;
my $strXlogPath = shift;
my $iSourceNo = shift;
my $iArchiveNo = shift;
my $strWalVersion = shift;
my $bPartial = shift;
my $strArchiveFile = uc(sprintf('0000000100000001%08x', $iArchiveNo)) .
(defined($bPartial) && $bPartial ? '.partial' : '');
my $strArchiveTestFile = $self->dataPath() . "/backup.wal${iSourceNo}_${strWalVersion}.bin";
my $strSourceFile = "${strXlogPath}/${strArchiveFile}";
$oFile->copy(PATH_DB_ABSOLUTE, $strArchiveTestFile, # Source file
PATH_DB_ABSOLUTE, $strSourceFile, # Destination file
false, # Source is not compressed
false); # Destination is not compressed
return $strArchiveFile, $strSourceFile;
}
1;

View File

@@ -0,0 +1,134 @@
####################################################################################################################################
# BackupExpireTest.pm - Tests for expire command
####################################################################################################################################
package pgBackRestTest::Backup::BackupExpireTest;
use parent 'pgBackRestTest::Backup::BackupCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use pgBackRest::ArchiveInfo;
use pgBackRest::BackupInfo;
use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Backup::BackupCommonTest;
use pgBackRestTest::Backup::ExpireCommonTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
if ($self->begin("local"))
{
# Create hosts, file object, and config
my ($oHostDbMaster, $oHostDbStandby, $oHostBackup, $oFile) = $self->setup(true, $self->expect());
# Create the test object
my $oExpireTest = new pgBackRestTest::Backup::ExpireCommonTest($oHostBackup, $self->backrestExe(), $oFile, $self->expect());
# ??? This function creates data elements in the $oExpireTest object that are used by the $oExpireTest functions. But
# should probably change to use the stanza-create command especially with stanza-upgrade.
$oExpireTest->stanzaCreate($self->stanza(), PG_VERSION_92);
use constant SECONDS_PER_DAY => 86400;
my $lBaseTime = time() - (SECONDS_PER_DAY * 56);
#-----------------------------------------------------------------------------------------------------------------------
my $strDescription = 'Nothing to expire';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_INCR, $lBaseTime += SECONDS_PER_DAY, 246);
$oExpireTest->process($self->stanza(), 1, 1, BACKUP_TYPE_FULL, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire oldest full backup, archive expire falls on segment major boundary';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), 1, 1, BACKUP_TYPE_FULL, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire oldest full backup';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY, 256);
$oExpireTest->process($self->stanza(), 1, 1, BACKUP_TYPE_FULL, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire oldest diff backup, archive expire does not fall on major segment boundary';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY, undef, 0);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_INCR, $lBaseTime += SECONDS_PER_DAY, undef, 0);
$oExpireTest->process($self->stanza(), 1, 1, BACKUP_TYPE_DIFF, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire oldest diff backup (cascade to incr)';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), 1, 1, BACKUP_TYPE_DIFF, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire archive based on newest incr backup';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_INCR, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), 1, 1, BACKUP_TYPE_INCR, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire diff treating full as diff';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), 2, 1, BACKUP_TYPE_DIFF, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire diff with retention-archive with warning retention-diff not set';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), undef, undef, BACKUP_TYPE_DIFF, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire full with retention-archive with warning retention-full not set';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), undef, undef, BACKUP_TYPE_FULL, 1, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire no archive with warning since retention-archive not set for INCR';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_INCR, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), 1, 1, BACKUP_TYPE_INCR, undef, $strDescription);
#-----------------------------------------------------------------------------------------------------------------------
$strDescription = 'Expire no archive with warning since neither retention-archive nor retention-diff is set';
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_FULL, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->backupCreate($self->stanza(), BACKUP_TYPE_DIFF, $lBaseTime += SECONDS_PER_DAY);
$oExpireTest->process($self->stanza(), undef, undef, BACKUP_TYPE_DIFF, undef, $strDescription);
}
}
1;

View File

@@ -0,0 +1,864 @@
####################################################################################################################################
# BackupFullTest.pm - Tests for all commands against a real database
####################################################################################################################################
package pgBackRestTest::Backup::BackupFullTest;
use parent 'pgBackRestTest::Backup::BackupCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use pgBackRest::ArchiveInfo;
use pgBackRest::BackupInfo;
use pgBackRest::Db;
use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRest::Version;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::FileTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Backup::BackupCommonTest;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::Host::HostBackupTest;
use pgBackRestTest::Common::Host::HostDbTest;
use pgBackRestTest::Backup::ExpireCommonTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
foreach my $bHostBackup (false, true)
{
foreach my $bHostStandby (false, true)
{
foreach my $strBackupDestination (
$bHostBackup ? (HOST_BACKUP) : $bHostStandby ? (HOST_DB_MASTER, HOST_DB_STANDBY) : (HOST_DB_MASTER))
{
foreach my $bArchiveAsync ($bHostStandby ? (false) : (false, true))
{
foreach my $bCompress ($bHostStandby ? (false) : (false, true))
{
# Increment the run, log, and decide whether this unit test should be run
next if (!$self->begin(
"bkp ${bHostBackup}, sby ${bHostStandby}, dst ${strBackupDestination}, asy ${bArchiveAsync}, cmp ${bCompress}",
$self->processMax() == 1 && $self->pgVersion() eq PG_VERSION_95));
if ($bHostStandby && $self->pgVersion() < PG_VERSION_HOT_STANDBY)
{
&log(INFO, 'skipped - this version of PostgreSQL does not support hot standby');
next;
}
# Create hosts, file object, and config
my ($oHostDbMaster, $oHostDbStandby, $oHostBackup, $oFile) = $self->setup(
false, $self->expect(),
{bHostBackup => $bHostBackup, bStandby => $bHostStandby, strBackupDestination => $strBackupDestination,
bCompress => $bCompress, bArchiveAsync => $bArchiveAsync});
# Determine if extra tests are performed. Extra tests should not be primary tests for compression or async archiving.
my $bTestExtra = ($self->runCurrent() == 1) || ($self->runCurrent() == 7);
$oHostDbMaster->clusterCreate();
# Create the stanza
$oHostBackup->stanzaCreate('main create stanza info files');
# Static backup parameters
my $fTestDelay = 1;
# Variable backup parameters
my $bDelta = true;
my $bForce = false;
my $strType = undef;
my $strTarget = undef;
my $bTargetExclusive = false;
my $strTargetAction;
my $strTargetTimeline = undef;
my $oRecoveryHashRef = undef;
my $strComment = undef;
my $iExpectedExitStatus = undef;
# Restore test string
my $strDefaultMessage = 'default';
my $strFullMessage = 'full';
my $strStandbyMessage = 'standby';
my $strIncrMessage = 'incr';
my $strTimeMessage = 'time';
my $strXidMessage = 'xid';
my $strNameMessage = 'name';
my $strTimelineMessage = 'timeline3';
# Create two new databases
$oHostDbMaster->sqlExecute('create database test1', {bAutoCommit => true});
$oHostDbMaster->sqlExecute('create database test2', {bAutoCommit => true});
# Test check command and stanza create
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra)
{
$strType = BACKUP_TYPE_FULL;
# Remove the files in the archive directory
executeTest('sudo rm -rf ' . $oFile->pathGet(PATH_BACKUP_ARCHIVE) . "/*");
$oHostDbMaster->check(
'fail on missing archive.info file',
{iTimeout => 0.1, iExpectedExitStatus => ERROR_FILE_MISSING});
# Backup.info was created earlier so force stanza-create to create archive info file
$oHostBackup->stanzaCreate('force create stanza info files', {strOptionalParam => ' --' . OPTION_FORCE});
# Check ERROR_ARCHIVE_DISABLED error
$strComment = 'fail on archive_mode=off';
$oHostDbMaster->clusterRestart({bIgnoreLogError => true, bArchiveEnabled => false});
$oHostBackup->backup($strType, $strComment, {iExpectedExitStatus => ERROR_ARCHIVE_DISABLED});
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_DISABLED});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_DISABLED});
}
# Check ERROR_ARCHIVE_COMMAND_INVALID error
$strComment = 'fail on invalid archive_command';
$oHostDbMaster->clusterRestart({bIgnoreLogError => true, bArchive => false});
$oHostBackup->backup($strType, $strComment, {iExpectedExitStatus => ERROR_ARCHIVE_COMMAND_INVALID});
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_COMMAND_INVALID});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_COMMAND_INVALID});
}
# When archive-check=n then ERROR_ARCHIVE_TIMEOUT will be raised instead of ERROR_ARCHIVE_COMMAND_INVALID
# ??? But maybe we should error with the fact that that option is not valid
$strComment = 'fail on archive timeout when archive-check=n';
$oHostDbMaster->check(
$strComment,
{iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_TIMEOUT, strOptionalParam => '--no-archive-check'});
# Stop the cluster ignoring any errors in the postgresql log
$oHostDbMaster->clusterStop({bIgnoreLogError => true});
# Providing a sufficient archive-timeout, verify that the check command runs successfully.
$strComment = 'verify success';
$oHostDbMaster->clusterStart();
$oHostDbMaster->check($strComment, {iTimeout => 5});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 5});
}
# Check archive mismatch due to upgrade error
$strComment = 'fail on archive mismatch after upgrade';
# load the archive info file and munge it for testing by breaking the database version
$oHostBackup->infoMunge(
$oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE),
{&INFO_ARCHIVE_SECTION_DB => {&INFO_ARCHIVE_KEY_DB_VERSION => '8.0'}});
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH});
}
# Restore the file to its original condition
$oHostBackup->infoRestore($oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
# Check archive_timeout error when WAL segment is not found
$strComment = 'fail on archive timeout';
$oHostDbMaster->clusterRestart({bIgnoreLogError => true, bArchiveInvalid => true});
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_TIMEOUT});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_TIMEOUT});
}
# Restart the cluster ignoring any errors in the postgresql log
$oHostDbMaster->clusterRestart({bIgnoreLogError => true});
# With a valid archive info, create the backup.info file by running a backup then munge the backup.info file.
# Check backup mismatch error
$strComment = 'fail on backup info mismatch';
# First run a successful backup to create the backup.info file
$oHostBackup->backup($strType, 'run a successful backup');
# Load the backup.info file and munge it for testing by breaking the database version and system id
$oHostBackup->infoMunge(
$oFile->pathGet(PATH_BACKUP_CLUSTER, FILE_BACKUP_INFO),
{&INFO_BACKUP_SECTION_DB =>
{&INFO_BACKUP_KEY_DB_VERSION => '8.0', &INFO_BACKUP_KEY_SYSTEM_ID => 6999999999999999999}});
# Run the test
$oHostDbMaster->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
}
# Restore the file to its original condition
$oHostBackup->infoRestore($oFile->pathGet(PATH_BACKUP_CLUSTER, FILE_BACKUP_INFO));
# Providing a sufficient archive-timeout, verify that the check command runs successfully now with valid
# archive.info and backup.info files
$strComment = 'verify success after backup';
$oHostDbMaster->check($strComment, {iTimeout => 5});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 5});
}
# Restart the cluster ignoring any errors in the postgresql log
$oHostDbMaster->clusterRestart({bIgnoreLogError => true});
# Stanza Create - ??? move to stanza-create tests when can create a backup synthetically
#-----------------------------------------------------------------------------------------------------------------------
# With data existing in the archive and backup directory, remove backup info file and confirm failure
executeTest('sudo rm ' . $oHostBackup->repoPath() . '/backup/' . $self->stanza() . '/backup.info');
$oHostBackup->stanzaCreate('fail on backup info file missing from non-empty dir',
{iExpectedExitStatus => ERROR_PATH_NOT_EMPTY});
# Force the backup.info file to be recreated
$oHostBackup->stanzaCreate('verify success with force', {strOptionalParam => ' --' . OPTION_FORCE});
# Remove the backup info file
executeTest('sudo rm ' . $oHostBackup->repoPath() . '/backup/' . $self->stanza() . '/backup.info');
# Change the database version by copying a new pg_control file
executeTest('sudo mv ' . $oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL .
' ' . $oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL . 'save');
executeTest(
'cp ' . $self->dataPath() . '/backup.pg_control_' . WAL_VERSION_94 . '.bin ' . $oHostDbMaster->dbBasePath() . '/' .
DB_FILE_PGCONTROL);
# Run stanza-create with --force
$oHostBackup->stanzaCreate('test force fails on database mismatch with directory',
{iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH, strOptionalParam => '--no-' . OPTION_ONLINE .
' --' . OPTION_FORCE});
# Restore the database version
executeTest('sudo rm ' . $oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL);
executeTest('sudo mv ' . $oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL . 'save' .
' ' . $oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL);
# Run stanza-create offline with --force
$oHostBackup->stanzaCreate('restore stanza files',
{strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
}
# Full backup
#---------------------------------------------------------------------------------------------------------------------------
$strType = BACKUP_TYPE_FULL;
# Create the table where test messages will be stored
$oHostDbMaster->sqlExecute("create table test (message text not null)");
$oHostDbMaster->sqlXlogRotate();
$oHostDbMaster->sqlExecute("insert into test values ('$strDefaultMessage')");
if ($bTestExtra)
{
# Acquire the backup advisory lock so it looks like a backup is running
if (!$oHostDbMaster->sqlSelectOne('select pg_try_advisory_lock(' . DB_BACKUP_ADVISORY_LOCK . ')'))
{
confess 'unable to acquire advisory lock for testing';
}
$oHostBackup->backup($strType, 'fail on backup lock exists', {iExpectedExitStatus => ERROR_LOCK_ACQUIRE});
# Release the backup advisory lock so the next backup will succeed
if (!$oHostDbMaster->sqlSelectOne('select pg_advisory_unlock(' . DB_BACKUP_ADVISORY_LOCK . ')'))
{
confess 'unable to release advisory lock';
}
}
my $oExecuteBackup = $oHostBackup->backupBegin(
$strType, 'update during backup',
{strTest => TEST_MANIFEST_BUILD, fTestDelay => $fTestDelay,
strOptionalParam => ' --' . OPTION_BUFFER_SIZE . '=16384'});
$oHostDbMaster->sqlExecute("update test set message = '$strFullMessage'");
# Required to set hint bits to be sent to the standby to make the heap match on both sides
$oHostDbMaster->sqlSelectOneTest('select message from test', $strFullMessage);
my $strFullBackup = $oHostBackup->backupEnd($strType, $oExecuteBackup);
# Kick out a bunch of archive logs to excercise async archiving. Only do this when compressed and remote to slow it
# down enough to make it evident that the async process is working.
if ($bArchiveAsync && $bCompress && $strBackupDestination eq HOST_BACKUP)
{
&log(INFO, ' multiple pg_switch_xlog() to exercise async archiving');
$oHostDbMaster->sqlExecute("create table xlog_activity (id int)");
$oHostDbMaster->sqlXlogRotate();
$oHostDbMaster->sqlExecute("insert into xlog_activity values (1)");
$oHostDbMaster->sqlXlogRotate();
$oHostDbMaster->sqlExecute("insert into xlog_activity values (2)");
$oHostDbMaster->sqlXlogRotate();
$oHostDbMaster->sqlExecute("insert into xlog_activity values (3)");
$oHostDbMaster->sqlXlogRotate();
$oHostDbMaster->sqlExecute("insert into xlog_activity values (4)");
$oHostDbMaster->sqlXlogRotate();
}
# Setup replica
#---------------------------------------------------------------------------------------------------------------------------
if ($bHostStandby)
{
$bDelta = false;
$bForce = false;
$strType = RECOVERY_TYPE_DEFAULT;
$strTarget = undef;
$bTargetExclusive = undef;
$strTargetAction = undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
$strComment = 'restore backup on replica';
my %oRemapHash;
$oRemapHash{&MANIFEST_TARGET_PGDATA} = $oHostDbStandby->dbBasePath();
if ($oHostDbStandby->pgVersion() >= PG_VERSION_92)
{
$oHostDbStandby->linkRemap(DB_PATH_PGXLOG, $oHostDbStandby->dbPath() . '/' . DB_PATH_PGXLOG);
}
$oHostDbStandby->restore(
OPTION_DEFAULT_RESTORE_SET, undef, \%oRemapHash, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive,
$strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus,
' --recovery-option=standby_mode=on' .
' --recovery-option="primary_conninfo=host=' . HOST_DB_MASTER .
' port=' . $oHostDbMaster->pgPort() . ' user=replicator"');
$oHostDbStandby->clusterStart({bHotStandby => true});
# Make sure streaming replication is on
$oHostDbMaster->sqlSelectOneTest(
"select client_addr || '-' || state from pg_stat_replication", $oHostDbStandby->ipGet() . '/32-streaming');
# Check that the cluster was restored properly
$oHostDbStandby->sqlSelectOneTest('select message from test', $strFullMessage);
# Update message for standby
$oHostDbMaster->sqlExecute("update test set message = '$strStandbyMessage'");
my $strStandbyBackup = $oHostBackup->backup(
BACKUP_TYPE_FULL, 'backup from standby',
{bStandby => true,
iExpectedExitStatus => $oHostDbStandby->pgVersion() >= PG_VERSION_BACKUP_STANDBY ? undef : ERROR_CONFIG,
strOptionalParam => '--' . OPTION_RETENTION_FULL . '=1'});
if ($oHostDbStandby->pgVersion() >= PG_VERSION_BACKUP_STANDBY)
{
$strFullBackup = $strStandbyBackup;
}
# Confirm the check command runs without error on a standby
$oHostDbStandby->check('verify check command on standby');
}
# Execute stop and make sure the backup fails
#---------------------------------------------------------------------------------------------------------------------------
# Restart the cluster to check for any errors before continuing since the stop tests will definitely create errors and
# the logs will to be deleted to avoid causing issues further down the line.
if ($bTestExtra)
{
$strType = BACKUP_TYPE_INCR;
$oHostDbMaster->clusterRestart();
$oHostDbMaster->stop();
$oHostBackup->backup($strType, 'attempt backup when stopped', {iExpectedExitStatus => ERROR_STOP});
$oHostDbMaster->start();
}
# Setup the time target
#---------------------------------------------------------------------------------------------------------------------------
$oHostDbMaster->sqlExecute("update test set message = '$strTimeMessage'");
$oHostDbMaster->sqlXlogRotate();
my $strTimeTarget = $oHostDbMaster->sqlSelectOne("select to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS.US TZ')");
&log(INFO, " time target is ${strTimeTarget}");
# Incr backup - fail on archive_mode=always when version >= 9.5
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra && $oHostDbMaster->pgVersion() >= PG_VERSION_95)
{
$strType = BACKUP_TYPE_INCR;
# Set archive_mode=always
$oHostDbMaster->clusterRestart({bArchiveAlways => true});
$oHostBackup->backup($strType, 'fail on archive_mode=always', {iExpectedExitStatus => ERROR_FEATURE_NOT_SUPPORTED});
# Reset the cluster to a normal state so the next test will work
$oHostDbMaster->clusterRestart();
}
# Incr backup
#---------------------------------------------------------------------------------------------------------------------------
$strType = BACKUP_TYPE_INCR;
# Create a tablespace directory
filePathCreate($oHostDbMaster->tablespacePath(1), undef, undef, true);
# Also create it on the standby so replay won't fail
if (defined($oHostDbStandby))
{
filePathCreate($oHostDbStandby->tablespacePath(1), undef, undef, true);
}
$oHostDbMaster->sqlExecute(
"create tablespace ts1 location '" . $oHostDbMaster->tablespacePath(1) . "'", {bAutoCommit => true});
$oHostDbMaster->sqlExecute("alter table test set tablespace ts1", {bCheckPoint => true});
# Create a table in the tablespace
$oHostDbMaster->sqlExecute("create table test_remove (id int)");
$oHostDbMaster->sqlXlogRotate();
$oHostDbMaster->sqlExecute("update test set message = '$strDefaultMessage'");
$oHostDbMaster->sqlXlogRotate();
# Start a backup so the next backup has to restart it. This test is not required for PostgreSQL >= 9.6 since backups
# are run in non-exlusive mode.
if ($bTestExtra && $oHostDbMaster->pgVersion() >= PG_VERSION_93 && $oHostDbMaster->pgVersion() < PG_VERSION_96)
{
$oHostDbMaster->sqlSelectOne("select pg_start_backup('test backup that will cause an error', true)");
# Verify that an error is returned if the backup is already running
$oHostBackup->backup($strType, 'fail on backup already running', {iExpectedExitStatus => ERROR_DB_QUERY});
# Restart the cluster ignoring any errors in the postgresql log
$oHostDbMaster->clusterRestart({bIgnoreLogError => true});
# Start a new backup to make the next test restart it
$oHostDbMaster->sqlSelectOne("select pg_start_backup('test backup that will be restarted', true)");
}
$oExecuteBackup = $oHostBackup->backupBegin(
$strType, 'update during backup',
{strTest => TEST_MANIFEST_BUILD, fTestDelay => $fTestDelay,
strOptionalParam => '--' . OPTION_STOP_AUTO . ' --no-' . OPTION_BACKUP_ARCHIVE_CHECK .
' --' . OPTION_BUFFER_SIZE . '=24576'});
# Drop a table
$oHostDbMaster->sqlExecute('drop table test_remove');
$oHostDbMaster->sqlXlogRotate();
$oHostDbMaster->sqlExecute("update test set message = '$strIncrMessage'", {bCommit => true});
# Check that application name is set
if ($oHostDbMaster->pgVersion() >= PG_VERSION_APPLICATION_NAME)
{
my $strApplicationNameExpected = BACKREST_NAME . ' [' . CMD_BACKUP . ']';
my $strApplicationName = $oHostDbMaster->sqlSelectOne(
"select application_name from pg_stat_activity where application_name like '" . BACKREST_NAME . "%'");
if (!defined($strApplicationName) || $strApplicationName ne $strApplicationNameExpected)
{
confess &log(ERROR,
"application name '" . (defined($strApplicationName) ? $strApplicationName : '[null]') .
"' does not match '" . $strApplicationNameExpected . "'");
}
}
my $strIncrBackup = $oHostBackup->backupEnd($strType, $oExecuteBackup);
# Setup the xid target
#---------------------------------------------------------------------------------------------------------------------------
my $strXidTarget = undef;
if ($bTestExtra)
{
$oHostDbMaster->sqlExecute("update test set message = '$strXidMessage'", {bCommit => false});
$oHostDbMaster->sqlXlogRotate();
$strXidTarget = $oHostDbMaster->sqlSelectOne("select txid_current()");
$oHostDbMaster->sqlCommit();
&log(INFO, " xid target is ${strXidTarget}");
}
# Setup the name target
#---------------------------------------------------------------------------------------------------------------------------
my $strNameTarget = 'backrest';
if ($bTestExtra)
{
$oHostDbMaster->sqlExecute("update test set message = '$strNameMessage'", {bCommit => true});
$oHostDbMaster->sqlXlogRotate();
if ($oHostDbMaster->pgVersion() >= PG_VERSION_91)
{
$oHostDbMaster->sqlExecute("select pg_create_restore_point('${strNameTarget}')");
}
&log(INFO, " name target is ${strNameTarget}");
}
# Create a table and data in database test2
#---------------------------------------------------------------------------------------------------------------------------
$oHostDbMaster->sqlExecute(
'create table test (id int);' .
'insert into test values (1);' .
'create table test_ts1 (id int) tablespace ts1;' .
'insert into test_ts1 values (2);',
{strDb => 'test2', bAutoCommit => true});
$oHostDbMaster->sqlXlogRotate();
# Restore (type = default)
#---------------------------------------------------------------------------------------------------------------------------
$bDelta = false;
$bForce = false;
$strType = RECOVERY_TYPE_DEFAULT;
$strTarget = undef;
$bTargetExclusive = undef;
$strTargetAction = undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
# &log(INFO, " testing recovery type = ${strType}");
if ($bTestExtra)
{
# Expect failure because postmaster.pid exists
$strComment = 'postmaster running';
$iExpectedExitStatus = ERROR_POSTMASTER_RUNNING;
$oHostDbMaster->restore(
OPTION_DEFAULT_RESTORE_SET, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive,
$strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
}
$oHostDbMaster->clusterStop();
if ($bTestExtra)
{
# Expect failure because db path is not empty
$strComment = 'path not empty';
$iExpectedExitStatus = ERROR_PATH_NOT_EMPTY;
$oHostDbMaster->restore(
OPTION_DEFAULT_RESTORE_SET, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive,
$strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
}
# Drop and recreate db path
testPathRemove($oHostDbMaster->dbBasePath());
filePathCreate($oHostDbMaster->dbBasePath());
testPathRemove($oHostDbMaster->dbPath() . '/pg_xlog');
filePathCreate($oHostDbMaster->dbPath() . '/pg_xlog');
testPathRemove($oHostDbMaster->tablespacePath(1));
filePathCreate($oHostDbMaster->tablespacePath(1));
# Now the restore should work
$strComment = undef;
$iExpectedExitStatus = undef;
$oHostDbMaster->restore(
OPTION_DEFAULT_RESTORE_SET, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive,
$strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus, ' --db-include=test1');
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest('select message from test', $bTestExtra ? $strNameMessage : $strIncrMessage);
# Now it should be OK to drop database test2
$oHostDbMaster->sqlExecute('drop database test2', {bAutoCommit => true});
# The test table lives in ts1 so it needs to be moved or dropped
if ($oHostDbMaster->pgVersion() >= PG_VERSION_90)
{
$oHostDbMaster->sqlExecute('alter table test set tablespace pg_default');
}
# Drop for older versions
else
{
$oHostDbMaster->sqlExecute('drop table test');
}
# And drop the tablespace
$oHostDbMaster->sqlExecute("drop tablespace ts1", {bAutoCommit => true});
# Restore (restore type = immediate, inclusive)
#---------------------------------------------------------------------------------------------------------------------------
if (($bTestExtra || $bHostStandby) && $oHostDbMaster->pgVersion() >= PG_VERSION_94)
{
$bDelta = false;
$bForce = true;
$strType = RECOVERY_TYPE_IMMEDIATE;
$strTarget = undef;
$bTargetExclusive = undef;
$strTargetAction = undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
&log(INFO, " testing recovery type = ${strType}");
$oHostDbMaster->clusterStop();
$oHostDbMaster->restore(
$strFullBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus, undef);
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest(
'select message from test', ($bHostStandby ? $strStandbyMessage : $strFullMessage));
}
# Restore (restore type = xid, inclusive)
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra)
{
$bDelta = false;
$bForce = true;
$strType = RECOVERY_TYPE_XID;
$strTarget = $strXidTarget;
$bTargetExclusive = undef;
$strTargetAction = $oHostDbMaster->pgVersion() >= PG_VERSION_91 ? 'promote' : undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
&log(INFO, " testing recovery type = ${strType}");
$oHostDbMaster->clusterStop();
executeTest('rm -rf ' . $oHostDbMaster->dbBasePath() . "/*");
executeTest('rm -rf ' . $oHostDbMaster->dbPath() . "/pg_xlog/*");
$oHostDbMaster->restore(
$strIncrBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus,
'--tablespace-map-all=../../tablespace', false);
# Save recovery file to test so we can use it in the next test
$oFile->copy(PATH_ABSOLUTE, $oHostDbMaster->dbBasePath() . '/recovery.conf',
PATH_ABSOLUTE, $self->testPath() . '/recovery.conf');
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest('select message from test', $strXidMessage);
$oHostDbMaster->sqlExecute("update test set message = '$strTimelineMessage'");
}
# Restore (restore type = preserve, inclusive)
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra)
{
$bDelta = false;
$bForce = false;
$strType = RECOVERY_TYPE_PRESERVE;
$strTarget = undef;
$bTargetExclusive = undef;
$strTargetAction = undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
&log(INFO, " testing recovery type = ${strType}");
$oHostDbMaster->clusterStop();
executeTest('rm -rf ' . $oHostDbMaster->dbBasePath() . "/*");
executeTest('rm -rf ' . $oHostDbMaster->dbPath() . "/pg_xlog/*");
executeTest('rm -rf ' . $oHostDbMaster->tablespacePath(1) . "/*");
# Restore recovery file that was saved in last test
$oFile->move(PATH_ABSOLUTE, $self->testPath . '/recovery.conf',
PATH_ABSOLUTE, $oHostDbMaster->dbBasePath() . '/recovery.conf');
$oHostDbMaster->restore(
OPTION_DEFAULT_RESTORE_SET, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive,
$strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest('select message from test', $strXidMessage);
$oHostDbMaster->sqlExecute("update test set message = '$strTimelineMessage'");
}
# Restore (restore type = time, inclusive) - there is no exclusive time test because I can't find a way to find the
# exact commit time of a transaction.
#---------------------------------------------------------------------------------------------------------------------------
$bDelta = true;
$bForce = false;
$strType = RECOVERY_TYPE_TIME;
$strTarget = $strTimeTarget;
$bTargetExclusive = undef;
$strTargetAction = undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
&log(INFO, " testing recovery type = ${strType}");
$oHostDbMaster->clusterStop();
$oHostDbMaster->restore(
$strFullBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest('select message from test', $strTimeMessage);
# Restore (restore type = xid, exclusive)
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra)
{
$bDelta = true;
$bForce = false;
$strType = RECOVERY_TYPE_XID;
$strTarget = $strXidTarget;
$bTargetExclusive = true;
$strTargetAction = undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
&log(INFO, " testing recovery type = ${strType}");
$oHostDbMaster->clusterStop();
$oHostDbMaster->restore(
$strIncrBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest('select message from test', $strIncrMessage);
}
# Restore (restore type = name)
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra && $oHostDbMaster->pgVersion() >= PG_VERSION_91)
{
$bDelta = true;
$bForce = true;
$strType = RECOVERY_TYPE_NAME;
$strTarget = $strNameTarget;
$bTargetExclusive = undef;
$strTargetAction = undef;
$strTargetTimeline = undef;
$oRecoveryHashRef = undef;
$strComment = undef;
$iExpectedExitStatus = undef;
&log(INFO, " testing recovery type = ${strType}");
$oHostDbMaster->clusterStop();
$oHostDbMaster->restore(
OPTION_DEFAULT_RESTORE_SET, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive,
$strTargetAction, $strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest('select message from test', $strNameMessage);
}
# Restore (restore type = default, timeline = 3)
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra && $oHostDbMaster->pgVersion() >= PG_VERSION_84)
{
$bDelta = true;
$bForce = false;
$strType = RECOVERY_TYPE_DEFAULT;
$strTarget = undef;
$bTargetExclusive = undef;
$strTargetAction = undef;
$strTargetTimeline = 4;
$oRecoveryHashRef = $oHostDbMaster->pgVersion() >= PG_VERSION_90 ? {'standby-mode' => 'on'} : undef;
$strComment = undef;
$iExpectedExitStatus = undef;
&log(INFO, " testing recovery type = ${strType}");
$oHostDbMaster->clusterStop();
$oHostDbMaster->restore(
$strIncrBackup, undef, undef, $bDelta, $bForce, $strType, $strTarget, $bTargetExclusive, $strTargetAction,
$strTargetTimeline, $oRecoveryHashRef, $strComment, $iExpectedExitStatus);
$oHostDbMaster->clusterStart({bHotStandby => true});
$oHostDbMaster->sqlSelectOneTest('select message from test', $strTimelineMessage, {iTimeout => 120});
}
# Incr backup - make sure a --no-online backup fails
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra)
{
$strType = BACKUP_TYPE_INCR;
$oHostBackup->backup(
$strType, 'fail on --no-' . OPTION_ONLINE,
{iExpectedExitStatus => ERROR_POSTMASTER_RUNNING, strOptionalParam => '--no-' . OPTION_ONLINE});
}
# Incr backup - allow --no-online backup to succeed with --force
#---------------------------------------------------------------------------------------------------------------------------
if ($bTestExtra)
{
$strType = BACKUP_TYPE_INCR;
$oHostBackup->backup(
$strType, 'succeed on --no-' . OPTION_ONLINE . ' with --' . OPTION_FORCE,
{strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
}
# Stop clusters to catch any errors in the postgres log
#---------------------------------------------------------------------------------------------------------------------------
$oHostDbMaster->clusterStop({bImmediate => true});
if (defined($oHostDbStandby))
{
$oHostDbStandby->clusterStop({bImmediate => true});
}
}
}
}
}
}
}
1;

View File

@@ -0,0 +1,136 @@
####################################################################################################################################
# BackupStanzaCreateTest.pm - Tests for stanza-create command
####################################################################################################################################
package pgBackRestTest::Backup::BackupStanzaCreateTest;
use parent 'pgBackRestTest::Backup::BackupCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use pgBackRest::ArchiveInfo;
use pgBackRest::BackupInfo;
use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Backup::BackupCommonTest;
use pgBackRestTest::Backup::ExpireCommonTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin($bRemote ? "remote" : "local")) {next}
# Create hosts, file object, and config
my ($oHostDbMaster, $oHostDbStandby, $oHostBackup, $oFile) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote});
# Create the stanza
$oHostBackup->stanzaCreate('fail on missing control file', {iExpectedExitStatus => ERROR_FILE_OPEN,
strOptionalParam => '--no-' . OPTION_ONLINE});
# Create the test path for pg_control
filePathCreate(($oHostDbMaster->dbBasePath() . '/' . DB_PATH_GLOBAL), undef, false, true);
# Copy pg_control for stanza-create
executeTest(
'cp ' . $self->dataPath() . '/backup.pg_control_' . WAL_VERSION_94 . '.bin ' . $oHostDbMaster->dbBasePath() . '/' .
DB_FILE_PGCONTROL);
$oHostBackup->stanzaCreate('successfully create the stanza', {strOptionalParam => '--no-' . OPTION_ONLINE});
# Create the xlog path
my $strXlogPath = $oHostDbMaster->dbBasePath() . '/pg_xlog';
filePathCreate($strXlogPath, undef, false, true);
# Generate WAL then push to get valid archive data in the archive directory
my ($strArchiveFile, $strSourceFile) = $self->archiveGenerate($oFile, $strXlogPath, 1, 1, WAL_VERSION_94);
my $strCommand = $oHostDbMaster->backrestExe() . ' --config=' . $oHostDbMaster->backrestConfig() .
' --no-fork --stanza=db archive-push';
$oHostDbMaster->executeSimple($strCommand . " ${strSourceFile}", {oLogTest => $self->expect()});
# With data existing in the archive dir, remove the info file and confirm failure
$oHostBackup->executeSimple('rm ' . $oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
$oHostBackup->stanzaCreate('fail on archive info file missing from non-empty dir',
{iExpectedExitStatus => ERROR_PATH_NOT_EMPTY, strOptionalParam => '--no-' . OPTION_ONLINE});
# Change the permissions of the archive file so it cannot be read
executeTest('sudo chmod 220 ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . '-1/' .
substr($strArchiveFile, 0, 16) . "/*.gz");
# Force creation of the info file but fail on gunzip
$oHostBackup->stanzaCreate('gunzip fail on forced stanza-create',
{iExpectedExitStatus => ERROR_FILE_OPEN, strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
# Change permissions back and force creation of archive info from the gz file
executeTest('sudo chmod 640 ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . '-1/' .
substr($strArchiveFile, 0, 16) . "/*.gz");
$oHostBackup->stanzaCreate('force create archive.info from gz file',
{strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
# Run force again to ensure the format is still valid
$oHostBackup->stanzaCreate('repeat force', {strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
# Change the database version by copying a new pg_control file
executeTest('sudo rm ' . $oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL);
executeTest('cp ' . $self->dataPath() . '/backup.pg_control_93.bin ' . $oHostDbMaster->dbBasePath() . '/' .
DB_FILE_PGCONTROL);
# Remove the archive info file
$oHostBackup->executeSimple('rm ' . $oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
# Run stanza-create with --force
$oHostBackup->stanzaCreate('test force fails for database mismatch with directory',
{iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH, strOptionalParam => '--no-' . OPTION_ONLINE .
' --' . OPTION_FORCE});
# Restore pg_control
executeTest('sudo rm ' . $oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL);
executeTest('cp ' . $self->dataPath() . '/backup.pg_control_' . WAL_VERSION_94 . '.bin ' .
$oHostDbMaster->dbBasePath() . '/' . DB_FILE_PGCONTROL);
# Unzip the archive file and recreate the archive.info file from it
executeTest('sudo gunzip ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . '-1/' .
substr($strArchiveFile, 0, 16) . "/${strArchiveFile}-1e34fa1c833090d94b9bb14f2a8d3153dca6ea27.gz");
$oHostBackup->stanzaCreate('force create archive.info from uncompressed file',
{strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
# Remove the uncompressed WAL archive file and archive.info
executeTest('sudo rm ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . '-1/' .
substr($strArchiveFile, 0, 16) . "/${strArchiveFile}-1e34fa1c833090d94b9bb14f2a8d3153dca6ea27");
$oHostBackup->executeSimple('rm ' . $oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
$oHostBackup->stanzaCreate('force with missing WAL archive file',
{strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
# Remove the WAL archive directory
executeTest('sudo rm -rf ' . $oHostBackup->repoPath() . '/archive/' . $self->stanza() . '/' . PG_VERSION_94 . '-1/' .
substr($strArchiveFile, 0, 16));
$oHostBackup->executeSimple('rm ' . $oFile->pathGet(PATH_BACKUP_ARCHIVE, ARCHIVE_INFO_FILE));
$oHostBackup->stanzaCreate('force with missing WAL archive directory',
{strOptionalParam => '--no-' . OPTION_ONLINE . ' --' . OPTION_FORCE});
}
}
1;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
####################################################################################################################################
# ExpireCommonTest.pm - Common code for expire tests
####################################################################################################################################
package pgBackRestTest::Backup::Common::ExpireCommonTest;
package pgBackRestTest::Backup::ExpireCommonTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
@@ -21,11 +22,9 @@ use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRest::Version;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::FileTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::CommonTest;
####################################################################################################################################
# new
@@ -42,6 +41,7 @@ sub new
(
my $strOperation,
$self->{oHostBackup},
$self->{strBackRestExe},
$self->{oFile},
$self->{oLogTest}
) =
@@ -49,6 +49,7 @@ sub new
(
__PACKAGE__ . '->new', \@_,
{name => 'oHostBackup', trace => true},
{name => 'strBackRestExe', trace => true},
{name => 'oFile', trace => true},
{name => 'oLogTest', required => false, trace => true}
);
@@ -403,8 +404,7 @@ sub process
undef($$oStanza{strBackupDescription});
my $oHostGroup = hostGroupGet();
my $strCommand = $oHostGroup->paramGet(HOST_PARAM_BACKREST_EXE) .
my $strCommand = $self->{strBackRestExe} .
' --' . OPTION_CONFIG . '="' . $self->{oHostBackup}->backrestConfig() . '"' .
' --' . OPTION_STANZA . '=' . $strStanza .
' --' . OPTION_LOG_LEVEL_CONSOLE . '=' . lc(DETAIL);

View File

@@ -45,6 +45,7 @@ use constant TEST_USER_ID => $UID;
use constant BACKREST_GROUP => POSTGRES_GROUP;
use constant BACKREST_GROUP_ID => POSTGRES_GROUP_ID;
use constant BACKREST_USER => 'backrest';
push @EXPORT, qw(BACKREST_USER);
use constant BACKREST_USER_ID => getpwnam(BACKREST_USER) . '';
####################################################################################################################################

View File

@@ -23,8 +23,12 @@ use constant TESTDEF_MODULE => 'module';
use constant TESTDEF_MODULE_NAME => 'name';
push @EXPORT, qw(TESTDEF_MODULE_NAME);
use constant TESTDEF_EXPECT => 'expect';
push @EXPORT, qw(TESTDEF_EXPECT);
use constant TESTDEF_TEST => 'test';
push @EXPORT, qw(TESTDEF_TEST);
use constant TESTDEF_TEST_INDIVIDUAL => 'individual';
push @EXPORT, qw(TESTDEF_TEST_INDIVIDUAL);
use constant TESTDEF_TEST_NAME => 'name';
push @EXPORT, qw(TESTDEF_TEST_NAME);
use constant TESTDEF_TEST_TOTAL => 'total';
@@ -47,11 +51,14 @@ my $oTestDef =
{
&TESTDEF_MODULE_NAME => 'help',
&TESTDEF_TEST_CONTAINER => true,
&TESTDEF_EXPECT => true,
&TESTDEF_TEST =>
[
{
&TESTDEF_TEST_NAME => 'help'
&TESTDEF_TEST_NAME => 'help',
&TESTDEF_TEST_TOTAL => 1,
&TESTDEF_TEST_INDIVIDUAL => false,
}
]
},
@@ -63,10 +70,14 @@ my $oTestDef =
&TESTDEF_TEST =>
[
{
&TESTDEF_TEST_NAME => 'option'
&TESTDEF_TEST_NAME => 'option',
&TESTDEF_TEST_TOTAL => 34,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'config'
&TESTDEF_TEST_NAME => 'config',
&TESTDEF_TEST_TOTAL => 25,
&TESTDEF_TEST_INDIVIDUAL => false,
}
]
},
@@ -78,34 +89,54 @@ my $oTestDef =
&TESTDEF_TEST =>
[
{
&TESTDEF_TEST_NAME => 'path_create'
&TESTDEF_TEST_NAME => 'path-create',
&TESTDEF_TEST_TOTAL => 8,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'move'
&TESTDEF_TEST_NAME => 'move',
&TESTDEF_TEST_TOTAL => 24,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'compress'
&TESTDEF_TEST_NAME => 'compress',
&TESTDEF_TEST_TOTAL => 4,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'wait'
&TESTDEF_TEST_NAME => 'wait',
&TESTDEF_TEST_TOTAL => 2,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'manifest'
&TESTDEF_TEST_NAME => 'manifest',
&TESTDEF_TEST_TOTAL => 8,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'list'
&TESTDEF_TEST_NAME => 'list',
&TESTDEF_TEST_TOTAL => 72,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'remove'
&TESTDEF_TEST_NAME => 'remove',
&TESTDEF_TEST_TOTAL => 32,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'hash'
&TESTDEF_TEST_NAME => 'hash',
&TESTDEF_TEST_TOTAL => 16,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'exists'
&TESTDEF_TEST_NAME => 'exists',
&TESTDEF_TEST_TOTAL => 6,
&TESTDEF_TEST_INDIVIDUAL => false,
},
{
&TESTDEF_TEST_NAME => 'copy'
&TESTDEF_TEST_NAME => 'copy',
&TESTDEF_TEST_TOTAL => 144,
&TESTDEF_TEST_INDIVIDUAL => false,
}
]
},
@@ -113,6 +144,7 @@ my $oTestDef =
{
&TESTDEF_MODULE_NAME => 'backup',
&TESTDEF_TEST_CONTAINER => false,
&TESTDEF_EXPECT => true,
&TESTDEF_TEST =>
[
@@ -162,4 +194,46 @@ sub testDefGet
push @EXPORT, qw(testDefGet);
####################################################################################################################################
# testDefModuleGet
####################################################################################################################################
sub testDefModuleGet
{
my $strModule = shift;
# Find the module
foreach my $hModule (@{$oTestDef->{&TESTDEF_MODULE}})
{
if ($hModule->{&TESTDEF_MODULE_NAME} eq $strModule)
{
return $hModule;
}
}
confess &log(ASSERT, "unable to find module ${strModule}");
}
push @EXPORT, qw(testDefModuleGet);
####################################################################################################################################
# testDefModuleTestGet
####################################################################################################################################
sub testDefModuleTestGet
{
my $hModule = shift;
my $strModuleTest = shift;
foreach my $hModuleTest (@{$hModule->{&TESTDEF_TEST}})
{
if ($hModuleTest->{&TESTDEF_TEST_NAME} eq $strModuleTest)
{
return $hModuleTest;
}
}
confess &log(ASSERT, "unable to find module test ${strModuleTest}");
}
push @EXPORT, qw(testDefModuleTestGet);
1;

View File

@@ -32,9 +32,9 @@ use pgBackRest::Manifest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::LogTest;
use pgBackRestTest::Common::VmTest;
use pgBackRestTest::Backup::Common::HostBackupTest;
use pgBackRestTest::Backup::Common::HostDbCommonTest;
use pgBackRestTest::Backup::Common::HostDbTest;
use pgBackRestTest::Common::Host::HostBackupTest;
use pgBackRestTest::Common::Host::HostDbCommonTest;
use pgBackRestTest::Common::Host::HostDbTest;
####################################################################################################################################
# testLinkCreate

View File

@@ -1,8 +1,8 @@
####################################################################################################################################
# HostBackupTest.pm - Backup host
####################################################################################################################################
package pgBackRestTest::Backup::Common::HostBackupTest;
use parent 'pgBackRestTest::Backup::Common::HostBaseTest';
package pgBackRestTest::Common::Host::HostBackupTest;
use parent 'pgBackRestTest::Common::Host::HostBaseTest';
####################################################################################################################################
# Perl includes
@@ -25,58 +25,24 @@ use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRest::Version;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::CommonTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# Host constants
####################################################################################################################################
use constant HOST_BACKUP_USER => 'backup-user';
push @EXPORT, qw(HOST_BACKUP_USER);
####################################################################################################################################
# Host parameters
####################################################################################################################################
use constant HOST_PARAM_BACKREST_CONFIG => 'backrest-config';
push @EXPORT, qw(HOST_PARAM_BACKREST_CONFIG);
use constant HOST_PARAM_LOCK_PATH => 'lock-path';
push @EXPORT, qw(HOST_PARAM_LOCK_PATH);
use constant HOST_PARAM_LOG_PATH => 'log-path';
push @EXPORT, qw(HOST_PARAM_LOG_PATH);
use constant HOST_PARAM_REPO_PATH => 'repo-path';
push @EXPORT, qw(HOST_PARAM_REPO_PATH);
use constant HOST_PARAM_STANZA => 'stanza';
push @EXPORT, qw(HOST_PARAM_STANZA);
use constant HOST_PARAM_PROCESS_MAX => 'process-max';
push @EXPORT, qw(HOST_PARAM_PROCESS_MAX);
####################################################################################################################################
# Host paths
# Host defaults
####################################################################################################################################
use constant HOST_PATH_LOCK => 'lock';
push @EXPORT, qw(HOST_PATH_LOCK);
use constant HOST_PATH_LOG => 'log';
push @EXPORT, qw(HOST_PATH_LOG);
use constant HOST_PATH_REPO => 'repo';
push @EXPORT, qw(HOST_PATH_REPO);
####################################################################################################################################
# Backup Defaults
####################################################################################################################################
use constant HOST_STANZA => 'db';
push @EXPORT, qw(HOST_STANZA);
use constant HOST_PROTOCOL_TIMEOUT => 10;
push @EXPORT, qw(HOST_PROTOCOL_TIMEOUT);
####################################################################################################################################
# Cached data sections
####################################################################################################################################
use constant SECTION_FILE_NAME => 'strFileName';
push @EXPORT, qw(SECTION_FILE_NAME);
####################################################################################################################################
# new
####################################################################################################################################
@@ -98,42 +64,37 @@ sub new
# If params are not passed
my $oHostGroup = hostGroupGet();
my ($strName, $strImage, $strUser, $strVm);
my ($strName, $strImage, $strUser);
if (!defined($$oParam{strName}) || $$oParam{strName} eq HOST_BACKUP)
{
$strName = HOST_BACKUP;
$strImage = containerNamespace() . '/' . $oHostGroup->paramGet(HOST_PARAM_VM) . '-backup-test-pre';
$strUser = $oHostGroup->paramGet(HOST_BACKUP_USER);
$strVm = $oHostGroup->paramGet(HOST_PARAM_VM);
$strImage = containerNamespace() . '/' . testRunGet()->vm() . '-backup-test-pre';
$strUser = testRunGet()->backrestUser();
}
else
{
$strName = $$oParam{strName};
$strImage = $$oParam{strImage};
$strUser = $$oParam{strUser};
$strVm = $$oParam{strVm};
$strUser = testRunGet()->pgUser();
}
# Create the host
my $self = $class->SUPER::new($strName, {strImage => $strImage, strUser => $strUser, strVm => $strVm});
my $self = $class->SUPER::new($strName, {strImage => $strImage, strUser => $strUser});
bless $self, $class;
# Set parameters
$self->paramSet(
HOST_PARAM_REPO_PATH, $oHostGroup->paramGet(HOST_PARAM_TEST_PATH) . "/$$oParam{strBackupDestination}/" . HOST_PATH_REPO);
$self->{strRepoPath} = $self->testRunGet()->testPath() . "/$$oParam{strBackupDestination}/" . HOST_PATH_REPO;
if ($$oParam{strBackupDestination} eq $self->nameGet())
{
$self->paramSet(HOST_PARAM_LOG_PATH, $self->repoPath() . '/' . HOST_PATH_LOG);
$self->paramSet(HOST_PARAM_LOCK_PATH, $self->repoPath() . '/' . HOST_PATH_LOCK);
$self->{strLogPath} = $self->repoPath() . '/' . HOST_PATH_LOG;
$self->{strLockPath} = $self->repoPath() . '/' . HOST_PATH_LOCK;
filePathCreate($self->repoPath(), '0770');
}
$self->paramSet(HOST_PARAM_BACKREST_CONFIG, $self->testPath() . '/' . BACKREST_CONF);
$self->paramSet(HOST_PARAM_BACKREST_EXE, $oHostGroup->paramGet(HOST_PARAM_BACKREST_EXE));
$self->paramSet(HOST_PARAM_STANZA, HOST_STANZA);
$self->paramSet(HOST_PARAM_PROCESS_MAX, $oHostGroup->paramGet(HOST_PARAM_PROCESS_MAX));
$self->{strBackRestConfig} = $self->testPath() . '/' . BACKREST_CONF;
# Set LogTest object
$self->{oLogTest} = $$oParam{oLogTest};
@@ -783,18 +744,17 @@ sub stanzaCreate
bLogOutput => $self->synthetic()});
# If the info file was created, then add it to the expect log
if (defined($self->{oLogTest}) && $self->synthetic())
{
if (fileExists($self->repoPath() . '/backup/' . $self->stanza() . '/backup.info'))
if (defined($self->{oLogTest}) && $self->synthetic() &&
fileExists($self->repoPath() . '/backup/' . $self->stanza() . '/backup.info'))
{
$self->{oLogTest}->supplementalAdd($self->repoPath() . '/backup/' . $self->stanza() . '/backup.info');
}
if (fileExists($self->repoPath() . '/archive/' . $self->stanza() . '/archive.info'))
if (defined($self->{oLogTest}) && $self->synthetic() &&
fileExists($self->repoPath() . '/archive/' . $self->stanza() . '/archive.info'))
{
$self->{oLogTest}->supplementalAdd($self->repoPath() . '/archive/' . $self->stanza() . '/archive.info');
}
}
# Return from function and log return values if any
return logDebugReturn($strOperation);
@@ -956,7 +916,7 @@ sub configCreate
# Port can't be configured for a synthetic host
if (!$self->synthetic())
{
$oParamHash{$strStanza}{optionIndex(OPTION_DB_PORT, 1, $bForce)} = $oHostDb1->dbPort();
$oParamHash{$strStanza}{optionIndex(OPTION_DB_PORT, 1, $bForce)} = $oHostDb1->pgPort();
}
}
@@ -973,7 +933,7 @@ sub configCreate
# Only test explicit ports on the backup server. This is so locally configured ports are also tested.
if (!$self->synthetic() && $self->nameTest(HOST_BACKUP))
{
$oParamHash{$strStanza}{optionIndex(OPTION_DB_PORT, 2)} = $oHostDb2->dbPort();
$oParamHash{$strStanza}{optionIndex(OPTION_DB_PORT, 2)} = $oHostDb2->pgPort();
}
}
}
@@ -985,8 +945,8 @@ sub configCreate
if (!$self->synthetic())
{
$oParamHash{$strStanza}{&OPTION_DB_SOCKET_PATH} = $self->dbSocketPath();
$oParamHash{$strStanza}{&OPTION_DB_PORT} = $self->dbPort();
$oParamHash{$strStanza}{&OPTION_DB_SOCKET_PATH} = $self->pgSocketPath();
$oParamHash{$strStanza}{&OPTION_DB_PORT} = $self->pgPort();
}
if ($bArchiveAsync)
@@ -1078,18 +1038,8 @@ sub manifestMunge
}
}
# Remove the old checksum
delete($oManifest{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});
my $oSHA = Digest::SHA->new('sha1');
my $oJSON = JSON::PP->new()->canonical()->allow_nonref();
$oSHA->add($oJSON->encode(\%oManifest));
# Set the new checksum
$oManifest{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = $oSHA->hexdigest();
# Resave the manifest
iniSave($self->{oFile}->pathGet(PATH_BACKUP_CLUSTER, $strManifestFile), \%oManifest);
$self->iniSaveChecksum($self->{oFile}->pathGet(PATH_BACKUP_CLUSTER, $strManifestFile), \%oManifest, true);
}
####################################################################################################################################
@@ -1139,7 +1089,7 @@ sub infoMunge
}
# Save the munged data to the file
testIniSave($strFileName, \%{$hContent}, true);
$self->iniSaveChecksum($strFileName, \%{$hContent}, true);
# Return from function and log return values if any
return logDebugReturn($strOperation);
@@ -1184,22 +1134,49 @@ sub infoRestore
return logDebugReturn($strOperation);
}
####################################################################################################################################
# iniSaveChecksum
#
# Save an ini file an optionall recalculate the checksum so it's valid.
####################################################################################################################################
sub iniSaveChecksum
{
my $self = shift;
my $strFileName = shift;
my $oIniRef = shift;
my $bChecksum = shift;
# Calculate a new checksum if requested
if (defined($bChecksum) && $bChecksum)
{
delete($$oIniRef{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});
my $oSHA = Digest::SHA->new('sha1');
my $oJSON = JSON::PP->new()->canonical()->allow_nonref();
$oSHA->add($oJSON->encode($oIniRef));
$$oIniRef{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = $oSHA->hexdigest();
}
iniSave($strFileName, $oIniRef);
}
####################################################################################################################################
# Getters
####################################################################################################################################
sub backrestConfig {return shift->paramGet(HOST_PARAM_BACKREST_CONFIG);}
sub backupDestination {return shift->{strBackupDestination};}
sub backrestExe {return shift->paramGet(HOST_PARAM_BACKREST_EXE);}
sub hardLink {return shift->{bHardLink};}
sub isHostBackup {my $self = shift; return $self->backupDestination() eq $self->nameGet();}
sub isHostDbMaster {return shift->nameGet() eq HOST_DB_MASTER;}
sub isHostDbStandby {return shift->nameGet() eq HOST_DB_STANDBY;}
sub isHostDb {my $self = shift; return $self->isHostDbMaster() || $self->isHostDbStandby();}
sub lockPath {return shift->paramGet(HOST_PARAM_LOCK_PATH);}
sub logPath {return shift->paramGet(HOST_PARAM_LOG_PATH);}
sub repoPath {return shift->paramGet(HOST_PARAM_REPO_PATH);}
sub stanza {return shift->paramGet(HOST_PARAM_STANZA);}
sub processMax {return shift->paramGet(HOST_PARAM_PROCESS_MAX);}
sub synthetic {return shift->{bSynthetic};}
sub backrestConfig {return shift->{strBackRestConfig}}
sub backupDestination {return shift->{strBackupDestination}}
sub backrestExe {return testRunGet()->backrestExe()}
sub hardLink {return shift->{bHardLink}}
sub isHostBackup {my $self = shift; return $self->backupDestination() eq $self->nameGet()}
sub isHostDbMaster {return shift->nameGet() eq HOST_DB_MASTER}
sub isHostDbStandby {return shift->nameGet() eq HOST_DB_STANDBY}
sub isHostDb {my $self = shift; return $self->isHostDbMaster() || $self->isHostDbStandby()}
sub lockPath {return shift->{strLockPath}}
sub logPath {return shift->{strLogPath}}
sub repoPath {return shift->{strRepoPath}}
sub stanza {return testRunGet()->stanza()}
sub processMax {return testRunGet()->processMax()}
sub synthetic {return shift->{bSynthetic}}
1;

View File

@@ -1,7 +1,7 @@
####################################################################################################################################
# HostBackupTest.pm - Backup host
####################################################################################################################################
package pgBackRestTest::Backup::Common::HostBaseTest;
package pgBackRestTest::Common::Host::HostBaseTest;
use parent 'pgBackRestTest::Common::HostTest';
####################################################################################################################################
@@ -21,7 +21,7 @@ use pgBackRest::FileCommon;
use pgBackRest::Version;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Common::VmTest;
####################################################################################################################################
@@ -36,18 +36,6 @@ use constant HOST_DB_STANDBY => 'db-stand
use constant HOST_BACKUP => 'backup';
push @EXPORT, qw(HOST_BACKUP);
####################################################################################################################################
# Host parameters
####################################################################################################################################
use constant HOST_PARAM_BACKREST_EXE => 'backrest-exe';
push @EXPORT, qw(HOST_PARAM_BACKREST_EXE);
use constant HOST_PARAM_VM_ID => 'vm-id';
push @EXPORT, qw(HOST_PARAM_VM_ID);
use constant HOST_PARAM_TEST_PATH => 'test-path';
push @EXPORT, qw(HOST_PARAM_TEST_PATH);
use constant HOST_PARAM_VM => 'vm';
push @EXPORT, qw(HOST_PARAM_VM);
####################################################################################################################################
# new
####################################################################################################################################
@@ -69,40 +57,37 @@ sub new
{name => 'oParam', required => false, trace => true},
);
# Create the test path
my $oHostGroup = hostGroupGet();
my $strTestPath = $oHostGroup->paramGet(HOST_PARAM_TEST_PATH) . ($strName eq HOST_BASE ? '' : "/${strName}");
my $strTestPath = testRunGet()->testPath() . ($strName eq HOST_BASE ? '' : "/${strName}");
filePathCreate($strTestPath, '0770');
# Create the host
my $strProjectPath = dirname(dirname(abs_path($0)));
my $strContainer = 'test-' . $oHostGroup->paramGet(HOST_PARAM_VM_ID) . "-$strName";
my $strContainer = 'test-' . testRunGet()->vmId() . "-$strName";
my $self = $class->SUPER::new(
$strName, $strContainer, $$oParam{strImage}, $$oParam{strUser}, $$oParam{strVm},
$strName, $strContainer, $$oParam{strImage}, $$oParam{strUser}, testRunGet()->vm(),
["${strProjectPath}:${strProjectPath}", "${strTestPath}:${strTestPath}"]);
bless $self, $class;
# Set parameters
$self->paramSet(HOST_PARAM_TEST_PATH, $strTestPath);
# Set test path
$self->{strTestPath} = $strTestPath;
# Set permissions on the test path
$self->executeSimple('chown -R ' . $self->userGet() . ':'. POSTGRES_GROUP . ' ' . $self->testPath(), undef, 'root');
# Install Perl C Library
my $oVm = vmGet();
my $strBuildPath = dirname(dirname($oHostGroup->paramGet(HOST_PARAM_BACKREST_EXE))) . "/test/.vagrant/libc/$self->{strOS}";
my $strBuildPath = testRunGet()->basePath() . "/test/.vagrant/libc/$self->{strOS}";
my $strPerlAutoPath = $$oVm{$self->{strOS}}{&VMDEF_PERL_ARCH_PATH} . '/auto/pgBackRest/LibC';
my $strPerlModulePath = $$oVm{$self->{strOS}}{&VMDEF_PERL_ARCH_PATH} . '/pgBackRest';
$self->executeSimple(
"bash -c '" .
"mkdir -p -m 755 ${strPerlAutoPath} && " .
# "cp ${strBuildPath}/blib/arch/auto/pgBackRest/LibC/LibC.bs ${strPerlAutoPath} && " .
"cp ${strBuildPath}/blib/arch/auto/pgBackRest/LibC/LibC.so ${strPerlAutoPath} && " .
"cp ${strBuildPath}/blib/lib/auto/pgBackRest/LibC/autosplit.ix ${strPerlAutoPath} && " .
"mkdir -p -m 755 ${strPerlModulePath} && " .
"cp ${strBuildPath}/blib/lib/pgBackRest/LibC.pm ${strPerlModulePath}'",
"cp ${strBuildPath}/blib/lib/pgBackRest/LibC.pm ${strPerlModulePath}",
undef, 'root');
# Return from function and log return values if any
@@ -116,6 +101,6 @@ sub new
####################################################################################################################################
# Getters
####################################################################################################################################
sub testPath {return shift->paramGet(HOST_PARAM_TEST_PATH);}
sub testPath {return shift->{strTestPath}}
1;

View File

@@ -1,8 +1,8 @@
####################################################################################################################################
# HostDbTest.pm - Database host
####################################################################################################################################
package pgBackRestTest::Backup::Common::HostDbCommonTest;
use parent 'pgBackRestTest::Backup::Common::HostBackupTest';
package pgBackRestTest::Common::Host::HostDbCommonTest;
use parent 'pgBackRestTest::Common::Host::HostBackupTest';
####################################################################################################################################
# Perl includes
@@ -29,38 +29,17 @@ use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRest::Version;
use pgBackRestTest::Backup::Common::HostBackupTest;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Common::Host::HostBackupTest;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::HostGroupTest;
####################################################################################################################################
# Host constants
####################################################################################################################################
use constant HOST_DB_USER => 'db-user';
push @EXPORT, qw(HOST_DB_USER);
####################################################################################################################################
# Host parameters
####################################################################################################################################
use constant HOST_PARAM_DB_BASE_PATH => 'db-base-path';
push @EXPORT, qw(HOST_PARAM_DB_BASE_PATH);
use constant HOST_PARAM_DB_PATH => 'db-path';
push @EXPORT, qw(HOST_PARAM_DB_PATH);
use constant HOST_PARAM_SPOOL_PATH => 'spool-path';
push @EXPORT, qw(HOST_PARAM_SPOOL_PATH);
use constant HOST_PARAM_TABLESPACE_PATH => 'tablespace-path';
push @EXPORT, qw(HOST_PARAM_TABLESPACE_PATH);
####################################################################################################################################
# Host paths
# Host defaults
####################################################################################################################################
use constant HOST_PATH_SPOOL => 'spool';
push @EXPORT, qw(HOST_PATH_SPOOL);
use constant HOST_PATH_DB => 'db';
push @EXPORT, qw(HOST_PATH_DB);
use constant HOST_PATH_DB_BASE => 'base';
push @EXPORT, qw(HOST_PATH_DB_BASE);
####################################################################################################################################
# new
@@ -91,8 +70,6 @@ sub new
{
strName => $bStandby ? HOST_DB_STANDBY : HOST_DB_MASTER,
strImage => $$oParam{strImage},
strUser => $oHostGroup->paramGet(HOST_DB_USER),
strVm => $oHostGroup->paramGet(HOST_PARAM_VM),
strBackupDestination => $$oParam{strBackupDestination},
oLogTest => $$oParam{oLogTest},
bSynthetic => $$oParam{bSynthetic},
@@ -102,23 +79,23 @@ sub new
# Set parameters
$self->{bStandby} = $bStandby;
$self->paramSet(HOST_PARAM_DB_PATH, $self->testPath() . '/' . HOST_PATH_DB);
$self->paramSet(HOST_PARAM_DB_BASE_PATH, $self->dbPath() . '/' . HOST_PATH_DB_BASE);
$self->paramSet(HOST_PARAM_TABLESPACE_PATH, $self->dbPath() . '/tablespace');
$self->{strDbPath} = $self->testPath() . '/' . HOST_PATH_DB;
$self->{strDbBasePath} = $self->dbPath() . '/' . HOST_PATH_DB_BASE;
$self->{strTablespacePath} = $self->dbPath() . '/tablespace';
filePathCreate($self->dbBasePath(), undef, undef, true);
if ($$oParam{strBackupDestination} ne $self->nameGet())
{
$self->paramSet(HOST_PARAM_SPOOL_PATH, $self->testPath() . '/' . HOST_PATH_SPOOL);
$self->paramSet(HOST_PARAM_LOG_PATH, $self->spoolPath() . '/' . HOST_PATH_LOG);
$self->paramSet(HOST_PARAM_LOCK_PATH, $self->spoolPath() . '/' . HOST_PATH_LOCK);
$self->{strSpoolPath} = $self->testPath() . '/' . HOST_PATH_SPOOL;
$self->{strLogPath} = $self->spoolPath() . '/' . HOST_PATH_LOG;
$self->{strLockPath} = $self->spoolPath() . '/' . HOST_PATH_LOCK;
filePathCreate($self->spoolPath());
}
else
{
$self->paramSet(HOST_PARAM_SPOOL_PATH, $self->repoPath());
$self->{strSpoolPath} = $self->repoPath();
}
# Initialize linkRemap Hashes
@@ -495,10 +472,6 @@ sub restoreCompare
my $strTestPath = $self->testPath();
# Load module dynamically
require pgBackRestTest::CommonTest;
pgBackRestTest::CommonTest->import();
# Get the backup host
my $oHostGroup = hostGroupGet();
my $oHostBackup = defined($oHostGroup->hostGet(HOST_BACKUP, true)) ? $oHostGroup->hostGet(HOST_BACKUP) : $self;
@@ -765,17 +738,17 @@ sub restoreCompare
####################################################################################################################################
# Getters
####################################################################################################################################
sub dbPath {return shift->paramGet(HOST_PARAM_DB_PATH);}
sub dbPath {return shift->{strDbPath};}
sub dbBasePath
{
my $self = shift;
my $iIndex = shift;
return $self->paramGet(HOST_PARAM_DB_BASE_PATH) . (defined($iIndex) ? "-${iIndex}" : '');
return $self->{strDbBasePath} . (defined($iIndex) ? "-${iIndex}" : '');
}
sub spoolPath {return shift->paramGet(HOST_PARAM_SPOOL_PATH);}
sub spoolPath {return shift->{strSpoolPath}}
sub standby {return shift->{bStandby}}
sub tablespacePath
@@ -785,7 +758,7 @@ sub tablespacePath
my $iIndex = shift;
return
$self->paramGet(HOST_PARAM_TABLESPACE_PATH) .
$self->{strTablespacePath} .
(defined($iTablespace) ? "/ts${iTablespace}" .
(defined($iIndex) ? "-${iIndex}" : '') : '');
}

View File

@@ -1,8 +1,8 @@
####################################################################################################################################
# HostDbTest.pm - Database host
####################################################################################################################################
package pgBackRestTest::Backup::Common::HostDbSyntheticTest;
use parent 'pgBackRestTest::Backup::Common::HostDbCommonTest';
package pgBackRestTest::Common::Host::HostDbSyntheticTest;
use parent 'pgBackRestTest::Common::Host::HostDbCommonTest';
####################################################################################################################################
# Perl includes
@@ -27,13 +27,12 @@ use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRest::Version;
use pgBackRestTest::Backup::Common::HostBackupTest;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Backup::Common::HostDbCommonTest;
use pgBackRestTest::Common::Host::HostBackupTest;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::Host::HostDbCommonTest;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::FileTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::CommonTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# new
@@ -54,12 +53,9 @@ sub new
{name => 'oParam', required => false, trace => true},
);
# Get host group
my $oHostGroup = hostGroupGet();
my $self = $class->SUPER::new(
{
strImage => containerNamespace() . '/' . $oHostGroup->paramGet(HOST_PARAM_VM) . "-db-test-pre",
strImage => containerNamespace() . '/' . testRunGet()->vm() . "-db-test-pre",
strBackupDestination => $$oParam{strBackupDestination},
oLogTest => $$oParam{oLogTest},
bSynthetic => true,

View File

@@ -1,8 +1,8 @@
####################################################################################################################################
# HostDbTest.pm - Database host
####################################################################################################################################
package pgBackRestTest::Backup::Common::HostDbTest;
use parent 'pgBackRestTest::Backup::Common::HostDbCommonTest';
package pgBackRestTest::Common::Host::HostDbTest;
use parent 'pgBackRestTest::Common::Host::HostDbCommonTest';
####################################################################################################################################
# Perl includes
@@ -25,37 +25,26 @@ use pgBackRest::FileCommon;
use pgBackRest::Manifest;
use pgBackRest::Version;
use pgBackRestTest::Backup::Common::HostBackupTest;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Backup::Common::HostDbCommonTest;
use pgBackRestTest::Common::Host::HostBackupTest;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::Host::HostDbCommonTest;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# Host parameters
####################################################################################################################################
use constant HOST_PARAM_DB_BIN_PATH => 'db-bin-path';
push @EXPORT, qw(HOST_PARAM_DB_BIN_PATH);
use constant HOST_PARAM_DB_LOG_FILE => 'db-log-file';
push @EXPORT, qw(HOST_PARAM_LOG_DB_FILE);
use constant HOST_PARAM_DB_LOG_PATH => 'db-log-path';
push @EXPORT, qw(HOST_PARAM_LOG_DB_PATH);
use constant HOST_PARAM_DB_PORT => 'db-port';
push @EXPORT, qw(HOST_PARAM_DB_PORT);
use constant HOST_PARAM_DB_SOCKET_PATH => 'db-socket-path';
push @EXPORT, qw(HOST_PARAM_DB_SOCKET_PATH);
use constant HOST_PARAM_DB_VERSION => 'db-version';
push @EXPORT, qw(HOST_PARAM_DB_VERSION);
####################################################################################################################################
# Db defaults
####################################################################################################################################
use constant HOST_DB_PORT => 6543;
push @EXPORT, qw(HOST_DB_PORT);
use constant HOST_DB_DEFAULT => 'postgres';
push @EXPORT, qw(HOST_DB_DEFAULT);
use constant HOST_DB_TIMEOUT => 30;
push @EXPORT, qw(HOST_DB_TIMEOUT);
####################################################################################################################################
# new
@@ -77,12 +66,11 @@ sub new
);
# Get db version
my $oHostGroup = hostGroupGet();
my $strDbVersion = $oHostGroup->paramGet(HOST_PARAM_DB_VERSION);
my $strDbVersion = testRunGet()->pgVersion();
my $self = $class->SUPER::new(
{
strImage => containerNamespace() . '/' . $oHostGroup->paramGet(HOST_PARAM_VM) . "-db-${strDbVersion}-test-pre",
strImage => containerNamespace() . '/' . testRunGet()->vm() . "-db-${strDbVersion}-test-pre",
strBackupDestination => $$oParam{strBackupDestination},
oLogTest => $$oParam{oLogTest},
bStandby => $$oParam{bStandby},
@@ -90,18 +78,16 @@ sub new
bless $self, $class;
# Set parameters
$self->paramSet(HOST_PARAM_DB_BIN_PATH, $oHostGroup->paramGet(HOST_PARAM_DB_BIN_PATH));
$self->paramSet(HOST_PARAM_DB_VERSION, $strDbVersion);
$self->paramSet(HOST_PARAM_DB_SOCKET_PATH, $self->dbPath());
$self->paramSet(HOST_PARAM_DB_PORT, defined($$oParam{bStandby}) && $$oParam{bStandby} ? 6544 : 6543);
$self->{strPgSocketPath} = $self->dbPath();
$self->{iPgPort} = defined($$oParam{bStandby}) && $$oParam{bStandby} ? 6544 : 6543;
$self->paramSet(HOST_PARAM_DB_LOG_PATH, $self->testPath());
$self->paramSet(HOST_PARAM_DB_LOG_FILE, $self->dbLogPath() . '/postgresql.log');
$self->{strPgLogPath} = $self->testPath();
$self->{strPgLogFile} = $self->pgLogPath() . '/postgresql.log';
# Get Db version
if (defined($strDbVersion))
{
my $strOutLog = $self->executeSimple($self->dbBinPath() . '/postgres --version');
my $strOutLog = $self->executeSimple($self->pgBinPath() . '/postgres --version');
my @stryVersionToken = split(/ /, $strOutLog);
@stryVersionToken = split(/\./, $stryVersionToken[2]);
@@ -169,7 +155,7 @@ sub sqlConnect
# Connect to the db (whether it is local or remote)
$self->{db}{$strDb}{hDb} =
DBI->connect(
"dbi:Pg:dbname=${strDb};port=" . $self->dbPort() . ';host=' . $self->dbSocketPath(),
"dbi:Pg:dbname=${strDb};port=" . $self->pgPort() . ';host=' . $self->pgSocketPath(),
$self->userGet(), undef,
{AutoCommit => 0, RaiseError => 0, PrintError => 0});
@@ -358,12 +344,12 @@ sub clusterCreate
# Don't link pg_xlog for versions < 9.2 because some recovery scenarios won't work.
$self->executeSimple(
$self->dbBinPath() . '/initdb ' .
($self->dbVersion() >= PG_VERSION_93 ? ' -k' : '') .
($self->dbVersion() >= PG_VERSION_92 ? " --xlogdir=${strXlogPath}" : '') .
$self->pgBinPath() . '/initdb ' .
($self->pgVersion() >= PG_VERSION_93 ? ' -k' : '') .
($self->pgVersion() >= PG_VERSION_92 ? " --xlogdir=${strXlogPath}" : '') .
' --pgdata=' . $self->dbBasePath() . ' --auth=trust');
if (!$self->standby() && $self->dbVersion() >= PG_VERSION_HOT_STANDBY)
if (!$self->standby() && $self->pgVersion() >= PG_VERSION_HOT_STANDBY)
{
$self->executeSimple(
"echo 'host replication replicator db-standby trust' >> " . $self->dbBasePath() . '/pg_hba.conf');
@@ -373,7 +359,7 @@ sub clusterCreate
{bHotStandby => $$hParam{bHotStandby}, bArchive => $$hParam{bArchive}, bArchiveAlways => $$hParam{bArchiveAlways},
bArchiveInvalid => $$hParam{bArchiveInvalid}});
if (!$self->standby() && $self->dbVersion() >= PG_VERSION_HOT_STANDBY)
if (!$self->standby() && $self->pgVersion() >= PG_VERSION_HOT_STANDBY)
{
$self->sqlExecute("create user replicator replication", {bCommit =>true});
}
@@ -409,12 +395,12 @@ sub clusterStart
# Start the cluster
my $strCommand =
$self->dbBinPath() . '/pg_ctl start -o "-c port=' . $self->dbPort() .
($self->dbVersion() < PG_VERSION_95 ? ' -c checkpoint_segments=1' : '');
$self->pgBinPath() . '/pg_ctl start -o "-c port=' . $self->pgPort() .
($self->pgVersion() < PG_VERSION_95 ? ' -c checkpoint_segments=1' : '');
if ($bArchiveEnabled)
{
if ($self->dbVersion() >= PG_VERSION_95 && $bArchiveAlways)
if ($self->pgVersion() >= PG_VERSION_95 && $bArchiveAlways)
{
$strCommand .= " -c archive_mode=always";
}
@@ -437,7 +423,7 @@ sub clusterStart
$strCommand .= " -c archive_command=true";
}
if ($self->dbVersion() >= PG_VERSION_90)
if ($self->pgVersion() >= PG_VERSION_90)
{
$strCommand .= " -c wal_level=hot_standby";
@@ -448,15 +434,15 @@ sub clusterStart
}
$strCommand .=
($self->dbVersion() >= PG_VERSION_HOT_STANDBY ? ' -c max_wal_senders=3' : '') .
($self->pgVersion() >= PG_VERSION_HOT_STANDBY ? ' -c max_wal_senders=3' : '') .
' -c listen_addresses=\'*\'' .
' -c log_directory=\'' . $self->dbLogPath() . "'" .
' -c log_filename=\'' . basename($self->dbLogFile()) . "'" .
' -c log_directory=\'' . $self->pgLogPath() . "'" .
' -c log_filename=\'' . basename($self->pgLogFile()) . "'" .
' -c log_rotation_age=0' .
' -c log_rotation_size=0' .
' -c log_error_verbosity=verbose' .
' -c unix_socket_director' . ($self->dbVersion() < PG_VERSION_93 ? 'y=\'' : 'ies=\'') . $self->dbPath() . '\'"' .
' -D ' . $self->dbBasePath() . ' -l ' . $self->dbLogFile() . ' -s';
' -c unix_socket_director' . ($self->pgVersion() < PG_VERSION_93 ? 'y=\'' : 'ies=\'') . $self->dbPath() . '\'"' .
' -D ' . $self->dbBasePath() . ' -l ' . $self->pgLogFile() . ' -s';
$self->executeSimple($strCommand);
@@ -485,18 +471,18 @@ sub clusterStop
if (-e $self->dbBasePath() . '/' . DB_FILE_POSTMASTERPID)
{
$self->executeSimple(
$self->dbBinPath() . '/pg_ctl stop -D ' . $self->dbBasePath() . ' -w -s -m ' .
$self->pgBinPath() . '/pg_ctl stop -D ' . $self->dbBasePath() . ' -w -s -m ' .
($bImmediate ? 'immediate' : 'fast'));
}
# Grep for errors in postgresql.log
if (!$bIgnoreLogError && fileExists($self->dbLogFile()))
if (!$bIgnoreLogError && fileExists($self->pgLogFile()))
{
$self->executeSimple('grep ERROR ' . $self->dbLogFile(), {iExpectedExitStatus => 1});
$self->executeSimple('grep ERROR ' . $self->pgLogFile(), {iExpectedExitStatus => 1});
}
# Remove the log file
fileRemove($self->dbLogFile(), true);
fileRemove($self->pgLogFile(), true);
}
####################################################################################################################################
@@ -516,11 +502,11 @@ sub clusterRestart
####################################################################################################################################
# Getters
####################################################################################################################################
sub dbBinPath {return shift->paramGet(HOST_PARAM_DB_BIN_PATH);}
sub dbLogFile {return shift->paramGet(HOST_PARAM_DB_LOG_FILE);}
sub dbLogPath {return shift->paramGet(HOST_PARAM_DB_LOG_PATH);}
sub dbPort {return shift->paramGet(HOST_PARAM_DB_PORT);}
sub dbSocketPath {return shift->paramGet(HOST_PARAM_DB_SOCKET_PATH);}
sub dbVersion {return shift->paramGet(HOST_PARAM_DB_VERSION);}
sub pgBinPath {return testRunGet()->pgBinPath()}
sub pgLogFile {return shift->{strPgLogFile}}
sub pgLogPath {return shift->{strPgLogPath}}
sub pgPort {return shift->{iPgPort}}
sub pgSocketPath {return shift->{strPgSocketPath}}
sub pgVersion {return testRunGet()->pgVersion()}
1;

View File

@@ -2,7 +2,6 @@
# HostGroupTest.pm - Encapsulate a group of docker containers for testing
####################################################################################################################################
package pgBackRestTest::Common::HostGroupTest;
use parent 'pgBackRestTest::Common::ParamTest';
####################################################################################################################################
# Perl includes
@@ -68,8 +67,8 @@ sub hostAdd
$self->{host}{$oHost->{strName}} = $oHost;
$oHost->executeSimple("sh -c 'echo \"\" >> /etc/hosts\'", undef, 'root');
$oHost->executeSimple("sh -c 'echo \"# Test Hosts\" >> /etc/hosts'", undef, 'root');
$oHost->executeSimple("echo \"\" >> /etc/hosts", undef, 'root');
$oHost->executeSimple("echo \"# Test Hosts\" >> /etc/hosts", undef, 'root');
# Iterate hosts to add IP mappings
foreach my $strOtherHostName (sort(keys(%{$self->{host}})))
@@ -79,10 +78,10 @@ sub hostAdd
if ($strOtherHostName ne $oHost->{strName})
{
# Add this host IP to all hosts
$oOtherHost->executeSimple("sh -c 'echo \"$oHost->{strIP} $oHost->{strName}\" >> /etc/hosts'", undef, 'root');
$oOtherHost->executeSimple("echo \"$oHost->{strIP} $oHost->{strName}\" >> /etc/hosts", undef, 'root');
# Add all other host IPs to this host
$oHost->executeSimple("sh -c 'echo \"$oOtherHost->{strIP} ${strOtherHostName}\" >> /etc/hosts'", undef, 'root');
$oHost->executeSimple("echo \"$oOtherHost->{strIP} ${strOtherHostName}\" >> /etc/hosts", undef, 'root');
}
}

View File

@@ -2,7 +2,6 @@
# HostTest.pm - Encapsulate a docker host for testing
####################################################################################################################################
package pgBackRestTest::Common::HostTest;
use parent 'pgBackRestTest::Common::ParamTest';
####################################################################################################################################
# Perl includes
@@ -120,8 +119,10 @@ sub execute
$strUser = $self->{strUser};
}
$strCommand =~ s/'/'\\''/g;
my $oExec = new pgBackRestTest::Common::ExecuteTest(
"docker exec -u ${strUser} $self->{strContainer} ${strCommand}" , $oParam);
"docker exec -u ${strUser} $self->{strContainer} sh -c '${strCommand}'" , $oParam);
# Return from function and log return values if any
return logDebugReturn

View File

@@ -108,11 +108,14 @@ sub testListGet
($strDbVersion ne 'all' &&
$strDbVersion eq ${$$oyVm{$strTestOS}{$strDbVersionKey}}[$iDbVersionIdx]))
{
my $iTestRunMin = defined($iModuleTestRun) ?
$iModuleTestRun : (defined($$oTest{&TESTDEF_TEST_TOTAL}) ? 1 : -1);
my $iTestRunMax = defined($iModuleTestRun) ?
$iModuleTestRun : (defined($$oTest{&TESTDEF_TEST_TOTAL}) ?
$$oTest{&TESTDEF_TEST_TOTAL} : -1);
# Individual tests will be each be run in a separate container. This is the default.
my $bTestIndividual =
!defined($$oTest{&TESTDEF_TEST_INDIVIDUAL}) || $$oTest{&TESTDEF_TEST_INDIVIDUAL} ? true : false;
my $iTestRunMin = defined($iModuleTestRun) ? $iModuleTestRun : ($bTestIndividual ? 1 : -1);
my $iTestRunMax =
defined($iModuleTestRun) ? $iModuleTestRun :
($bTestIndividual ? $$oTest{&TESTDEF_TEST_TOTAL} : -1);
if (defined($$oTest{total}) && $iTestRunMax > $$oTest{total})
{

View File

@@ -44,7 +44,6 @@ sub new
$self->{strCommandMain},
$self->{strPgSqlBin},
$self->{strTestPath},
$self->{strRepoPath}
) =
logDebugParam
(
@@ -57,13 +56,15 @@ sub new
{name => 'strCommandMain', trace => true},
{name => 'strPgSqlBin', required => false, trace => true},
{name => 'strTestPath', trace => true},
{name => 'strRepoPath', trace => true}
);
# Initialize the test log
$self->{strLog} = 'run ' . sprintf('%03d', $self->{iRun}) . ' - ' . $self->{strComment};
$self->{strLog} .= "\n" . ('=' x length($self->{strLog})) . "\n";
# Initialize the filename
$self->{strFileName} = sprintf("expect/$self->{strModule}-$self->{strTest}-%03d.log", $self->{iRun});
# Initialize the replacement hash
$self->{oReplaceHash} = {};
@@ -180,8 +181,7 @@ sub logWrite
__PACKAGE__ . '->logWrite', \@_,
{name => 'strBasePath', trace => true},
{name => 'strTestPath', trace => true},
{name => 'strFileName',
default => sprintf("expect/$self->{strModule}-$self->{strTest}-%03d.log", $self->{iRun}), trace => true}
{name => 'strFileName', default => $self->{strFileName}, trace => true}
);
my $strReferenceLogFile = "${strBasePath}/test/${strFileName}";
@@ -318,7 +318,17 @@ sub regExpReplaceAll
my $strBinPath = dirname(dirname(abs_path($0))) . '/bin';
# Replace the exe path/file
# Replace the exe path/file with sh ' prepended
my $strLineBefore = $strLine;
$strLine =~ s/sh -c '$self->{strCommandMain}/[BACKREST-BIN]/g;
# Replace the final ' if the previous expression made changes
if ($strLine ne $strLineBefore)
{
$strLine =~ s/'$//g;
}
# Replace the exe path/file with sh ' prepended
$strLine =~ s/$self->{strCommandMain}/[BACKREST-BIN]/g;
# Replace the test path

View File

@@ -1,119 +0,0 @@
####################################################################################################################################
# ParamTest.pm - Allows parameters to be added to any object
####################################################################################################################################
package pgBackRestTest::Common::ParamTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use pgBackRest::Common::Log;
####################################################################################################################################
# paramSet
####################################################################################################################################
sub paramSet
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$strKey,
$strValue,
) =
logDebugParam
(
__PACKAGE__ . '->paramSet', \@_,
{name => 'strKey', trace => true},
{name => 'strValue', trace => true},
);
$self->{param}{$strKey} = $strValue;
# Return from function and log return values if any
return logDebugReturn($strOperation);
}
####################################################################################################################################
# paramGet
####################################################################################################################################
sub paramGet
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$strKey,
$bRequired,
$strDefault,
) =
logDebugParam
(
__PACKAGE__ . '->paramGet', \@_,
{name => 'strKey', trace => true},
{name => 'bRequired', default => true, trace => true},
{name => 'strDefault', required => false, trace => true},
);
my $strValue = $self->{param}{$strKey};
if (!defined($strValue))
{
if ($bRequired)
{
confess &log(ERROR, "param '${strKey}' is required");
}
$strValue = $strDefault;
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'strValue', value => $strValue, trace => true}
);
}
####################################################################################################################################
# paramTest
####################################################################################################################################
sub paramTest
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$strKey,
$strTestValue,
) =
logDebugParam
(
__PACKAGE__ . '->paramTest', \@_,
{name => 'strKey', trace => true},
{name => 'strTestValue', required => false, trace => true},
);
my $strValue = $self->paramGet($strKey, false);
my $bResult =
!defined($strTestValue) && defined($strValue) || defined($strTestValue) && $strTestValue eq $strValue ? true : false;
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'bResult', value => $bResult, trace => true}
);
}
1;

View File

@@ -0,0 +1,328 @@
####################################################################################################################################
# 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 pgBackRest::Common::Log;
use pgBackRestTest::Common::LogTest;
use pgBackRestTest::Common::DefineTest;
####################################################################################################################################
# Constant to use when bogus data is required
####################################################################################################################################
use constant BOGUS => 'bogus';
push @EXPORT, qw(BOGUS);
####################################################################################################################################
# The current test run that is executung. 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;
####################################################################################################################################
# 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}
);
}
####################################################################################################################################
# init
#
# Empty init sub in case the ancestor class does not delare one.
####################################################################################################################################
sub init {}
####################################################################################################################################
# final
#
# Empty final sub in case the ancestor class does not delare one.
####################################################################################################################################
sub final {}
####################################################################################################################################
# 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->{strPgBinPath},
$self->{strPgVersion},
$self->{strModule},
$self->{strModuleTest},
$self->{iModuleTestRun},
$self->{iProcessMax},
$self->{bOutput},
$self->{bDryRun},
$self->{bCleanup},
$self->{bLogForce},
$self->{strPgUser},
$self->{strBackRestUser},
$self->{strGroup},
) =
logDebugParam
(
__PACKAGE__ . '->process', \@_,
{name => 'strVm'},
{name => 'iVmId'},
{name => 'strBasePath'},
{name => 'strTestPath'},
{name => 'strBackRestExe'},
{name => 'strPgBinPath', required => false},
{name => 'strPgVersion', required => false},
{name => 'strModule'},
{name => 'strModuleTest'},
{name => 'iModuleTestRun', required => false},
{name => 'iProcessMax'},
{name => 'bOutput'},
{name => 'bDryRun'},
{name => 'bCleanup'},
{name => 'bLogForce'},
{name => 'strPgUser'},
{name => 'strBackRestUser'},
{name => 'strGroup'},
);
# Init, run, and end the test(s)
$self->init();
$self->run();
$self->final();
$self->end();
# Make sure the correct number of tests ran
my $hModule = testDefModuleGet($self->{strModule});
my $hModuleTest = testDefModuleTestGet($hModule, $self->{strModuleTest});
if ($hModuleTest->{&TESTDEF_TEST_TOTAL} != $self->runCurrent())
{
confess &log(ASSERT, "expected $hModuleTest->{&TESTDEF_TEST_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,
$bExpect,
) =
logDebugParam
(
__PACKAGE__ . '->begin', \@_,
{name => 'strDescription'},
{name => 'bExpect', required => false},
);
# Save the previous expect log
$self->end();
# If bExpect is defined then it is an override of the default
if (defined($bExpect))
{
$self->{bExpect} = $bExpect;
}
# Else get the default expect setting
else
{
my $hModule = testDefModuleGet($self->{strModule});
$self->{bExpect} = $hModule->{&TESTDEF_EXPECT} ? true : false;
}
# Increment the run counter;
$self->{iRun}++;
# Return if this test should not be run
if (defined($self->moduleTestRun()) && $self->moduleTestRun() != $self->runCurrent())
{
return false;
}
# Output information about test to run
&log(INFO, 'run ' . sprintf('%03d', $self->runCurrent()) . ' - ' . $strDescription);
if ($self->isDryRun())
{
return false;
}
# If the module is defined then create a ExpectTest object
if ($self->doExpect())
{
$self->{oExpect} = new pgBackRestTest::Common::LogTest(
$self->module(), $self->moduleTest(), $self->runCurrent(), $self->doLogForce(), $strDescription, $self->backrestExe(),
$self->pgBinPath(), $self->testPath());
&log(INFO, ' expect log: ' . $self->{oExpect}->{strFileName});
}
return true;
}
####################################################################################################################################
# end
####################################################################################################################################
sub end
{
my $self = shift;
# Save the previous test log
if (defined($self->expect()))
{
$self->expect()->logWrite($self->basePath(), $self->testPath());
delete($self->{oExpect});
}
}
####################################################################################################################################
# testRunName
#
# Create module/test names by upper-casing the first letter and then inserting capitals after each -.
####################################################################################################################################
sub testRunName
{
my $strName = shift;
my @stryName = split('\-', $strName);
$strName = undef;
foreach my $strPart (@stryName)
{
$strName .= ucfirst($strPart);
}
return $strName;
}
####################################################################################################################################
# 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::' . testRunName($strModule) . '::' . testRunName($strModule) . testRunName($strModuleTest) . 'Test';
$oTestRun = eval( ## no critic (BuiltinFunctions::ProhibitStringyEval)
"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);
####################################################################################################################################
# Getters
####################################################################################################################################
sub backrestExe {return shift->{strBackRestExe}}
sub backrestUser {return shift->{strBackRestUser}}
sub basePath {return shift->{strBasePath}}
sub dataPath {return shift->basePath() . '/test/data'}
sub doCleanup {return shift->{bCleanup}}
sub doExpect {return shift->{bExpect}}
sub doLogForce {return shift->{bLogForce}}
sub group {return shift->{strGroup}}
sub isDryRun {return shift->{bDryRun}}
sub expect {return shift->{oExpect}}
sub module {return shift->{strModule}}
sub moduleTest {return shift->{strModuleTest}}
sub moduleTestRun {return shift->{iModuleTestRun}}
sub pgBinPath {return shift->{strPgBinPath}}
sub pgUser {return shift->{strPgUser}}
sub pgVersion {return shift->{strPgVersion}}
sub processMax {return shift->{iProcessMax}}
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;

View File

@@ -1,182 +0,0 @@
####################################################################################################################################
# CommonTest.pm - Common globals used for testing
####################################################################################################################################
package pgBackRestTest::CommonTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Cwd qw(abs_path);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname);
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRestTest::Common::LogTest;
####################################################################################################################################
# Module variables
####################################################################################################################################
my $strPgSqlBin;
my $strCommonCommandMain;
my $strCommonBasePath;
my $strCommonTestPath;
my $strCommonDataPath;
my $strCommonRepoPath;
my $strCommonDbPath;
my $strCommonDbCommonPath;
my $iModuleTestRunOnly;
my $bDryRun;
my $bNoCleanup;
my $bLogForce;
####################################################################################################################################
# testRun
####################################################################################################################################
sub testRun
{
my $iRun = shift;
my $strLog = shift;
my $strModuleParam = shift;
my $strModuleTestParam = shift;
my $oLogTestRef = shift;
# Save the previous test log
if (defined($$oLogTestRef))
{
$$oLogTestRef->logWrite($strCommonBasePath, $strCommonTestPath);
$$oLogTestRef = undef;
}
# Return if this test should not be run
if (defined($iModuleTestRunOnly) && $iModuleTestRunOnly != $iRun)
{
return false;
}
# Output information about test to run
&log(INFO, 'run ' . sprintf('%03d', $iRun) . ' - ' . $strLog);
if ($bDryRun)
{
return false;
}
# If the module is defined then create a LogTest object
if (defined($strModuleParam))
{
$$oLogTestRef = new pgBackRestTest::Common::LogTest(
$strModuleParam, $strModuleTestParam, $iRun, $bLogForce, $strLog, $strCommonCommandMain, $strPgSqlBin,
$strCommonTestPath, $strCommonRepoPath);
}
return true;
}
push(@EXPORT, qw(testRun));
####################################################################################################################################
# testCleanup
####################################################################################################################################
sub testCleanup
{
my $oLogTestRef = shift;
# Save the previous test log
if (defined($$oLogTestRef))
{
$$oLogTestRef->logWrite($strCommonBasePath, $strCommonTestPath);
$$oLogTestRef = undef;
}
# Return false if there is no cleanup or if this was a test run (this prevents cleanup from being run)
return !$bNoCleanup && !$bDryRun;
}
push(@EXPORT, qw(testCleanup));
####################################################################################################################################
# testSetup
####################################################################################################################################
sub testSetup
{
my $strTestPathParam = shift;
my $strPgSqlBinParam = shift;
my $iModuleTestRunOnlyParam = shift;
my $bDryRunParam = shift;
my $bNoCleanupParam = shift;
my $bLogForceParam = shift;
$strCommonBasePath = dirname(dirname(abs_path($0)));
$strPgSqlBin = $strPgSqlBinParam;
if (defined($strTestPathParam))
{
$strCommonTestPath = $strTestPathParam;
}
else
{
$strCommonTestPath = cwd() . "/test";
}
$strCommonDataPath = "${strCommonBasePath}/test/data";
$strCommonRepoPath = "${strCommonTestPath}/backrest";
$strCommonDbPath = "${strCommonTestPath}/db-master/db";
$strCommonDbCommonPath = "${strCommonDbPath}/common";
$strCommonCommandMain = $strCommonBasePath . '/bin/pgbackrest';
$iModuleTestRunOnly = $iModuleTestRunOnlyParam;
$bDryRun = $bDryRunParam;
$bNoCleanup = $bNoCleanupParam;
$bLogForce = $bLogForceParam;
return true;
}
push(@EXPORT, qw(testSetup));
####################################################################################################################################
# testIniSave
####################################################################################################################################
sub testIniSave
{
my $strFileName = shift;
my $oIniRef = shift;
my $bChecksum = shift;
# Calculate a new checksum if requested
if (defined($bChecksum) && $bChecksum)
{
delete($$oIniRef{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});
my $oSHA = Digest::SHA->new('sha1');
my $oJSON = JSON::PP->new()->canonical()->allow_nonref();
$oSHA->add($oJSON->encode($oIniRef));
$$oIniRef{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = $oSHA->hexdigest();
}
iniSave($strFileName, $oIniRef);
}
push(@EXPORT, qw(testIniSave));
####################################################################################################################################
# Get Methods
####################################################################################################################################
sub testDataPath
{
return $strCommonDataPath;
}
push(@EXPORT, qw(testDataPath));
1;

View File

@@ -0,0 +1,245 @@
####################################################################################################################################
# ConfigCommonTest.pm - Common code for Config unit tests
####################################################################################################################################
package pgBackRestTest::Config::ConfigCommonTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
sub optionSetTest
{
my $self = shift;
my $oOption = shift;
my $strKey = shift;
my $strValue = shift;
$$oOption{option}{$strKey} = $strValue;
}
sub optionBoolSetTest
{
my $self = shift;
my $oOption = shift;
my $strKey = shift;
my $bValue = shift;
$$oOption{boolean}{$strKey} = defined($bValue) ? $bValue : true;
}
sub commandSetTest
{
my $self = shift;
my $oOption = shift;
my $strCommand = shift;
$$oOption{command} = $strCommand;
}
# sub optionRemoveTest
# {
# my $self = shift;
# my $oOption = shift;
# my $strKey = shift;
#
# delete($$oOption{option}{$strKey});
# delete($$oOption{boolean}{$strKey});
# }
sub argvWriteTest
{
my $self = shift;
my $oOption = shift;
@ARGV = ();
if (defined($$oOption{boolean}))
{
foreach my $strKey (keys(%{$$oOption{boolean}}))
{
if ($$oOption{boolean}{$strKey})
{
$ARGV[@ARGV] = "--${strKey}";
}
else
{
$ARGV[@ARGV] = "--no-${strKey}";
}
}
}
if (defined($$oOption{option}))
{
foreach my $strKey (keys(%{$$oOption{option}}))
{
$ARGV[@ARGV] = "--${strKey}=$$oOption{option}{$strKey}";
}
}
$ARGV[@ARGV] = $$oOption{command};
&log(INFO, " command line: " . join(" ", @ARGV));
%$oOption = ();
}
sub configLoadExpect
{
my $self = shift;
my $oOption = shift;
my $strCommand = shift;
my $iExpectedError = shift;
my $strErrorParam1 = shift;
my $strErrorParam2 = shift;
my $strErrorParam3 = shift;
my $oOptionRuleExpected = optionRuleGet();
$self->commandSetTest($oOption, $strCommand);
$self->argvWriteTest($oOption);
my $bErrorFound = false;
eval
{
configLoad(false);
return true;
}
or do
{
my $oException = $EVAL_ERROR;
if (!defined($iExpectedError))
{
confess $oException;
}
$bErrorFound = true;
if (isException($oException))
{
if ($oException->code() != $iExpectedError)
{
confess "expected error ${iExpectedError} from configLoad but got [" . $oException->code() .
"] '" . $oException->message() . "'";
}
my $strError;
if ($iExpectedError == ERROR_OPTION_REQUIRED)
{
$strError = "${strCommand} command requires option: ${strErrorParam1}" .
(defined($strErrorParam2) ? "\nHINT: ${strErrorParam2}" : '');
}
elsif ($iExpectedError == ERROR_COMMAND_REQUIRED)
{
$strError = "command must be specified";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID)
{
$strError = "option '${strErrorParam1}' not valid without option '${strErrorParam2}'";
if (defined($strErrorParam3))
{
$strError .= @{$strErrorParam3} == 1 ? " = '$$strErrorParam3[0]'" :
" in ('" . join("', '",@{ $strErrorParam3}) . "')";
}
}
elsif ($iExpectedError == ERROR_OPTION_COMMAND)
{
$strError = "option '${strErrorParam1}' not valid for command '${strErrorParam2}'";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID_VALUE)
{
$strError = "'${strErrorParam1}' is not valid for '${strErrorParam2}' option" .
(defined($strErrorParam3) ? "\nHINT: ${strErrorParam3}." : '');
}
elsif ($iExpectedError == ERROR_OPTION_MULTIPLE_VALUE)
{
$strError = "option '${strErrorParam1}' cannot be specified multiple times";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID_RANGE)
{
$strError = "'${strErrorParam1}' is not valid for '${strErrorParam2}' option";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID_PAIR)
{
$strError = "'${strErrorParam1}' not valid key/value for '${strErrorParam2}' option";
}
elsif ($iExpectedError == ERROR_OPTION_NEGATE)
{
$strError = "option '${strErrorParam1}' cannot be both set and negated";
}
elsif ($iExpectedError == ERROR_FILE_INVALID)
{
$strError = "'${strErrorParam1}' is not a file";
}
else
{
confess
"must construct message for error ${iExpectedError}, use this as an example: '" . $oException->message() . "'";
}
if ($oException->message() ne $strError)
{
confess "expected error message \"${strError}\" from configLoad but got \"" . $oException->message() . "\"";
}
}
else
{
confess "configLoad should throw pgBackRest::Common::Exception:\n$oException";
}
};
if (!$bErrorFound && defined($iExpectedError))
{
confess "expected error ${iExpectedError} from configLoad but got success";
}
}
sub optionTestExpect
{
my $self = shift;
my $strOption = shift;
my $strExpectedValue = shift;
my $strExpectedKey = shift;
if (defined($strExpectedValue))
{
my $strActualValue = optionGet($strOption);
if (defined($strExpectedKey))
{
$strActualValue = $$strActualValue{$strExpectedKey};
}
if (!defined($strActualValue))
{
confess "expected option ${strOption} to have value ${strExpectedValue} but [undef] found instead";
}
$strActualValue eq $strExpectedValue
or confess "expected option ${strOption} to have value ${strExpectedValue} but ${strActualValue} found instead";
}
elsif (optionTest($strOption))
{
confess "expected option ${strOption} to be [undef], but " . optionGet($strOption) . ' found instead';
}
}
####################################################################################################################################
# Getters
####################################################################################################################################
# Change this from the default so the same stanza is not used in all tests.
sub stanza {return 'main'};
1;

View File

@@ -0,0 +1,353 @@
####################################################################################################################################
# ConfigConfigTest.pm - Tests for mixed command line and config file options in Config.pm
####################################################################################################################################
package pgBackRestTest::Config::ConfigConfigTest;
use parent 'pgBackRestTest::Config::ConfigCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
my $oOption = {};
my $oConfig = {};
my $strConfigFile = $self->testPath() . '/pgbackrest.conf';
if ($self->begin('set and negate option ' . OPTION_CONFIG))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, '/dude/dude.conf');
$self->optionBoolSetTest($oOption, OPTION_CONFIG, false);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_NEGATE, OPTION_CONFIG);
}
if ($self->begin('option ' . OPTION_CONFIG))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionBoolSetTest($oOption, OPTION_CONFIG, false);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_CONFIG);
}
if ($self->begin('default option ' . OPTION_CONFIG))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_CONFIG, OPTION_DEFAULT_CONFIG);
}
if ($self->begin('config file is a path'))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $self->testPath());
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_FILE_INVALID, $self->testPath());
}
if ($self->begin('load from config stanza command section - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{$self->stanza() . ':' . &CMD_BACKUP}{&OPTION_PROCESS_MAX} = 2;
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROCESS_MAX, 2);
}
if ($self->begin('load from config stanza section - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_PROCESS_MAX} = 3;
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROCESS_MAX, 3);
}
if ($self->begin('load from config global command section - option thread-max'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{'thread-max'} = 2;
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROCESS_MAX, 2);
}
if ($self->begin('load from config global section - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_PROCESS_MAX} = 5;
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROCESS_MAX, 5);
}
if ($self->begin('default - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROCESS_MAX, 1);
}
if ($self->begin('command-line override - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_PROCESS_MAX} = 9;
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_PROCESS_MAX, 7);
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROCESS_MAX, 7);
}
if ($self->begin('invalid boolean - option ' . OPTION_HARDLINK))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{&OPTION_HARDLINK} = 'Y';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, 'Y', OPTION_HARDLINK);
}
if ($self->begin('invalid value - option ' . OPTION_LOG_LEVEL_CONSOLE))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_LOG_LEVEL_CONSOLE} = BOGUS;
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_LOG_LEVEL_CONSOLE);
}
if ($self->begin('valid value - option ' . OPTION_LOG_LEVEL_CONSOLE))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_LOG_LEVEL_CONSOLE} = lc(INFO);
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_RESTORE);
}
if ($self->begin('archive-push - option ' . OPTION_LOG_LEVEL_CONSOLE))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_ARCHIVE_PUSH);
}
if ($self->begin(CMD_EXPIRE . ' ' . OPTION_RETENTION_FULL))
{
$oConfig = {};
$$oConfig{$self->stanza() . ':' . &CMD_EXPIRE}{&OPTION_RETENTION_FULL} = 2;
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_EXPIRE);
$self->optionTestExpect(OPTION_RETENTION_FULL, 2);
}
if ($self->begin(CMD_BACKUP . ' option ' . OPTION_COMPRESS))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{&OPTION_COMPRESS} = 'n';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_COMPRESS, false);
}
if ($self->begin(CMD_RESTORE . ' global option ' . OPTION_RESTORE_RECOVERY_OPTION . ' error'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} = 'bogus=';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_VALUE, 'bogus=', OPTION_RESTORE_RECOVERY_OPTION);
}
if ($self->begin(CMD_RESTORE . ' global option ' . OPTION_RESTORE_RECOVERY_OPTION . ' error'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} = '=bogus';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_VALUE, '=bogus', OPTION_RESTORE_RECOVERY_OPTION);
}
if ($self->begin(CMD_RESTORE . ' global option ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} =
'archive-command=/path/to/pgbackrest';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_RESTORE);
$self->optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, '/path/to/pgbackrest', 'archive-command');
}
if ($self->begin(CMD_RESTORE . ' stanza option ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_RESTORE_RECOVERY_OPTION} = ['standby-mode=on', 'a=b'];
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_RESTORE);
$self->optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, 'b', 'a');
$self->optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, 'on', 'standby-mode');
}
if ($self->begin(CMD_BACKUP . ' option ' . OPTION_DB_PATH))
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_DB_PATH, '/path/to/db');
}
if ($self->begin(CMD_BACKUP . ' option ' . OPTION_BACKUP_ARCHIVE_CHECK))
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->optionBoolSetTest($oOption, OPTION_BACKUP_ARCHIVE_CHECK, false);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_ONLINE, true);
$self->optionTestExpect(OPTION_BACKUP_ARCHIVE_CHECK, false);
}
if ($self->begin(CMD_ARCHIVE_PUSH . ' option ' . OPTION_DB_PATH))
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_ARCHIVE_PUSH);
$self->optionTestExpect(OPTION_DB_PATH, '/path/to/db');
}
if ($self->begin(CMD_BACKUP . ' option ' . OPTION_REPO_PATH))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_REPO_PATH} = '/repo';
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_REPO_PATH, '/repo');
}
if ($self->begin(CMD_BACKUP . ' option ' . OPTION_REPO_PATH . ' multiple times'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_REPO_PATH} = ['/repo', '/repo2'];
iniSave($strConfigFile, $oConfig, true);
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_MULTIPLE_VALUE, OPTION_REPO_PATH);
}
}
1;

View File

@@ -0,0 +1,383 @@
####################################################################################################################################
# ConfigOptionTest.pm - Tests for command line options in Config.pm
####################################################################################################################################
package pgBackRestTest::Config::ConfigOptionTest;
use parent 'pgBackRestTest::Config::ConfigCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Cwd qw(abs_path);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
my $oOption = {};
my @oyArray;
if ($self->begin('backup with no stanza'))
{
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_REQUIRED, OPTION_STANZA);
}
if ($self->begin('backup with boolean stanza'))
{
$self->optionBoolSetTest($oOption, OPTION_STANZA);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_COMMAND_REQUIRED);
}
if ($self->begin('backup type defaults to ' . BACKUP_TYPE_INCR))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_TYPE, BACKUP_TYPE_INCR);
}
if ($self->begin(CMD_BACKUP . ' invalid option ' . OPTION_ARCHIVE_ASYNC))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionBoolSetTest($oOption, OPTION_ARCHIVE_ASYNC);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_COMMAND, OPTION_ARCHIVE_ASYNC, CMD_BACKUP);
}
if ($self->begin('backup type set to ' . BACKUP_TYPE_FULL))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_TYPE, BACKUP_TYPE_FULL);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_TYPE, BACKUP_TYPE_FULL);
}
if ($self->begin('backup type invalid'))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_TYPE, BOGUS);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TYPE);
}
if ($self->begin('backup invalid force'))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionBoolSetTest($oOption, OPTION_FORCE);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID, OPTION_FORCE, 'no-' . OPTION_ONLINE);
}
if ($self->begin('backup valid force'))
{
# $oOption = {};
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionBoolSetTest($oOption, OPTION_ONLINE, false);
$self->optionBoolSetTest($oOption, OPTION_FORCE);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_ONLINE, false);
$self->optionTestExpect(OPTION_FORCE, true);
}
if ($self->begin('backup invalid value for ' . OPTION_TEST_DELAY))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionBoolSetTest($oOption, OPTION_TEST);
$self->optionSetTest($oOption, OPTION_TEST_DELAY, BOGUS);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TEST_DELAY);
}
if ($self->begin('backup invalid ' . OPTION_TEST_DELAY))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_TEST_DELAY, 5);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID, OPTION_TEST_DELAY, OPTION_TEST);
}
if ($self->begin('backup check ' . OPTION_TEST_DELAY . ' undef'))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_TEST_DELAY);
}
if ($self->begin('restore invalid ' . OPTION_TARGET))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_DEFAULT);
$self->optionSetTest($oOption, OPTION_TARGET, BOGUS);
@oyArray = (RECOVERY_TYPE_NAME, RECOVERY_TYPE_TIME, RECOVERY_TYPE_XID);
$self->configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID, OPTION_TARGET, OPTION_TYPE, \@oyArray);
}
if ($self->begin('restore ' . OPTION_TARGET))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_NAME);
$self->optionSetTest($oOption, OPTION_TARGET, BOGUS);
$self->configLoadExpect($oOption, CMD_RESTORE);
$self->optionTestExpect(OPTION_TYPE, RECOVERY_TYPE_NAME);
$self->optionTestExpect(OPTION_TARGET, BOGUS);
$self->optionTestExpect(OPTION_TARGET_TIMELINE);
}
if ($self->begin('invalid string ' . OPTION_PROCESS_MAX))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_PROCESS_MAX, BOGUS);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_PROCESS_MAX);
}
if ($self->begin('invalid float ' . OPTION_PROCESS_MAX))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_PROCESS_MAX, '0.0');
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, '0.0', OPTION_PROCESS_MAX);
}
if ($self->begin('valid ' . OPTION_PROCESS_MAX))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_PROCESS_MAX, '2');
$self->configLoadExpect($oOption, CMD_BACKUP);
}
if ($self->begin('valid thread-max'))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, 'thread-max', '2');
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROCESS_MAX, 2);
}
if ($self->begin('valid float ' . OPTION_TEST_DELAY))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionBoolSetTest($oOption, OPTION_TEST);
$self->optionSetTest($oOption, OPTION_TEST_DELAY, '0.25');
$self->configLoadExpect($oOption, CMD_BACKUP);
}
if ($self->begin('valid int ' . OPTION_TEST_DELAY))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionBoolSetTest($oOption, OPTION_TEST);
$self->optionSetTest($oOption, OPTION_TEST_DELAY, 3);
$self->configLoadExpect($oOption, CMD_BACKUP);
}
if ($self->begin('restore valid ' . OPTION_TARGET_TIMELINE))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_TARGET_TIMELINE, 2);
$self->configLoadExpect($oOption, CMD_RESTORE);
}
if ($self->begin('invalid ' . OPTION_BUFFER_SIZE))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_BUFFER_SIZE, '512');
$self->configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_RANGE, '512', OPTION_BUFFER_SIZE);
}
if ($self->begin(CMD_BACKUP . ' invalid value ' . OPTION_RETENTION_ARCHIVE_TYPE))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RETENTION_ARCHIVE_TYPE, BOGUS);
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_RETENTION_ARCHIVE_TYPE);
}
if ($self->begin(
CMD_BACKUP . ' default value ' . OPTION_DEFAULT_RETENTION_ARCHIVE_TYPE . ' for ' . OPTION_RETENTION_ARCHIVE_TYPE .
' with valid ' . OPTION_RETENTION_FULL))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RETENTION_FULL, 1);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_RETENTION_ARCHIVE, 1);
$self->optionTestExpect(OPTION_RETENTION_FULL, 1);
$self->optionTestExpect(OPTION_RETENTION_DIFF, undef);
# Default is FULL so this test will fail if the default is changed, alerting to the need to update configLoad.
$self->optionTestExpect(OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_FULL);
}
if ($self->begin(
CMD_BACKUP . ' valid value ' . OPTION_RETENTION_ARCHIVE . ' for ' . OPTION_RETENTION_ARCHIVE_TYPE . ' ' .
BACKUP_TYPE_DIFF))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RETENTION_DIFF, 1);
$self->optionSetTest($oOption, OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_DIFF);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_RETENTION_ARCHIVE, 1);
$self->optionTestExpect(OPTION_RETENTION_DIFF, 1);
$self->optionTestExpect(OPTION_RETENTION_FULL, undef);
$self->optionTestExpect(OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_DIFF);
}
if ($self->begin(
CMD_BACKUP . ' warn no valid value ' . OPTION_RETENTION_ARCHIVE . ' for ' . OPTION_RETENTION_ARCHIVE_TYPE . ' ' .
BACKUP_TYPE_INCR))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RETENTION_FULL, 2);
$self->optionSetTest($oOption, OPTION_RETENTION_DIFF, 1);
$self->optionSetTest($oOption, OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_INCR);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_RETENTION_ARCHIVE, undef);
$self->optionTestExpect(OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_INCR);
}
if ($self->begin(CMD_BACKUP . ' invalid value ' . OPTION_PROTOCOL_TIMEOUT))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_DB_TIMEOUT, 5);
$self->optionSetTest($oOption, OPTION_PROTOCOL_TIMEOUT, 4);
$self->configLoadExpect(
$oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, 4, OPTION_PROTOCOL_TIMEOUT,
"'protocol-timeout' option should be greater than 'db-timeout' option");
}
if ($self->begin(CMD_RESTORE . ' invalid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, '=');
$self->configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_PAIR, '=', OPTION_RESTORE_RECOVERY_OPTION);
}
if ($self->begin(CMD_RESTORE . ' invalid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, '=' . BOGUS);
$self->configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_PAIR, '=' . BOGUS, OPTION_RESTORE_RECOVERY_OPTION);
}
if ($self->begin(CMD_RESTORE . ' invalid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, BOGUS . '=');
$self->configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_PAIR, BOGUS . '=', OPTION_RESTORE_RECOVERY_OPTION);
}
if ($self->begin(CMD_RESTORE . ' valid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, 'primary-conn-info=db.domain.net');
$self->configLoadExpect($oOption, CMD_RESTORE);
$self->optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, 'db.domain.net', 'primary-conn-info');
}
if ($self->begin(CMD_RESTORE . ' values passed to ' . CMD_ARCHIVE_GET))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db path/main');
$self->optionSetTest($oOption, OPTION_REPO_PATH, '/repo');
$self->optionSetTest($oOption, OPTION_BACKUP_HOST, 'db.mydomain.com');
$self->configLoadExpect($oOption, CMD_RESTORE);
my $strCommand = commandWrite(CMD_ARCHIVE_GET);
my $strExpectedCommand = abs_path($0) . " --backup-host=db.mydomain.com \"--db-path=/db path/main\"" .
" --repo-path=/repo --stanza=main " . CMD_ARCHIVE_GET;
if ($strCommand ne $strExpectedCommand)
{
confess "expected command '${strExpectedCommand}' but got '${strCommand}'";
}
}
if ($self->begin(CMD_BACKUP . ' default value ' . OPTION_DB_CMD))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_HOST, 'db');
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_DB_CMD, abs_path($0));
}
if ($self->begin(CMD_BACKUP . ' missing option ' . OPTION_DB_PATH))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_REQUIRED, OPTION_DB_PATH, 'does this stanza exist?');
}
if ($self->begin(CMD_BACKUP . ' automatically increase ' . OPTION_PROTOCOL_TIMEOUT))
{
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
$self->optionSetTest($oOption, OPTION_DB_TIMEOUT, OPTION_DEFAULT_PROTOCOL_TIMEOUT + 30);
$self->configLoadExpect($oOption, CMD_BACKUP);
$self->optionTestExpect(OPTION_PROTOCOL_TIMEOUT, OPTION_DEFAULT_PROTOCOL_TIMEOUT + 60);
}
}
1;

View File

@@ -1,970 +0,0 @@
####################################################################################################################################
# ConfigTest.pm - Unit Tests for pgBackRest::Param and pgBackRest::Config::Config
####################################################################################################################################
package pgBackRestTest::Config::ConfigTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Cwd qw(abs_path);
use Exporter qw(import);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::CommonTest;
sub optionSetTest
{
my $oOption = shift;
my $strKey = shift;
my $strValue = shift;
$$oOption{option}{$strKey} = $strValue;
}
sub optionBoolSetTest
{
my $oOption = shift;
my $strKey = shift;
my $bValue = shift;
$$oOption{boolean}{$strKey} = defined($bValue) ? $bValue : true;
}
sub commandSetTest
{
my $oOption = shift;
my $strCommand = shift;
$$oOption{command} = $strCommand;
}
sub optionRemoveTest
{
my $oOption = shift;
my $strKey = shift;
delete($$oOption{option}{$strKey});
delete($$oOption{boolean}{$strKey});
}
sub argvWriteTest
{
my $oOption = shift;
@ARGV = ();
if (defined($$oOption{boolean}))
{
foreach my $strKey (keys(%{$$oOption{boolean}}))
{
if ($$oOption{boolean}{$strKey})
{
$ARGV[@ARGV] = "--${strKey}";
}
else
{
$ARGV[@ARGV] = "--no-${strKey}";
}
}
}
if (defined($$oOption{option}))
{
foreach my $strKey (keys(%{$$oOption{option}}))
{
$ARGV[@ARGV] = "--${strKey}=$$oOption{option}{$strKey}";
}
}
$ARGV[@ARGV] = $$oOption{command};
&log(INFO, " command line: " . join(" ", @ARGV));
%$oOption = ();
}
sub configLoadExpect
{
my $oOption = shift;
my $strCommand = shift;
my $iExpectedError = shift;
my $strErrorParam1 = shift;
my $strErrorParam2 = shift;
my $strErrorParam3 = shift;
my $oOptionRuleExpected = optionRuleGet();
commandSetTest($oOption, $strCommand);
argvWriteTest($oOption);
my $bErrorFound = false;
eval
{
configLoad(false);
return true;
}
or do
{
my $oException = $EVAL_ERROR;
if (!defined($iExpectedError))
{
confess $oException;
}
$bErrorFound = true;
if (isException($oException))
{
if ($oException->code() != $iExpectedError)
{
confess "expected error ${iExpectedError} from configLoad but got [" . $oException->code() .
"] '" . $oException->message() . "'";
}
my $strError;
if ($iExpectedError == ERROR_OPTION_REQUIRED)
{
$strError = "${strCommand} command requires option: ${strErrorParam1}" .
(defined($strErrorParam2) ? "\nHINT: ${strErrorParam2}" : '');
}
elsif ($iExpectedError == ERROR_COMMAND_REQUIRED)
{
$strError = "command must be specified";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID)
{
$strError = "option '${strErrorParam1}' not valid without option '${strErrorParam2}'";
if (defined($strErrorParam3))
{
$strError .= @{$strErrorParam3} == 1 ? " = '$$strErrorParam3[0]'" :
" in ('" . join("', '",@{ $strErrorParam3}) . "')";
}
}
elsif ($iExpectedError == ERROR_OPTION_COMMAND)
{
$strError = "option '${strErrorParam1}' not valid for command '${strErrorParam2}'";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID_VALUE)
{
$strError = "'${strErrorParam1}' is not valid for '${strErrorParam2}' option" .
(defined($strErrorParam3) ? "\nHINT: ${strErrorParam3}." : '');
}
elsif ($iExpectedError == ERROR_OPTION_MULTIPLE_VALUE)
{
$strError = "option '${strErrorParam1}' cannot be specified multiple times";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID_RANGE)
{
$strError = "'${strErrorParam1}' is not valid for '${strErrorParam2}' option";
}
elsif ($iExpectedError == ERROR_OPTION_INVALID_PAIR)
{
$strError = "'${strErrorParam1}' not valid key/value for '${strErrorParam2}' option";
}
elsif ($iExpectedError == ERROR_OPTION_NEGATE)
{
$strError = "option '${strErrorParam1}' cannot be both set and negated";
}
elsif ($iExpectedError == ERROR_FILE_INVALID)
{
$strError = "'${strErrorParam1}' is not a file";
}
else
{
confess
"must construct message for error ${iExpectedError}, use this as an example: '" . $oException->message() . "'";
}
if ($oException->message() ne $strError)
{
confess "expected error message \"${strError}\" from configLoad but got \"" . $oException->message() . "\"";
}
}
else
{
confess "configLoad should throw pgBackRest::Common::Exception:\n$oException";
}
};
if (!$bErrorFound && defined($iExpectedError))
{
confess "expected error ${iExpectedError} from configLoad but got success";
}
}
sub optionTestExpect
{
my $strOption = shift;
my $strExpectedValue = shift;
my $strExpectedKey = shift;
if (defined($strExpectedValue))
{
my $strActualValue = optionGet($strOption);
if (defined($strExpectedKey))
{
$strActualValue = $$strActualValue{$strExpectedKey};
}
if (!defined($strActualValue))
{
confess "expected option ${strOption} to have value ${strExpectedValue} but [undef] found instead";
}
$strActualValue eq $strExpectedValue
or confess "expected option ${strOption} to have value ${strExpectedValue} but ${strActualValue} found instead";
}
elsif (optionTest($strOption))
{
confess "expected option ${strOption} to be [undef], but " . optionGet($strOption) . ' found instead';
}
}
####################################################################################################################################
# configTestRun
####################################################################################################################################
sub configTestRun
{
my $strTest = shift;
my $bVmOut = shift;
# Setup test variables
my $oHostGroup = hostGroupGet();
my $iRun;
my $bCreate;
my $strStanza = 'main';
my $oOption = {};
my $oConfig = {};
my @oyArray;
my $strTestPath = $oHostGroup->paramGet(HOST_PARAM_TEST_PATH);
my $strConfigFile = "${strTestPath}/pgbackrest.conf";
use constant BOGUS => 'bogus';
# Print test banner
if (!$bVmOut)
{
&log(INFO, 'CONFIG MODULE ******************************************************************');
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test command-line options
#-------------------------------------------------------------------------------------------------------------------------------
my $strThisTest = 'option';
if ($strTest eq 'all' || $strTest eq $strThisTest)
{
$iRun = 0;
if (!$bVmOut)
{
&log(INFO, "Test ${strThisTest}\n");
}
if (testRun(++$iRun, 'backup with no stanza'))
{
optionSetTest($oOption, OPTION_DB_PATH, '/db');
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_REQUIRED, OPTION_STANZA);
}
if (testRun(++$iRun, 'backup with boolean stanza'))
{
optionBoolSetTest($oOption, OPTION_STANZA);
configLoadExpect($oOption, CMD_BACKUP, ERROR_COMMAND_REQUIRED);
}
if (testRun(++$iRun, 'backup type defaults to ' . BACKUP_TYPE_INCR))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_TYPE, BACKUP_TYPE_INCR);
}
if (testRun(++$iRun, CMD_BACKUP . ' invalid option ' . OPTION_ARCHIVE_ASYNC))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionBoolSetTest($oOption, OPTION_ARCHIVE_ASYNC);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_COMMAND, OPTION_ARCHIVE_ASYNC, CMD_BACKUP);
}
if (testRun(++$iRun, 'backup type set to ' . BACKUP_TYPE_FULL))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_TYPE, BACKUP_TYPE_FULL);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_TYPE, BACKUP_TYPE_FULL);
}
if (testRun(++$iRun, 'backup type invalid'))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_TYPE, BOGUS);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TYPE);
}
if (testRun(++$iRun, 'backup invalid force'))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionBoolSetTest($oOption, OPTION_FORCE);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID, OPTION_FORCE, 'no-' . OPTION_ONLINE);
}
if (testRun(++$iRun, 'backup valid force'))
{
# $oOption = {};
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionBoolSetTest($oOption, OPTION_ONLINE, false);
optionBoolSetTest($oOption, OPTION_FORCE);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_ONLINE, false);
optionTestExpect(OPTION_FORCE, true);
}
if (testRun(++$iRun, 'backup invalid value for ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionBoolSetTest($oOption, OPTION_TEST);
optionSetTest($oOption, OPTION_TEST_DELAY, BOGUS);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_TEST_DELAY);
}
if (testRun(++$iRun, 'backup invalid ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_TEST_DELAY, 5);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID, OPTION_TEST_DELAY, OPTION_TEST);
}
if (testRun(++$iRun, 'backup check ' . OPTION_TEST_DELAY . ' undef'))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_TEST_DELAY);
}
if (testRun(++$iRun, 'restore invalid ' . OPTION_TARGET))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_DEFAULT);
optionSetTest($oOption, OPTION_TARGET, BOGUS);
@oyArray = (RECOVERY_TYPE_NAME, RECOVERY_TYPE_TIME, RECOVERY_TYPE_XID);
configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID, OPTION_TARGET, OPTION_TYPE, \@oyArray);
}
if (testRun(++$iRun, 'restore ' . OPTION_TARGET))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_TYPE, RECOVERY_TYPE_NAME);
optionSetTest($oOption, OPTION_TARGET, BOGUS);
configLoadExpect($oOption, CMD_RESTORE);
optionTestExpect(OPTION_TYPE, RECOVERY_TYPE_NAME);
optionTestExpect(OPTION_TARGET, BOGUS);
optionTestExpect(OPTION_TARGET_TIMELINE);
}
if (testRun(++$iRun, 'invalid string ' . OPTION_PROCESS_MAX))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_PROCESS_MAX, BOGUS);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_PROCESS_MAX);
}
if (testRun(++$iRun, 'invalid float ' . OPTION_PROCESS_MAX))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_PROCESS_MAX, '0.0');
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, '0.0', OPTION_PROCESS_MAX);
}
if (testRun(++$iRun, 'valid ' . OPTION_PROCESS_MAX))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_PROCESS_MAX, '2');
configLoadExpect($oOption, CMD_BACKUP);
}
if (testRun(++$iRun, 'valid thread-max'))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, 'thread-max', '2');
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROCESS_MAX, 2);
}
if (testRun(++$iRun, 'valid float ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionBoolSetTest($oOption, OPTION_TEST);
optionSetTest($oOption, OPTION_TEST_DELAY, '0.25');
configLoadExpect($oOption, CMD_BACKUP);
}
if (testRun(++$iRun, 'valid int ' . OPTION_TEST_DELAY))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionBoolSetTest($oOption, OPTION_TEST);
optionSetTest($oOption, OPTION_TEST_DELAY, 3);
configLoadExpect($oOption, CMD_BACKUP);
}
if (testRun(++$iRun, 'restore valid ' . OPTION_TARGET_TIMELINE))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_TARGET_TIMELINE, 2);
configLoadExpect($oOption, CMD_RESTORE);
}
if (testRun(++$iRun, 'invalid ' . OPTION_BUFFER_SIZE))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_BUFFER_SIZE, '512');
configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_RANGE, '512', OPTION_BUFFER_SIZE);
}
if (testRun(++$iRun, CMD_BACKUP . ' invalid value ' . OPTION_RETENTION_ARCHIVE_TYPE))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RETENTION_ARCHIVE_TYPE, BOGUS);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_RETENTION_ARCHIVE_TYPE);
}
if (testRun(++$iRun, CMD_BACKUP . ' default value ' . OPTION_DEFAULT_RETENTION_ARCHIVE_TYPE . ' for ' .
OPTION_RETENTION_ARCHIVE_TYPE . ' with valid ' . OPTION_RETENTION_FULL))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RETENTION_FULL, 1);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_RETENTION_ARCHIVE, 1);
optionTestExpect(OPTION_RETENTION_FULL, 1);
optionTestExpect(OPTION_RETENTION_DIFF, undef);
# Default is FULL so this test will fail if the default is changed, alerting to the need to update configLoad.
optionTestExpect(OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_FULL);
}
if (testRun(++$iRun, CMD_BACKUP . ' valid value ' . OPTION_RETENTION_ARCHIVE . ' for ' . OPTION_RETENTION_ARCHIVE_TYPE .
' ' . BACKUP_TYPE_DIFF))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RETENTION_DIFF, 1);
optionSetTest($oOption, OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_DIFF);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_RETENTION_ARCHIVE, 1);
optionTestExpect(OPTION_RETENTION_DIFF, 1);
optionTestExpect(OPTION_RETENTION_FULL, undef);
optionTestExpect(OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_DIFF);
}
if (testRun(++$iRun, CMD_BACKUP . ' warn no valid value ' . OPTION_RETENTION_ARCHIVE . ' for ' . OPTION_RETENTION_ARCHIVE_TYPE .
' ' . BACKUP_TYPE_INCR))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RETENTION_FULL, 2);
optionSetTest($oOption, OPTION_RETENTION_DIFF, 1);
optionSetTest($oOption, OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_INCR);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_RETENTION_ARCHIVE, undef);
optionTestExpect(OPTION_RETENTION_ARCHIVE_TYPE, BACKUP_TYPE_INCR);
}
if (testRun(++$iRun, CMD_BACKUP . ' invalid value ' . OPTION_PROTOCOL_TIMEOUT))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_DB_TIMEOUT, 5);
optionSetTest($oOption, OPTION_PROTOCOL_TIMEOUT, 4);
configLoadExpect(
$oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, 4, OPTION_PROTOCOL_TIMEOUT,
"'protocol-timeout' option should be greater than 'db-timeout' option");
}
if (testRun(++$iRun, CMD_RESTORE . ' invalid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, '=');
configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_PAIR, '=', OPTION_RESTORE_RECOVERY_OPTION);
}
if (testRun(++$iRun, CMD_RESTORE . ' invalid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, '=' . BOGUS);
configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_PAIR, '=' . BOGUS, OPTION_RESTORE_RECOVERY_OPTION);
}
if (testRun(++$iRun, CMD_RESTORE . ' invalid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, BOGUS . '=');
configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_PAIR, BOGUS . '=', OPTION_RESTORE_RECOVERY_OPTION);
}
if (testRun(++$iRun, CMD_RESTORE . ' valid value ' . OPTION_RESTORE_RECOVERY_OPTION))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_RESTORE_RECOVERY_OPTION, 'primary-conn-info=db.domain.net');
configLoadExpect($oOption, CMD_RESTORE);
optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, 'db.domain.net', 'primary-conn-info');
}
if (testRun(++$iRun, CMD_RESTORE . ' values passed to ' . CMD_ARCHIVE_GET))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db path/main');
optionSetTest($oOption, OPTION_REPO_PATH, '/repo');
optionSetTest($oOption, OPTION_BACKUP_HOST, 'db.mydomain.com');
configLoadExpect($oOption, CMD_RESTORE);
my $strCommand = commandWrite(CMD_ARCHIVE_GET);
my $strExpectedCommand = abs_path($0) . " --backup-host=db.mydomain.com \"--db-path=/db path/main\"" .
" --repo-path=/repo --stanza=main " . CMD_ARCHIVE_GET;
if ($strCommand ne $strExpectedCommand)
{
confess "expected command '${strExpectedCommand}' but got '${strCommand}'";
}
}
if (testRun(++$iRun, CMD_BACKUP . ' default value ' . OPTION_DB_CMD))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_HOST, 'db');
optionSetTest($oOption, OPTION_DB_PATH, '/db');
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_DB_CMD, abs_path($0));
}
if (testRun(++$iRun, CMD_BACKUP . ' missing option ' . OPTION_DB_PATH))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_REQUIRED, OPTION_DB_PATH, 'does this stanza exist?');
}
if (testRun(++$iRun, CMD_BACKUP . ' automatically increase ' . OPTION_PROTOCOL_TIMEOUT))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_DB_TIMEOUT, OPTION_DEFAULT_PROTOCOL_TIMEOUT + 30);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROTOCOL_TIMEOUT, OPTION_DEFAULT_PROTOCOL_TIMEOUT + 60);
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test mixed command-line/config
#-------------------------------------------------------------------------------------------------------------------------------
$strThisTest = 'config';
if ($strTest eq 'all' || $strTest eq $strThisTest)
{
$iRun = 0;
if (!$bVmOut)
{
&log(INFO, "Test ${strThisTest}\n");
}
if (testRun(++$iRun, 'set and negate option ' . OPTION_CONFIG))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, '/dude/dude.conf');
optionBoolSetTest($oOption, OPTION_CONFIG, false);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_NEGATE, OPTION_CONFIG);
}
if (testRun(++$iRun, 'option ' . OPTION_CONFIG))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionBoolSetTest($oOption, OPTION_CONFIG, false);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_CONFIG);
}
if (testRun(++$iRun, 'default option ' . OPTION_CONFIG))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_CONFIG, OPTION_DEFAULT_CONFIG);
}
if (testRun(++$iRun, 'config file is a path'))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strTestPath);
configLoadExpect($oOption, CMD_BACKUP, ERROR_FILE_INVALID, $strTestPath);
}
if (testRun(++$iRun, 'load from config stanza command section - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{"${strStanza}:" . &CMD_BACKUP}{&OPTION_PROCESS_MAX} = 2;
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROCESS_MAX, 2);
}
if (testRun(++$iRun, 'load from config stanza section - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{$strStanza}{&OPTION_PROCESS_MAX} = 3;
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROCESS_MAX, 3);
}
if (testRun(++$iRun, 'load from config global command section - option thread-max'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{'thread-max'} = 2;
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROCESS_MAX, 2);
}
if (testRun(++$iRun, 'load from config global section - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_PROCESS_MAX} = 5;
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROCESS_MAX, 5);
}
if (testRun(++$iRun, 'default - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROCESS_MAX, 1);
}
if (testRun(++$iRun, 'command-line override - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_PROCESS_MAX} = 9;
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_PROCESS_MAX, 7);
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_PROCESS_MAX, 7);
}
if (testRun(++$iRun, 'invalid boolean - option ' . OPTION_HARDLINK))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{&OPTION_HARDLINK} = 'Y';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, 'Y', OPTION_HARDLINK);
}
if (testRun(++$iRun, 'invalid value - option ' . OPTION_LOG_LEVEL_CONSOLE))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_LOG_LEVEL_CONSOLE} = BOGUS;
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_INVALID_VALUE, BOGUS, OPTION_LOG_LEVEL_CONSOLE);
}
if (testRun(++$iRun, 'valid value - option ' . OPTION_LOG_LEVEL_CONSOLE))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_LOG_LEVEL_CONSOLE} = lc(INFO);
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_RESTORE);
}
if (testRun(++$iRun, 'archive-push - option ' . OPTION_LOG_LEVEL_CONSOLE))
{
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_ARCHIVE_PUSH);
}
if (testRun(++$iRun, CMD_EXPIRE . ' ' . OPTION_RETENTION_FULL))
{
$oConfig = {};
$$oConfig{"${strStanza}:" . &CMD_EXPIRE}{&OPTION_RETENTION_FULL} = 2;
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_EXPIRE);
optionTestExpect(OPTION_RETENTION_FULL, 2);
}
if (testRun(++$iRun, CMD_BACKUP . ' option ' . OPTION_COMPRESS))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{&OPTION_COMPRESS} = 'n';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_COMPRESS, false);
}
if (testRun(++$iRun, CMD_RESTORE . ' global option ' . OPTION_RESTORE_RECOVERY_OPTION . ' error'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} = 'bogus=';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_VALUE, 'bogus=', OPTION_RESTORE_RECOVERY_OPTION);
}
if (testRun(++$iRun, CMD_RESTORE . ' global option ' . OPTION_RESTORE_RECOVERY_OPTION . ' error'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} = '=bogus';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_RESTORE, ERROR_OPTION_INVALID_VALUE, '=bogus', OPTION_RESTORE_RECOVERY_OPTION);
}
if (testRun(++$iRun, CMD_RESTORE . ' global option ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} =
'archive-command=/path/to/pgbackrest';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_RESTORE);
optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, '/path/to/pgbackrest', 'archive-command');
}
if (testRun(++$iRun, CMD_RESTORE . ' stanza option ' . OPTION_RESTORE_RECOVERY_OPTION))
{
$oConfig = {};
$$oConfig{$strStanza}{&OPTION_RESTORE_RECOVERY_OPTION} = ['standby-mode=on', 'a=b'];
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_RESTORE);
optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, 'b', 'a');
optionTestExpect(OPTION_RESTORE_RECOVERY_OPTION, 'on', 'standby-mode');
}
if (testRun(++$iRun, CMD_BACKUP . ' option ' . OPTION_DB_PATH))
{
$oConfig = {};
$$oConfig{$strStanza}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_DB_PATH, '/path/to/db');
}
if (testRun(++$iRun, CMD_BACKUP . ' option ' . OPTION_BACKUP_ARCHIVE_CHECK))
{
$oConfig = {};
$$oConfig{$strStanza}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
optionBoolSetTest($oOption, OPTION_BACKUP_ARCHIVE_CHECK, false);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_ONLINE, true);
optionTestExpect(OPTION_BACKUP_ARCHIVE_CHECK, false);
}
if (testRun(++$iRun, CMD_ARCHIVE_PUSH . ' option ' . OPTION_DB_PATH))
{
$oConfig = {};
$$oConfig{$strStanza}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_ARCHIVE_PUSH);
optionTestExpect(OPTION_DB_PATH, '/path/to/db');
}
if (testRun(++$iRun, CMD_BACKUP . ' option ' . OPTION_REPO_PATH))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_REPO_PATH} = '/repo';
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP);
optionTestExpect(OPTION_REPO_PATH, '/repo');
}
if (testRun(++$iRun, CMD_BACKUP . ' option ' . OPTION_REPO_PATH . ' multiple times'))
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_REPO_PATH} = ['/repo', '/repo2'];
iniSave($strConfigFile, $oConfig, true);
optionSetTest($oOption, OPTION_STANZA, $strStanza);
optionSetTest($oOption, OPTION_DB_PATH, '/db');
optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
configLoadExpect($oOption, CMD_BACKUP, ERROR_OPTION_MULTIPLE_VALUE, OPTION_REPO_PATH);
}
# Cleanup
testCleanup();
}
}
our @EXPORT = qw(configTestRun);
1;

View File

@@ -0,0 +1,123 @@
####################################################################################################################################
# FileCommonTest.pm - Common code for File tests
####################################################################################################################################
package pgBackRestTest::File::FileCommonTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::FileCommon;
use pgBackRest::Protocol::Common;
use pgBackRest::Protocol::RemoteMaster;
use pgBackRestTest::Common::Host::HostBackupTest;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# init
#
# Common objects and variables used by all tests.
####################################################################################################################################
sub init
{
my $self = shift;
# Create the repo path so the remote won't complain that it's missing
my $strRepoPath = $self->testPath() . '/repo';
mkdir($strRepoPath, oct('0770'))
or confess "Unable to create repo directory: ${strRepoPath}";
# Create local
$self->{oLocal} = new pgBackRest::Protocol::Common(
262144,
1,
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK,
HOST_PROTOCOL_TIMEOUT);
# Create remote
$self->{oRemote} = new pgBackRest::Protocol::RemoteMaster(
BACKUP,
OPTION_DEFAULT_CMD_SSH,
$self->backrestExe() . ' --stanza=' . $self->stanza() .
" --type=backup --repo-path=${strRepoPath} --no-config --command=test remote",
262144,
OPTION_DEFAULT_COMPRESS_LEVEL,
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK,
$self->host(),
$self->backrestUser(),
HOST_PROTOCOL_TIMEOUT);
}
####################################################################################################################################
# final
#
# Close objects created for tests.
####################################################################################################################################
sub final
{
my $self = shift;
$self->remote()->close();
}
####################################################################################################################################
# setup
#
# Setup directories for file tests.
####################################################################################################################################
sub setup
{
my $self = shift;
my $bRemote = shift;
my $bPrivate = shift;
# Remove the backrest private directory
if (fileExists($self->testPath() . '/private'))
{
executeTest(
'ssh ' . $self->backrestUser() . '\@' . $self->host() . ' rm -rf ' . $self->testPath() . '/private',
{bSuppressStdErr => true});
}
# Remove contents of the test directory
executeTest('rm -rf ' . $self->testPath() . '/*');
# Create the private directories
if (defined($bPrivate) && $bPrivate)
{
executeTest(
'ssh ' . $self->backrestUser() . '\@' . $self->host() . ' mkdir -m 700 ' . $self->testPath() . '/backrest_private',
{bSuppressStdErr => true});
executeTest('mkdir -m 700 ' . $self->testPath() . '/user_private');
}
# Create the file object
my $oFile = new pgBackRest::File
(
$self->stanza(),
$self->testPath(),
$bRemote ? $self->remote() : $self->local()
);
return $oFile;
}
####################################################################################################################################
# Getters
####################################################################################################################################
sub host {return '127.0.0.1'}
sub local {return shift->{oLocal}}
sub remote {return shift->{oRemote}}
1;

View File

@@ -0,0 +1,101 @@
####################################################################################################################################
# FileCompressTest.pm - Tests for File->compress()
####################################################################################################################################
package pgBackRestTest::File::FileCompressTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote ??? enable remote tests and have them throw an error
foreach my $bRemote (false)
{
# Loop through exists
foreach my $bExists (false, true)
{
# Loop through error
foreach my $bError (false, true)
{
if (!$self->begin("rmt ${bRemote}, exists ${bExists}, err ${bError}")) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bError);
my $strFile = $self->testPath() . '/test.txt';
my $strSourceHash;
my $iSourceSize;
if ($bError)
{
$strFile = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . '_private/test.txt';
}
elsif ($bExists)
{
executeTest("echo 'TESTDATA' > ${strFile}");
($strSourceHash, $iSourceSize) = $oFile->hashSize(PATH_BACKUP_ABSOLUTE, $strFile);
}
# Execute in eval in case of error
eval
{
$oFile->compress(PATH_BACKUP_ABSOLUTE, $strFile);
return true;
}
or do
{
if (!$bExists || $bError)
{
next;
}
confess $EVAL_ERROR;
};
if (!$bExists || $bError)
{
confess 'expected error';
}
my $strDestinationFile = $strFile . '.gz';
if (-e $strFile)
{
confess 'source file still exists';
}
unless (-e $strDestinationFile)
{
confess 'file was not compressed';
}
executeTest("gzip -d ${strDestinationFile}");
my ($strDestinationHash, $iDestinationSize) = $oFile->hashSize(PATH_BACKUP_ABSOLUTE, $strFile);
if ($strSourceHash ne $strDestinationHash)
{
confess "source ${strSourceHash} and destination ${strDestinationHash} file hashes do not match";
}
}
}
}
}
1;

View File

@@ -0,0 +1,244 @@
####################################################################################################################################
# FileCopyTest.pm - Tests for File->copy()
####################################################################################################################################
package pgBackRestTest::File::FileCopyTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through small/large
for (my $iLarge = 0; $iLarge <= 3; $iLarge++)
{
# Loop through possible remotes
foreach my $strRemote ($iLarge ? (undef, 'db') : (undef, 'backup', 'db'))
{
# Loop through source path types
foreach my $strSourcePathType ($iLarge ? (PATH_BACKUP_ABSOLUTE) : (PATH_BACKUP_ABSOLUTE, PATH_DB_ABSOLUTE))
{
# Loop through destination path types
foreach my $strDestinationPathType ($iLarge ? (PATH_DB_ABSOLUTE) : (PATH_BACKUP_ABSOLUTE, PATH_DB_ABSOLUTE))
{
# Loop through source missing/present
foreach my $bSourceMissing ($iLarge ? (false) : (false, true))
{
# Loop through source ignore/require
foreach my $bSourceIgnoreMissing ($bSourceMissing ? (false, true) : (false))
{
# Loop through checksum append
foreach my $bChecksumAppend ($bSourceMissing || $iLarge ? (false) : (false, true))
{
# Loop through source compression
foreach my $bSourceCompressed ($bSourceMissing ? (false) : (false, true))
{
# Loop through destination compression
foreach my $bDestinationCompress ($bSourceMissing ? (false) : (false, true))
{
my $strSourcePath = $strSourcePathType eq PATH_DB_ABSOLUTE ? 'db' : 'backup';
my $strDestinationPath = $strDestinationPathType eq PATH_DB_ABSOLUTE ? 'db' : 'backup';
if (!$self->begin(
"lrg ${iLarge}, rmt " .
(defined($strRemote) && ($strRemote eq $strSourcePath || $strRemote eq $strDestinationPath) ? 1 : 0) .
', srcpth ' . (defined($strRemote) && $strRemote eq $strSourcePath ? 'rmt' : 'lcl') .
":${strSourcePath}, srcmiss ${bSourceMissing}, srcignmiss ${bSourceIgnoreMissing}, srccmp $bSourceCompressed, " .
'dstpth ' . (defined($strRemote) && $strRemote eq $strDestinationPath ? 'rmt' : 'lcl') .
":${strDestinationPath}, chkapp ${bChecksumAppend}, dstcmp $bDestinationCompress")) {next}
# Setup test directory and get file object
my $oFile = $self->setup(defined($strRemote), false);
executeTest('mkdir ' . $self->testPath() . '/backup');
executeTest('mkdir ' . $self->testPath() . '/db');
my $strSourceFile = $self->testPath() . "/${strSourcePath}/test-source";
my $strDestinationFile = $self->testPath() . "/${strDestinationPath}/test-destination";
my $strCopyHash;
my $iCopySize;
# Create the compressed or uncompressed test file
my $strSourceHash;
my $iSourceSize;
if (!$bSourceMissing)
{
if ($iLarge)
{
$strSourceFile .= '.bin';
$strDestinationFile .= '.bin';
if ($iLarge < 3)
{
executeTest('cp ' . $self->dataPath() . "/filecopy.archive${iLarge}.bin ${strSourceFile}");
}
else
{
for (my $iTableSizeIdx = 0; $iTableSizeIdx < 25; $iTableSizeIdx++)
{
executeTest('cat ' . $self->dataPath() . "/filecopy.table.bin >> ${strSourceFile}");
}
}
}
else
{
$strSourceFile .= '.txt';
$strDestinationFile .= '.txt';
system("echo 'TESTDATA' > ${strSourceFile}");
}
if ($iLarge == 1)
{
$strSourceHash = '4518a0fdf41d796760b384a358270d4682589820';
$iSourceSize = 16777216;
}
elsif ($iLarge == 2)
{
$strSourceHash = '1c7e00fd09b9dd11fc2966590b3e3274645dd031';
$iSourceSize = 16777216;
}
elsif ($iLarge == 3)
{
$strSourceHash = 'b2055a6ba15bf44359c18fbbf23c68b50a670eb0';
$iSourceSize = 26214400;
}
else
{
$strSourceHash = '06364afe79d801433188262478a76d19777ef351';
$iSourceSize = 9;
}
if ($bSourceCompressed)
{
system("gzip ${strSourceFile}");
$strSourceFile .= '.gz';
}
}
if ($bDestinationCompress)
{
$strDestinationFile .= '.gz';
}
# Run file copy in an eval block because some errors are expected
my $bReturn;
eval
{
($bReturn, $strCopyHash, $iCopySize) =
$oFile->copy($strSourcePathType, $strSourceFile,
$strDestinationPathType, $strDestinationFile,
$bSourceCompressed, $bDestinationCompress,
$bSourceIgnoreMissing, undef, '0770', false, undef, undef,
$bChecksumAppend);
return true;
}
# Check for errors after copy
or do
{
my $oException = $EVAL_ERROR;
if (isException($oException))
{
if ($bSourceMissing && !$bSourceIgnoreMissing)
{
next;
}
confess $oException;
}
confess $oException;
};
if ($bSourceMissing)
{
if ($bSourceIgnoreMissing)
{
if ($bReturn)
{
confess 'copy() returned ' . $bReturn . ' when ignore missing set';
}
next;
}
confess 'expected source file missing error';
}
if (!defined($strCopyHash))
{
confess 'copy hash must be defined';
}
if ($bChecksumAppend)
{
if ($bDestinationCompress)
{
$strDestinationFile =
substr($strDestinationFile, 0, length($strDestinationFile) -3) . "-${strSourceHash}.gz";
}
else
{
$strDestinationFile .= '-' . $strSourceHash;
}
}
unless (-e $strDestinationFile)
{
confess "could not find destination file ${strDestinationFile}";
}
my $strDestinationTest = $strDestinationFile;
if ($bDestinationCompress)
{
$strDestinationTest = substr($strDestinationFile, 0, length($strDestinationFile) - 3) . '.test';
system("gzip -dc ${strDestinationFile} > ${strDestinationTest}") == 0
or die "could not decompress ${strDestinationFile}";
}
my ($strDestinationHash, $iDestinationSize) = $oFile->hashSize(PATH_ABSOLUTE, $strDestinationTest);
if ($strSourceHash ne $strDestinationHash || $strSourceHash ne $strCopyHash)
{
confess
"source ${strSourceHash}, copy ${strCopyHash} and destination ${strDestinationHash} file hashes do not match";
}
if ($iSourceSize != $iDestinationSize || $iSourceSize != $iCopySize)
{
confess "source ${iSourceSize}, copy ${iCopySize} and destination ${iDestinationSize} sizes do not match";
}
}
}
}
}
}
}
}
}
}
}
1;

View File

@@ -0,0 +1,91 @@
####################################################################################################################################
# FileExistsTest.pm - Tests for File->exists()
####################################################################################################################################
package pgBackRestTest::File::FileExistsTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote
foreach my $bRemote (false, true)
{
# Loop through exists
foreach my $bExists (false, true)
{
# Loop through exists
foreach my $bError ($bExists ? (false, true) : (false))
{
if (!$self->begin("rmt ${bRemote}, err ${bError}, exists ${bExists}")) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bError);
my $strFile = $self->testPath() . '/test.txt';
if ($bError)
{
$strFile = $self->testPath() . '/private/test.txt';
}
elsif ($bExists)
{
executeTest("echo 'TESTDATA' > ${strFile}");
}
# Execute in eval in case of error
eval
{
if ($oFile->exists(PATH_BACKUP_ABSOLUTE, $strFile) != $bExists)
{
confess "bExists is set to ${bExists}, but exists() returned " . !$bExists;
}
return true;
}
or do
{
my $oException = $@;
my $iCode;
my $strMessage;
if (isException($oException))
{
$iCode = $oException->code();
$strMessage = $oException->message();
}
else
{
$strMessage = $oException;
}
if ($bError)
{
next;
}
confess 'error raised: ' . $strMessage . "\n";
};
}
}
}
}
1;

View File

@@ -0,0 +1,100 @@
####################################################################################################################################
# FileHashTest.pm - Tests for File->hash()
####################################################################################################################################
package pgBackRestTest::File::FileHashTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote
foreach my $bRemote (false, true)
{
# Loop through error
foreach my $bError (false, true)
{
# Loop through exists
foreach my $bExists (false, true)
{
# Loop through exists
foreach my $bCompressed (false, true)
{
if (!$self->begin("rmt ${bRemote}, err ${bError}, exists ${bExists}, cmp ${bCompressed}")) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bError);
my $strFile = $self->testPath() . '/test.txt';
if ($bError)
{
$strFile = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . '_private/test.txt';
}
elsif (!$bExists)
{
$strFile = $self->testPath() . '/error.txt';
}
else
{
executeTest("echo 'TESTDATA' > ${strFile}");
if ($bCompressed && !$bRemote)
{
$oFile->compress(PATH_BACKUP_ABSOLUTE, $strFile);
$strFile = $strFile . '.gz';
}
}
# Execute in eval in case of error
my $strHash;
my $iSize;
my $bErrorExpected = !$bExists || $bError || $bRemote;
eval
{
($strHash, $iSize) = $oFile->hashSize(PATH_BACKUP_ABSOLUTE, $strFile, $bCompressed);
return true;
}
or do
{
if ($bErrorExpected)
{
next;
}
confess $EVAL_ERROR;
};
if ($bErrorExpected)
{
confess 'error was expected';
}
if ($strHash ne '06364afe79d801433188262478a76d19777ef351')
{
confess 'hashes do not match';
}
}
}
}
}
}
1;

View File

@@ -0,0 +1,122 @@
####################################################################################################################################
# FileListTest.pm - Tests for File->list()
####################################################################################################################################
package pgBackRestTest::File::FileListTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote
foreach my $bRemote (false, true)
{
# Loop through sort
foreach my $strSort ('reverse', undef)
{
# Loop through expression
foreach my $strExpression (undef, "^test2\\..*\$", "^du\$")
{
# Loop through exists
foreach my $bExists (false, true)
{
# Loop through ignore missing
for (my $bIgnoreMissing = false; $bIgnoreMissing <= $bExists; $bIgnoreMissing++)
{
# Loop through error
foreach my $bError (false, true)
{
if (!$self->begin(
"rmt ${bRemote}, err ${bError}, exist ${bExists}, ignmis ${bIgnoreMissing}, " .
'exp ' . (defined($strExpression) ? $strExpression : '[undef]') . ', ' .
'srt ' . (defined($strSort) ? $strSort : '[undef]'))) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bError);
my $strPath = $self->testPath();
if ($bError)
{
$strPath = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . '_private';
}
elsif (!$bExists)
{
$strPath = $self->testPath() . '/error';
}
else
{
executeTest("echo 'TESTDATA' > ${strPath}/test.txt");
executeTest("echo 'TESTDATA2' > ${strPath}/test2.txt");
}
my @stryFileCompare = split(/\n/, "test.txt\ntest2.txt");
# Execute in eval in case of error
my @stryFileList;
my $bErrorExpected = (!$bExists && !$bIgnoreMissing) || $bError;
eval
{
@stryFileList = $oFile->list(PATH_BACKUP_ABSOLUTE, $strPath, $strExpression, $strSort, $bIgnoreMissing);
return true;
}
or do
{
if ($bErrorExpected)
{
next;
}
confess $EVAL_ERROR;
};
if ($bErrorExpected)
{
confess 'error was expected';
}
# Validate the list
if (defined($strExpression))
{
@stryFileCompare = grep(/$strExpression/i, @stryFileCompare);
}
if (defined($strSort))
{
@stryFileCompare = sort {$b cmp $a} @stryFileCompare;
}
my $strFileList = sprintf("@stryFileList");
my $strFileCompare = sprintf("@stryFileCompare");
if ($strFileList ne $strFileCompare)
{
confess "list (${strFileList})[" . @stryFileList .
"] does not match compare (${strFileCompare})[" . @stryFileCompare . ']';
}
}
}
}
}
}
}
}
1;

View File

@@ -0,0 +1,162 @@
####################################################################################################################################
# FileManifestTest.pm - Tests for File->manifest()
####################################################################################################################################
package pgBackRestTest::File::FileManifestTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
my $strManifestCompare =
'.,d,' . $self->pgUser() . ',' . $self->group() . ",0770,,,,\n" .
'sub1,d,' . $self->pgUser() . ',' . $self->group() . ",0750,,,,\n" .
'sub1/sub2,d,' . $self->pgUser() . ',' . $self->group() . ",0750,,,,\n" .
'sub1/sub2/test,l,' . $self->pgUser() . ',' . $self->group() . ",,,,,../..\n" .
'sub1/sub2/test-hardlink.txt,f,' . $self->pgUser() . ',' . $self->group() . ",1640,1111111111,0,9,\n" .
'sub1/sub2/test-sub2.txt,f,' . $self->pgUser() . ',' . $self->group() . ",0666,1111111113,0,11,\n" .
'sub1/test,l,' . $self->pgUser() . ',' . $self->group() . ",,,,,..\n" .
'sub1/test-hardlink.txt,f,' . $self->pgUser() . ',' . $self->group() . ",1640,1111111111,0,9,\n" .
'sub1/test-sub1.txt,f,' . $self->pgUser() . ',' . $self->group() . ",0646,1111111112,0,10,\n" .
'test.txt,f,' . $self->pgUser() . ',' . $self->group() . ",1640,1111111111,0,9,";
# Loop through local/remote
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
for (my $bError = 0; $bError <= 1; $bError++)
{
for (my $bExists = 0; $bExists <= 1; $bExists++)
{
if (!$self->begin("rmt ${bRemote}, exists ${bExists}, err ${bError}")) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bError);
# Setup test data
executeTest('mkdir -m 750 ' . $self->testPath() . '/sub1');
executeTest('mkdir -m 750 ' . $self->testPath() . '/sub1/sub2');
executeTest("echo 'TESTDATA' > " . $self->testPath() . '/test.txt');
utime(1111111111, 1111111111, $self->testPath() . '/test.txt');
executeTest('chmod 1640 ' . $self->testPath() . '/test.txt');
executeTest("echo 'TESTDATA_' > ". $self->testPath() . '/sub1/test-sub1.txt');
utime(1111111112, 1111111112, $self->testPath() . '/sub1/test-sub1.txt');
executeTest('chmod 0640 ' . $self->testPath() . '/sub1/test-sub1.txt');
executeTest("echo 'TESTDATA__' > " . $self->testPath() . '/sub1/sub2/test-sub2.txt');
utime(1111111113, 1111111113, $self->testPath() . '/sub1/sub2/test-sub2.txt');
executeTest('chmod 0646 ' . $self->testPath() . '/sub1/test-sub1.txt');
executeTest('ln ' . $self->testPath() . '/test.txt ' . $self->testPath() . '/sub1/test-hardlink.txt');
executeTest('ln ' . $self->testPath() . '/test.txt ' . $self->testPath() . '/sub1/sub2/test-hardlink.txt');
executeTest('ln -s .. ' . $self->testPath() . '/sub1/test');
executeTest('chmod 0700 ' . $self->testPath() . '/sub1/test');
executeTest('ln -s ../.. ' . $self->testPath() . '/sub1/sub2/test');
executeTest('chmod 0750 ' . $self->testPath() . '/sub1/sub2/test');
executeTest('chmod 0770 ' . $self->testPath());
# Create path
my $strPath = $self->testPath();
if ($bError)
{
$strPath = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . '_private';
}
elsif (!$bExists)
{
$strPath = $self->testPath() . '/error';
}
# Execute in eval in case of error
my $hManifest;
my $bErrorExpected = !$bExists || $bError;
eval
{
$hManifest = $oFile->manifest(PATH_BACKUP_ABSOLUTE, $strPath);
return true;
}
# Check for an error
or do
{
if ($bErrorExpected)
{
next;
}
confess $EVAL_ERROR;
};
# Check for an expected error
if ($bErrorExpected)
{
confess 'error was expected';
}
my $strManifest;
# Validate the manifest
foreach my $strName (sort(keys(%{$hManifest})))
{
if (!defined($strManifest))
{
$strManifest = '';
}
else
{
$strManifest .= "\n";
}
if (defined($hManifest->{$strName}{inode}))
{
$hManifest->{$strName}{inode} = 0;
}
$strManifest .=
"${strName}," .
$hManifest->{$strName}{type} . ',' .
(defined($hManifest->{$strName}{user}) ?
$hManifest->{$strName}{user} : '') . ',' .
(defined($hManifest->{$strName}{group}) ?
$hManifest->{$strName}{group} : '') . ',' .
(defined($hManifest->{$strName}{mode}) ?
$hManifest->{$strName}{mode} : '') . ',' .
(defined($hManifest->{$strName}{modification_time}) ?
$hManifest->{$strName}{modification_time} : '') . ',' .
(defined($hManifest->{$strName}{inode}) ?
$hManifest->{$strName}{inode} : '') . ',' .
(defined($hManifest->{$strName}{size}) ?
$hManifest->{$strName}{size} : '') . ',' .
(defined($hManifest->{$strName}{link_destination}) ?
$hManifest->{$strName}{link_destination} : '');
}
if ($strManifest ne $strManifestCompare)
{
confess "manifest is not equal:\n\n${strManifest}\n\ncompare:\n\n${strManifestCompare}\n\n";
}
}
}
}
}
1;

View File

@@ -0,0 +1,107 @@
####################################################################################################################################
# FileMoveTest.pm - Tests for File->move()
####################################################################################################################################
package pgBackRestTest::File::FileMoveTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote ??? enable remote tests and have them throw an error
for (my $bRemote = 0; $bRemote <= 0; $bRemote++)
{
# Loop through source exists
for (my $bSourceExists = 0; $bSourceExists <= 1; $bSourceExists++)
{
# Loop through source errors
for (my $bSourceError = 0; $bSourceError <= 1; $bSourceError++)
{
# Loop through destination exists
for (my $bDestinationExists = 0; $bDestinationExists <= 1; $bDestinationExists++)
{
# Loop through source errors
for (my $bDestinationError = 0; $bDestinationError <= 1; $bDestinationError++)
{
# Loop through create
for (my $bCreate = 0; $bCreate <= $bDestinationExists; $bCreate++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin(
"src_exists ${bSourceExists}, src_error ${bSourceError}, " .
", dst_exists ${bDestinationExists}, dst_error ${bDestinationError}, dst_create ${bCreate}")) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bSourceError || $bDestinationError);
my $strSourceFile = $self->testPath() . '/test.txt';
my $strDestinationFile = $self->testPath() . '/test-dest.txt';
if ($bSourceError)
{
$strSourceFile = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . "_private/test.txt";
}
elsif ($bSourceExists)
{
executeTest("echo 'TESTDATA' > ${strSourceFile}");
}
if ($bDestinationError)
{
$strDestinationFile = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . "_private/test.txt";
}
elsif (!$bDestinationExists)
{
$strDestinationFile = $self->testPath() . '/sub/test-dest.txt';
}
# Execute in eval in case of error
eval
{
$oFile->move(PATH_BACKUP_ABSOLUTE, $strSourceFile, PATH_BACKUP_ABSOLUTE, $strDestinationFile, $bCreate);
return true;
}
or do
{
if (!$bSourceExists || (!$bDestinationExists && !$bCreate) || $bSourceError || $bDestinationError)
{
next;
}
confess $EVAL_ERROR;
};
if (!$bSourceExists || (!$bDestinationExists && !$bCreate) || $bSourceError || $bDestinationError)
{
confess 'error should have been raised';
}
unless (-e $strDestinationFile)
{
confess 'file was not moved';
}
}
}
}
}
}
}
}
1;

View File

@@ -0,0 +1,115 @@
####################################################################################################################################
# FilePathCreateTest.pm - Tests for File->pathCreate()
####################################################################################################################################
package pgBackRestTest::File::FilePathCreateTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Fcntl qw(:mode);
use File::stat;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRest::FileCommon;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
# Loop through error
for (my $bError = 0; $bError <= 1; $bError++)
{
# Loop through mode (mode will be set on true)
foreach my $strMode (undef, '0700')
{
my $strPathType = PATH_BACKUP_CLUSTER;
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin("rmt ${bRemote}, err ${bError}, mode " . (defined($strMode) ? $strMode : 'undef'))) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bError);
filePathCreate($self->testPath() . '/backup', '770');
filePathCreate($self->testPath() . '/backup/db', '770');
my $strPath = 'path';
# If not exists then set the path to something bogus
if ($bError)
{
$strPath = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . "_private/path";
$strPathType = PATH_BACKUP_ABSOLUTE;
}
# Execute in eval to catch errors
my $bErrorExpected = $bError;
eval
{
$oFile->pathCreate($strPathType, $strPath, $strMode);
return true;
}
# Check for errors
or do
{
# Ignore errors if the path did not exist
if ($bErrorExpected)
{
next;
}
confess $EVAL_ERROR;
};
if ($bErrorExpected)
{
confess 'error was expected';
}
# Make sure the path was actually created
my $strPathCheck = $oFile->pathGet($strPathType, $strPath);
unless (-e $strPathCheck)
{
confess 'path was not created';
}
# Check that the mode was set correctly
my $oStat = lstat($strPathCheck);
if (!defined($oStat))
{
confess "unable to stat ${strPathCheck}";
}
if (defined($strMode))
{
if ($strMode ne sprintf('%04o', S_IMODE($oStat->mode)))
{
confess "mode was not set to {$strMode}";
}
}
}
}
}
}
1;

View File

@@ -0,0 +1,112 @@
####################################################################################################################################
# FileRemoveTest.pm - Tests for File->remove()
####################################################################################################################################
package pgBackRestTest::File::FileRemoveTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote
foreach my $bRemote (false, true)
{
# Loop through exists
foreach my $bError (false, true)
{
# Loop through exists
foreach my $bExists (false, true)
{
# Loop through temp
foreach my $bTemp (false, true)
{
# Loop through ignore missing
foreach my $bIgnoreMissing (false, true)
{
if (!$self->begin(
"rmt ${bRemote}, err = ${bError}, exists ${bExists}, tmp ${bTemp}, ignmis ${bIgnoreMissing}")) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote, $bError);
my $strFile = $self->testPath() . '/test.txt';
if ($bError)
{
$strFile = $self->testPath() . '/' . ($bRemote ? 'user' : 'backrest') . '_private/test.txt';
}
elsif (!$bExists)
{
$strFile = $self->testPath() . '/private/error.txt';
}
else
{
executeTest("echo 'TESTDATA' > ${strFile}" . ($bTemp ? '.pgbackrest.tmp' : ''));
}
# Execute in eval in case of error
my $bRemoved;
eval
{
$bRemoved = $oFile->remove(PATH_BACKUP_ABSOLUTE, $strFile, $bTemp, $bIgnoreMissing);
return true;
}
or do
{
if ($bError || $bRemote)
{
next;
}
if (!$bExists && !$bIgnoreMissing)
{
next;
}
confess $EVAL_ERROR;
};
if ($bError || $bRemote)
{
confess 'error should have been returned';
}
if (!$bRemoved)
{
if (!$bExists && $bIgnoreMissing)
{
next;
}
confess 'remove returned false, but something should have been removed';
}
if (-e ($strFile . ($bTemp ? '.pgbackrest.tmp' : '')))
{
confess 'file still exists';
}
}
}
}
}
}
}
1;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
####################################################################################################################################
# FileWaitTest.pm - Tests for File->wait()
####################################################################################################################################
package pgBackRestTest::File::FileWaitTest;
use parent 'pgBackRestTest::File::FileCommonTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use POSIX qw(ceil);
use Time::HiRes qw(gettimeofday usleep);
use pgBackRest::Common::Log;
use pgBackRest::File;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Loop through local/remote
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
my $lTimeBegin = gettimeofday();
if (!$self->begin("rmt ${bRemote}, begin ${lTimeBegin}")) {next}
# Setup test directory and get file object
my $oFile = $self->setup($bRemote);
# If there is not enough time to complete the test then sleep
if (ceil($lTimeBegin) - $lTimeBegin < .250)
{
my $lSleepMs = ceil(((int($lTimeBegin) + 1) - $lTimeBegin) * 1000);
usleep($lSleepMs * 1000);
&log(DEBUG, "slept ${lSleepMs}ms: begin ${lTimeBegin}, end " . gettimeofday());
$lTimeBegin = gettimeofday();
}
# Run the test
my $lTimeBeginCheck = $oFile->wait(PATH_DB_ABSOLUTE);
&log(DEBUG, "begin ${lTimeBegin}, check ${lTimeBeginCheck}, end " . time());
# Current time should have advanced by 1 second
if (int(time()) == int($lTimeBegin))
{
confess "time was not advanced by 1 second";
}
# lTimeBegin and lTimeBeginCheck should be equal
if (int($lTimeBegin) != $lTimeBeginCheck)
{
confess 'time begin ' || int($lTimeBegin) || "and check ${lTimeBeginCheck} should be equal";
}
}
}
1;

View File

@@ -0,0 +1,52 @@
####################################################################################################################################
# HelpHelpTest.pm - Tests for help command
####################################################################################################################################
package pgBackRestTest::Help::HelpHelpTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Exporter qw(import);
our @EXPORT = qw();
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRestTest::Common::Host::HostBaseTest;
use pgBackRestTest::Common::ExecuteTest;
####################################################################################################################################
# helpExecute
####################################################################################################################################
sub helpExecute
{
my $self = shift;
my $strCommand = shift;
executeTest($self->backrestExe() . " $strCommand", {oLogTest => $self->expect()});
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Increment the run, log, and decide whether this unit test should be run
if ($self->begin('base'))
{
$self->helpExecute(CMD_VERSION);
$self->helpExecute(CMD_HELP);
$self->helpExecute(CMD_HELP . ' version');
$self->helpExecute(CMD_HELP . ' --output=json --stanza=main info');
$self->helpExecute(CMD_HELP . ' --output=json --stanza=main info output');
}
}
1;

View File

@@ -1,87 +0,0 @@
####################################################################################################################################
# HelpTest.pm - Unit Tests for help
####################################################################################################################################
package pgBackRestTest::Help::HelpTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use Exporter qw(import);
our @EXPORT = qw();
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::CommonTest;
####################################################################################################################################
# helpExecute
####################################################################################################################################
sub helpExecute
{
my $strCommand = shift;
my $oLogTest = shift;
my $oHostGroup = hostGroupGet();
executeTest($oHostGroup->paramGet(HOST_PARAM_BACKREST_EXE) . " $strCommand", {oLogTest => $oLogTest});
}
####################################################################################################################################
# helpTestRun
####################################################################################################################################
sub helpTestRun
{
my $strTest = shift;
my $bVmOut = shift;
# Setup test variables
my $iRun;
my $strModule = 'help';
my $oLogTest = undef;
# Print test banner
if (!$bVmOut)
{
&log(INFO, 'HELP MODULE *****************************************************************');
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test config
#-------------------------------------------------------------------------------------------------------------------------------
my $strThisTest = 'help';
if ($strTest eq 'all' || $strTest eq $strThisTest)
{
$iRun = 0;
if (!$bVmOut)
{
&log(INFO, "Test help\n");
}
# Increment the run, log, and decide whether this unit test should be run
if (testRun(++$iRun, 'base', $strModule, $strThisTest, \$oLogTest))
{
helpExecute(CMD_VERSION, $oLogTest);
helpExecute(CMD_HELP, $oLogTest);
helpExecute(CMD_HELP . ' version', $oLogTest);
helpExecute(CMD_HELP . ' --output=json --stanza=main info', $oLogTest);
helpExecute(CMD_HELP . ' --output=json --stanza=main info output', $oLogTest);
}
# Cleanup
testCleanup(\$oLogTest);
}
}
push @EXPORT, qw(helpTestRun);
1;

View File

@@ -37,20 +37,12 @@ use pgBackRest::Version;
use BackRestDoc::Custom::DocCustomRelease;
use pgBackRestTest::Backup::BackupTest;
use pgBackRestTest::Backup::Common::HostBackupTest;
use pgBackRestTest::Backup::Common::HostBaseTest;
use pgBackRestTest::Backup::Common::HostDbCommonTest;
use pgBackRestTest::Backup::Common::HostDbTest;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::Common::ListTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Common::VmTest;
use pgBackRestTest::CommonTest;
use pgBackRestTest::Config::ConfigTest;
use pgBackRestTest::File::FileTest;
use pgBackRestTest::Help::HelpTest;
####################################################################################################################################
# Usage
@@ -420,7 +412,7 @@ eval
my $fTestElapsedTime = ceil((gettimeofday() - $$oyProcess[$iVmIdx]{start_time}) * 100) / 100;
if (!($iExitStatus == 0 || $iExitStatus == 255))
if ($iExitStatus != 0)
{
&log(ERROR, "${strTestDone} (err${iExitStatus}-${fTestElapsedTime}s)" .
(defined($oExecDone->{strOutLog}) && !$bShowOutputAsync ?
@@ -576,53 +568,20 @@ eval
################################################################################################################################
my $iRun = 0;
# Set parameters in host group
# Create host group for containers
my $oHostGroup = hostGroupGet();
$oHostGroup->paramSet(HOST_PARAM_VM, $strVm);
$oHostGroup->paramSet(HOST_PARAM_VM_ID, $iVmId);
$oHostGroup->paramSet(HOST_PARAM_TEST_PATH, $strTestPath);
$oHostGroup->paramSet(HOST_PARAM_BACKREST_EXE, "${strBackRestBase}/bin/pgbackrest");
$oHostGroup->paramSet(HOST_PARAM_PROCESS_MAX, $iProcessMax);
$oHostGroup->paramSet(HOST_DB_USER, TEST_USER);
$oHostGroup->paramSet(HOST_BACKUP_USER, 'backrest');
if ($strDbVersion ne 'minimal')
{
$oHostGroup->paramSet(HOST_PARAM_DB_VERSION, $strDbVersion);
$oHostGroup->paramSet(HOST_PARAM_DB_BIN_PATH, $strPgSqlBin);
}
if (testSetup($strTestPath, $strPgSqlBin, $iModuleTestRun,
$bDryRun, $bNoCleanup, $bLogForce))
{
if (!$bVmOut &&
($strModule eq 'all' ||
$strModule eq 'backup' && $strModuleTest eq 'all' ||
$strModule eq 'backup' && $strModuleTest eq 'full'))
{
&log(INFO, "TESTING psql-bin = $strPgSqlBin\n");
}
if ($strModule eq 'all' || $strModule eq 'help')
{
helpTestRun($strModuleTest, $bVmOut);
}
if ($strModule eq 'all' || $strModule eq 'config')
{
configTestRun($strModuleTest, $bVmOut);
}
if ($strModule eq 'all' || $strModule eq 'file')
{
fileTestRun($strModuleTest, $bVmOut);
}
if ($strModule eq 'all' || $strModule eq 'backup')
{
backupTestRun($strModuleTest, $bVmOut);
}
}
# Run the test
testRun($strModule, $strModuleTest)->process(
$strVm, $iVmId, # Vm info
$strBackRestBase, # Base backrest directory
$strTestPath, # Path where the tests will run
"${strBackRestBase}/bin/" . BACKREST_EXE, # Path to the backrest executable
$strDbVersion ne 'minimal' ? $strPgSqlBin: undef, # Db bin path
$strDbVersion ne 'minimal' ? $strDbVersion: undef, # Db version
$strModule, $strModuleTest, $iModuleTestRun, # Module info
$iProcessMax, $bVmOut, $bDryRun, $bNoCleanup, $bLogForce, # Test options
TEST_USER, BACKREST_USER, TEST_GROUP); # User/group info
if (!$bNoCleanup)
{