1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-06-12 23:37:43 +02:00
David Steele f83f0fa54d v0.70: Stability improvements for archiving, improved logging and help
* Fixed an issue where archive-copy would fail on an incr/diff backup when hardlink=n.  In this case the pg_xlog path does not already exist and must be created. Reported by Michael Renner

* Allow duplicate WAL segments to be archived when the checksum matches.  This is necessary for some recovery scenarios.

* Allow comments/disabling in pg_backrest.conf using #.  Suggested by Michael Renner.

* Better logging before pg_start_backup() to make it clear when the backup is waiting on a checkpoint.  Suggested by Michael Renner.

* Various command behavior, help and logging fixes.  Reported by Michael Renner.

* Fixed an issue in async archiving where archive-push was not properly returning 0 when archive-max-mb was reached and moved the async check after transfer to avoid having to remove the stop file twice.  Also added unit tests for this case and improved error messages to make it clearer to the user what went wrong.  Reported by Michael Renner.

* Fixed a locking issue that could allow multiple operations of the same type against a single stanza.  This appeared to be benign in terms of data integrity but caused spurious errors while archiving and could lead to errors in backup/restore. Reported by Michael Renner.

* Replaced JSON module with JSON::PP which ships with core Perl.
2015-06-01 09:24:38 -04:00

1303 lines
48 KiB
Perl
Executable File

#!/usr/bin/perl
####################################################################################################################################
# FileTest.pl - Unit Tests for BackRest::File
####################################################################################################################################
package BackRestTest::FileTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename;
use Cwd 'abs_path';
use File::stat;
use Fcntl ':mode';
use Scalar::Util 'blessed';
use Time::HiRes qw(gettimeofday usleep);
use POSIX qw(ceil);
use lib dirname($0) . '/../lib';
use BackRest::Utility;
use BackRest::Config;
use BackRest::File;
use BackRest::Remote;
use BackRestTest::CommonTest;
use Exporter qw(import);
our @EXPORT = qw(BackRestTestFile_Test);
my $strTestPath;
my $strHost;
my $strUserBackRest;
####################################################################################################################################
# BackRestTestFile_Setup
####################################################################################################################################
sub BackRestTestFile_Setup
{
my $bPrivate = shift;
my $bDropOnly = shift;
# Remove the backrest private directory
if (-e "${strTestPath}/private")
{
system("ssh ${strUserBackRest}\@${strHost} 'rm -rf ${strTestPath}/private'");
}
# Remove the test directory
system("rm -rf ${strTestPath}") == 0 or die 'unable to drop test path';
if (!defined($bDropOnly) || !$bDropOnly)
{
# Create the test directory
mkdir($strTestPath, oct('0770')) or confess 'Unable to create test directory';
# Create the backrest private directory
if (defined($bPrivate) && $bPrivate)
{
system("ssh backrest\@${strHost} 'mkdir -m 700 ${strTestPath}/private'") == 0 or die 'unable to create test/private path';
}
}
}
####################################################################################################################################
# BackRestTestFile_Test
####################################################################################################################################
sub BackRestTestFile_Test
{
my $strTest = shift;
# If no test was specified, then run them all
if (!defined($strTest))
{
$strTest = 'all';
}
# Setup test variables
my $iRun;
$strTestPath = BackRestTestCommon_TestPathGet();
my $strStanza = BackRestTestCommon_StanzaGet();
my $strUser = BackRestTestCommon_UserGet();
my $strGroup = BackRestTestCommon_GroupGet();
$strHost = BackRestTestCommon_HostGet();
$strUserBackRest = BackRestTestCommon_UserBackRestGet();
# Print test banner
&log(INFO, 'FILE MODULE ********************************************************************');
#-------------------------------------------------------------------------------------------------------------------------------
# Create remotes
#-------------------------------------------------------------------------------------------------------------------------------
my $oRemote = BackRest::Remote->new
(
$strHost, # Host
$strUser, # User
BackRestTestCommon_CommandRemoteGet(), # Command
$strStanza, # Stanza
'', # Repo Path
OPTION_DEFAULT_BUFFER_SIZE, # Buffer size
OPTION_DEFAULT_COMPRESS_LEVEL, # Compress level
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK, # Compress network level
);
my $oLocal = new BackRest::Remote
(
undef, # Host
undef, # User
undef, # Command
undef, # Stanza
undef, # Repo Path
OPTION_DEFAULT_BUFFER_SIZE, # Buffer size
OPTION_DEFAULT_COMPRESS_LEVEL, # Compress level
OPTION_DEFAULT_COMPRESS_LEVEL_NETWORK, # Compress network level
);
#-------------------------------------------------------------------------------------------------------------------------------
# Test path_create()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'path_create')
{
$iRun = 0;
&log(INFO, "Test File->path_create()\n");
# Loop through local/remote
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
# Create the file object
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
);
# Loop through error
for (my $bError = 0; $bError <= 1; $bError++)
{
# Loop through mode (mode will be set on true)
for (my $bMode = 0; $bMode <= 1; $bMode++)
{
my $strPathType = PATH_BACKUP_CLUSTER;
# Increment the run, log, and decide whether this unit test should be run
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, err ${bError}, mode ${bMode}")) {next}
# Setup test directory
BackRestTestFile_Setup($bError);
mkdir("${strTestPath}/backup") or confess 'Unable to create test/backup directory';
mkdir("${strTestPath}/backup/db") or confess 'Unable to create test/backup/db directory';
my $strPath = 'path';
my $strMode;
# If mode then set one (other than the default)
if ($bMode)
{
$strMode = '0700';
}
# If not exists then set the path to something bogus
if ($bError)
{
$strPath = "${strTestPath}/private/path";
$strPathType = PATH_BACKUP_ABSOLUTE;
}
# Execute in eval to catch errors
my $bErrorExpected = $bError;
eval
{
$oFile->path_create($strPathType, $strPath, $strMode);
};
# Check for errors
if ($@)
{
# Ignore errors if the path did not exist
if ($bErrorExpected)
{
next;
}
confess "error raised: " . $@ . "\n";
}
if ($bErrorExpected)
{
confess 'error was expected';
}
# Make sure the path was actually created
my $strPathCheck = $oFile->path_get($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 ($bMode)
{
if ($strMode ne sprintf('%04o', S_IMODE($oStat->mode)))
{
confess "mode were not set to {$strMode}";
}
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test move()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'move')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "Test File->move()\n");
for (my $bRemote = 0; $bRemote <= 0; $bRemote++)
{
# Create the file object
my $oFile = (new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
))->clone(1);
# 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 (!BackRestTestCommon_Run(++$iRun,
"src_exists ${bSourceExists}, src_error ${bSourceError}, " .
", dst_exists ${bDestinationExists}, dst_error ${bDestinationError}, " .
"dst_create ${bCreate}")) {next}
# Setup test directory
BackRestTestFile_Setup($bSourceError || $bDestinationError);
my $strSourceFile = "${strTestPath}/test.txt";
my $strDestinationFile = "${strTestPath}/test-dest.txt";
if ($bSourceError)
{
$strSourceFile = "${strTestPath}/private/test.txt";
}
elsif ($bSourceExists)
{
system("echo 'TESTDATA' > ${strSourceFile}");
}
if ($bDestinationError)
{
$strSourceFile = "${strTestPath}/private/test.txt";
}
elsif (!$bDestinationExists)
{
$strDestinationFile = "${strTestPath}/sub/test-dest.txt";
}
# Execute in eval in case of error
eval
{
$oFile->move(PATH_BACKUP_ABSOLUTE, $strSourceFile, PATH_BACKUP_ABSOLUTE, $strDestinationFile, $bCreate);
};
if ($@)
{
if (!$bSourceExists || (!$bDestinationExists && !$bCreate) || $bSourceError || $bDestinationError)
{
next;
}
confess 'error raised: ' . $@ . "\n";
}
if (!$bSourceExists || (!$bDestinationExists && !$bCreate) || $bSourceError || $bDestinationError)
{
confess 'error should have been raised';
}
unless (-e $strDestinationFile)
{
confess 'file was not moved';
}
}
}
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test compress()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'compress')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "Test File->compress()\n");
for (my $bRemote = 0; $bRemote <= 0; $bRemote++)
{
# Create the file object
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
);
# Loop through exists
for (my $bExists = 0; $bExists <= 1; $bExists++)
{
for (my $bError = 0; $bError <= 1; $bError++)
{
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, exists ${bExists}, err ${bError}")) {next}
# Setup test directory
BackRestTestFile_Setup($bError);
my $strFile = "${strTestPath}/test.txt";
my $strSourceHash;
my $iSourceSize;
if ($bError)
{
$strFile = "${strTestPath}/private/test.txt";
}
elsif ($bExists)
{
system("echo 'TESTDATA' > ${strFile}");
($strSourceHash, $iSourceSize) = $oFile->hash_size(PATH_BACKUP_ABSOLUTE, $strFile);
}
# Execute in eval in case of error
eval
{
$oFile->compress(PATH_BACKUP_ABSOLUTE, $strFile);
};
if ($@)
{
if (!$bExists || $bError)
{
next;
}
confess 'error raised: ' . $@ . "\n";
}
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';
}
system("gzip -d ${strDestinationFile}") == 0 or die "could not decompress ${strDestinationFile}";
my ($strDestinationHash, $iDestinationSize) = $oFile->hash_size(PATH_BACKUP_ABSOLUTE, $strFile);
if ($strSourceHash ne $strDestinationHash)
{
confess "source ${strSourceHash} and destination ${strDestinationHash} file hashes do not match";
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test wait()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'wait')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "Test File->wait()\n");
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
# Create the file object
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'db' : undef,
$bRemote ? $oRemote : $oLocal
);
my $lTimeBegin = gettimeofday();
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, begin ${lTimeBegin}")) {next}
# 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";
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test manifest()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'manifest')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "Test File->manifest()\n");
my $strManifestCompare =
".,d,${strUser},${strGroup},0770,,,,\n" .
"sub1,d,${strUser},${strGroup},0750,,,,\n" .
"sub1/sub2,d,${strUser},${strGroup},0750,,,,\n" .
"sub1/sub2/test,l,${strUser},${strGroup},,,,,../..\n" .
"sub1/sub2/test-hardlink.txt,f,${strUser},${strGroup},1640,1111111111,0,9,\n" .
"sub1/sub2/test-sub2.txt,f,${strUser},${strGroup},0666,1111111113,0,11,\n" .
"sub1/test,l,${strUser},${strGroup},,,,,..\n" .
"sub1/test-hardlink.txt,f,${strUser},${strGroup},1640,1111111111,0,9,\n" .
"sub1/test-sub1.txt,f,${strUser},${strGroup},0646,1111111112,0,10,\n" .
"test.txt,f,${strUser},${strGroup},1640,1111111111,0,9,";
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
# Create the file object
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
);
for (my $bError = 0; $bError <= 1; $bError++)
{
for (my $bExists = 0; $bExists <= 1; $bExists++)
{
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, exists ${bExists}, err ${bError}")) {next}
# Setup test directory
BackRestTestFile_Setup($bError);
# Setup test data
system("mkdir -m 750 ${strTestPath}/sub1") == 0 or confess 'Unable to create test directory';
system("mkdir -m 750 ${strTestPath}/sub1/sub2") == 0 or confess 'Unable to create test directory';
system("echo 'TESTDATA' > ${strTestPath}/test.txt");
utime(1111111111, 1111111111, "${strTestPath}/test.txt");
system("chmod 1640 ${strTestPath}/test.txt");
system("echo 'TESTDATA_' > ${strTestPath}/sub1/test-sub1.txt");
utime(1111111112, 1111111112, "${strTestPath}/sub1/test-sub1.txt");
system("chmod 0640 ${strTestPath}/sub1/test-sub1.txt");
system("echo 'TESTDATA__' > ${strTestPath}/sub1/sub2/test-sub2.txt");
utime(1111111113, 1111111113, "${strTestPath}/sub1/sub2/test-sub2.txt");
system("chmod 0646 ${strTestPath}/sub1/test-sub1.txt");
system("ln ${strTestPath}/test.txt ${strTestPath}/sub1/test-hardlink.txt");
system("ln ${strTestPath}/test.txt ${strTestPath}/sub1/sub2/test-hardlink.txt");
system("ln -s .. ${strTestPath}/sub1/test");
system("chmod 0700 ${strTestPath}/sub1/test");
system("ln -s ../.. ${strTestPath}/sub1/sub2/test");
system("chmod 0750 ${strTestPath}/sub1/sub2/test");
system("chmod 0770 ${strTestPath}");
# Create path
my $strPath = $strTestPath;
if ($bError)
{
$strPath = $strTestPath . '/private';
}
elsif (!$bExists)
{
$strPath = $strTestPath . '/error';
}
# Execute in eval in case of error
my %oManifestHash;
my $bErrorExpected = !$bExists || $bError;
eval
{
$oFile->manifest(PATH_BACKUP_ABSOLUTE, $strPath, \%oManifestHash);
};
# Check for an error
if ($@)
{
if ($bErrorExpected)
{
next;
}
confess 'error raised: ' . $@ . "\n";
}
# Check for an expected error
if ($bErrorExpected)
{
confess 'error was expected';
}
my $strManifest;
# Validate the manifest
foreach my $strName (sort(keys $oManifestHash{name}))
{
if (!defined($strManifest))
{
$strManifest = '';
}
else
{
$strManifest .= "\n";
}
if (defined($oManifestHash{name}{"${strName}"}{inode}))
{
$oManifestHash{name}{"${strName}"}{inode} = 0;
}
$strManifest .=
"${strName}," .
$oManifestHash{name}{"${strName}"}{type} . ',' .
(defined($oManifestHash{name}{"${strName}"}{user}) ?
$oManifestHash{name}{"${strName}"}{user} : '') . ',' .
(defined($oManifestHash{name}{"${strName}"}{group}) ?
$oManifestHash{name}{"${strName}"}{group} : '') . ',' .
(defined($oManifestHash{name}{"${strName}"}{mode}) ?
$oManifestHash{name}{"${strName}"}{mode} : '') . ',' .
(defined($oManifestHash{name}{"${strName}"}{modification_time}) ?
$oManifestHash{name}{"${strName}"}{modification_time} : '') . ',' .
(defined($oManifestHash{name}{"${strName}"}{inode}) ?
$oManifestHash{name}{"${strName}"}{inode} : '') . ',' .
(defined($oManifestHash{name}{"${strName}"}{size}) ?
$oManifestHash{name}{"${strName}"}{size} : '') . ',' .
(defined($oManifestHash{name}{"${strName}"}{link_destination}) ?
$oManifestHash{name}{"${strName}"}{link_destination} : '');
}
if ($strManifest ne $strManifestCompare)
{
confess "manifest is not equal:\n\n${strManifest}\n\ncompare:\n\n${strManifestCompare}\n\n";
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test list()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'list')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "Test File->list()\n");
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
# Create the file object
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
);
for (my $bSort = false; $bSort <= true; $bSort++)
{
my $strSort = $bSort ? undef : 'reverse';
# Loop through expression
for (my $iExpression = 0; $iExpression <= 2; $iExpression++)
{
my $strExpression;
# Expression tha returns results
if ($iExpression == 1)
{
$strExpression = "^test2\\..*\$";
}
# Expression that does not return results
elsif ($iExpression == 2)
{
$strExpression = "^du\$";
}
# Loop through exists
for (my $bExists = false; $bExists <= true; $bExists++)
{
# Loop through ignore missing
for (my $bIgnoreMissing = false; $bIgnoreMissing <= $bExists; $bIgnoreMissing++)
{
# Loop through error
for (my $bError = false; $bError <= true; $bError++)
{
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, err ${bError}, exists ${bExists}, ignmis ${bIgnoreMissing}, " .
'expression ' . (defined($strExpression) ? $strExpression : '[undef]') . ', ' .
'sort ' . (defined($strSort) ? $strSort : '[undef]'))) {next}
# Setup test directory
BackRestTestFile_Setup($bError);
my $strPath = $strTestPath;
if ($bError)
{
$strPath = "${strTestPath}/private";
}
elsif (!$bExists)
{
$strPath = "${strTestPath}/error";
}
else
{
system("echo 'TESTDATA' > ${strPath}/test.txt");
system("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);
};
if ($@)
{
if ($bErrorExpected)
{
next;
}
confess 'error raised: ' . $@ . "\n";
}
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 . ']';
}
}
}
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test remove()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'remove')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "Test File->remove()\n");
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
);
# Loop through exists
for (my $bError = 0; $bError <= 1; $bError++)
{
# Loop through exists
for (my $bExists = 0; $bExists <= 1; $bExists++)
{
# Loop through temp
for (my $bTemp = 0; $bTemp <= 1; $bTemp++)
{
# Loop through ignore missing
for (my $bIgnoreMissing = 0; $bIgnoreMissing <= 1; $bIgnoreMissing++)
{
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, err = ${bError}, exists ${bExists}, tmp ${bTemp}, " .
"ignore missing ${bIgnoreMissing}")) {next}
# Setup test directory
BackRestTestFile_Setup($bError);
my $strFile = "${strTestPath}/test.txt";
if ($bError)
{
$strFile = "${strTestPath}/private/test.txt"
}
elsif (!$bExists)
{
$strFile = "${strTestPath}/private/error.txt"
}
else
{
system("echo 'TESTDATA' > ${strFile}" . ($bTemp ? '.backrest.tmp' : ''));
}
# Execute in eval in case of error
my $bRemoved;
eval
{
$bRemoved = $oFile->remove(PATH_BACKUP_ABSOLUTE, $strFile, $bTemp, $bIgnoreMissing);
};
if ($@)
{
if ($bError || $bRemote)
{
next;
}
if (!$bExists && !$bIgnoreMissing)
{
next;
}
confess 'unexpected error raised: ' . $@;
}
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 ? '.backrest.tmp' : '')))
{
confess 'file still exists';
}
}
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test hash()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'hash')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "test File->hash()\n");
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
);
# Loop through error
for (my $bError = false; $bError <= true; $bError++)
{
# Loop through exists
for (my $bExists = false; $bExists <= true; $bExists++)
{
# Loop through exists
for (my $bCompressed = false; $bCompressed <= true; $bCompressed++)
{
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, err ${bError}, exists ${bExists}, cmp ${bCompressed}")) {next}
# Setup test directory
BackRestTestFile_Setup($bError);
my $strFile = "${strTestPath}/test.txt";
if ($bError)
{
$strFile = "${strTestPath}/private/test.txt";
}
elsif (!$bExists)
{
$strFile = "${strTestPath}/error.txt";
}
else
{
system("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->hash_size(PATH_BACKUP_ABSOLUTE, $strFile, $bCompressed)
};
if ($@)
{
if ($bErrorExpected)
{
next;
}
confess 'unexpected error raised: ' . $@;
}
if ($bErrorExpected)
{
confess 'error was expected';
}
if ($strHash ne '06364afe79d801433188262478a76d19777ef351')
{
confess 'hashes do not match';
}
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test exists()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'exists')
{
$iRun = 0;
&log(INFO, '--------------------------------------------------------------------------------');
&log(INFO, "test File->exists()\n");
for (my $bRemote = 0; $bRemote <= 1; $bRemote++)
{
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$bRemote ? 'backup' : undef,
$bRemote ? $oRemote : $oLocal
);
# Loop through exists
for (my $bExists = 0; $bExists <= 1; $bExists++)
{
# Loop through exists
for (my $bError = 0; $bError <= $bExists; $bError++)
{
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, err ${bError}, exists ${bExists}")) {next}
# Setup test directory
BackRestTestFile_Setup($bError);
my $strFile = "${strTestPath}/test.txt";
if ($bError)
{
$strFile = "${strTestPath}/private/test.txt";
}
elsif ($bExists)
{
system("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;
}
};
if ($@)
{
my $oMessage = $@;
my $iCode;
my $strMessage;
if (blessed($oMessage))
{
if ($oMessage->isa('BackRest::Exception'))
{
$iCode = $oMessage->code();
$strMessage = $oMessage->message();
}
else
{
confess 'unknown error object';
}
}
else
{
$strMessage = $oMessage;
}
if ($bError)
{
next;
}
confess 'error raised: ' . $strMessage . "\n";
}
}
}
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test copy()
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'copy')
{
$iRun = 0;
# Loop through small/large
for (my $iLarge = 0; $iLarge <= 3; $iLarge++)
{
# Loop through backup local vs remote
for (my $bBackupRemote = 0; $bBackupRemote <= 1; $bBackupRemote++)
{
# Loop through db local vs remote
for (my $bDbRemote = 0; $bDbRemote <= 1; $bDbRemote++)
{
# Backup and db cannot both be remote
if ($bBackupRemote && $bDbRemote)
{
next;
}
# Determine side is remote
my $strRemote = $bBackupRemote ? 'backup' : $bDbRemote ? 'db' : undef;
# Create the file object
my $oFile = new BackRest::File
(
$strStanza,
$strTestPath,
$strRemote,
defined($strRemote) ? $oRemote : $oLocal
);
# Loop through source path types
for (my $bSourcePathType = 0; $bSourcePathType <= 1; $bSourcePathType++)
{
# Loop through destination path types
for (my $bDestinationPathType = 0; $bDestinationPathType <= 1; $bDestinationPathType++)
{
# Loop through source missing/present
for (my $bSourceMissing = 0; $bSourceMissing <= !$iLarge; $bSourceMissing++)
{
# Loop through source ignore/require
for (my $bSourceIgnoreMissing = 0; $bSourceIgnoreMissing <= !$iLarge; $bSourceIgnoreMissing++)
{
# Loop through checksum append
for (my $bChecksumAppend = 0; $bChecksumAppend <= !$iLarge; $bChecksumAppend++)
{
# Loop through source compression
for (my $bSourceCompressed = 0; $bSourceCompressed <= !$bSourceMissing; $bSourceCompressed++)
{
# Loop through destination compression
for (my $bDestinationCompress = 0; $bDestinationCompress <= !$bSourceMissing; $bDestinationCompress++)
{
my $strSourcePathType = $bSourcePathType ? PATH_DB_ABSOLUTE : PATH_BACKUP_ABSOLUTE;
my $strSourcePath = $bSourcePathType ? 'db' : 'backup';
my $strDestinationPathType = $bDestinationPathType ? PATH_DB_ABSOLUTE : PATH_BACKUP_ABSOLUTE;
my $strDestinationPath = $bDestinationPathType ? 'db' : 'backup';
if (!BackRestTestCommon_Run(++$iRun,
"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
BackRestTestFile_Setup(false);
system("mkdir ${strTestPath}/backup") == 0 or confess 'Unable to create test/backup directory';
system("mkdir ${strTestPath}/db") == 0 or confess 'Unable to create test/db directory';
my $strSourceFile = "${strTestPath}/${strSourcePath}/test-source";
my $strDestinationFile = "${strTestPath}/${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)
{
BackRestTestCommon_Execute('cp ' . BackRestTestCommon_DataPathGet() .
"/test.archive${iLarge}.bin ${strSourceFile}");
}
else
{
for (my $iTableSizeIdx = 0; $iTableSizeIdx < 100; $iTableSizeIdx++)
{
BackRestTestCommon_Execute('cat ' . BackRestTestCommon_DataPathGet() .
"/test.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 = 'c23a89d47c7a006fcda51da0cc95993dc9aad995';
$iSourceSize = 104857600;
}
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, '0700', false, undef, undef,
$bChecksumAppend);
};
# Check for errors after copy
if ($@)
{
my $oMessage = $@;
if (blessed($oMessage))
{
if ($oMessage->isa('BackRest::Exception'))
{
if ($bSourceMissing && !$bSourceIgnoreMissing)
{
next;
}
confess $oMessage->message();
}
else
{
confess 'unknown error object: ' . $oMessage;
}
}
confess $oMessage;
}
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->hash_size(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";
}
}
}
}
}
}
}
}
}
}
}
}
if (BackRestTestCommon_Cleanup())
{
BackRestTestFile_Setup(undef, true);
}
}
1;