1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-11-06 08:49:29 +02:00

v0.30: core restructuring and unit tests

* Complete rewrite of BackRest::File module to use a custom protocol for remote operations and Perl native GZIP and SHA operations.  Compression is performed in threads rather than forked processes.

* Fairly comprehensive unit tests for all the basic operations.  More work to be done here for sure, but then there is always more work to be done on unit tests.

* Removed dependency on Storable and replaced with a custom ini file implementation.

* Added much needed documentation (see INSTALL.md).

* Numerous other changes that can only be identified with a diff.
This commit is contained in:
David Steele
2014-10-05 19:49:30 -04:00
parent 1fa8dbb778
commit 4bc4d97f2b
21 changed files with 7162 additions and 2599 deletions

BIN
test/data/test.archive.bin Normal file

Binary file not shown.

View File

@@ -0,0 +1,630 @@
#!/usr/bin/perl
####################################################################################################################################
# BackupTest.pl - Unit Tests for BackRest::File
####################################################################################################################################
package BackRestTest::BackupTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings;
use Carp;
use File::Basename;
use File::Copy 'cp';
use DBI;
use lib dirname($0) . '/../lib';
use BackRest::Utility;
use BackRest::File;
use BackRest::Remote;
use BackRestTest::CommonTest;
use Exporter qw(import);
our @EXPORT = qw(BackRestTestBackup_Test);
my $strTestPath;
my $strHost;
my $strUserBackRest;
my $hDb;
####################################################################################################################################
# BackRestTestBackup_PgConnect
####################################################################################################################################
sub BackRestTestBackup_PgConnect
{
# Disconnect user session
BackRestTestBackup_PgDisconnect();
# Connect to the db (whether it is local or remote)
$hDb = DBI->connect('dbi:Pg:dbname=postgres;port=' . BackRestTestCommon_DbPortGet .
';host=' . BackRestTestCommon_DbPathGet(),
BackRestTestCommon_UserGet(),
undef,
{AutoCommit => 1, RaiseError => 1});
}
####################################################################################################################################
# BackRestTestBackup_Disconnect
####################################################################################################################################
sub BackRestTestBackup_PgDisconnect
{
# Connect to the db (whether it is local or remote)
if (defined($hDb))
{
$hDb->disconnect;
undef($hDb);
}
}
####################################################################################################################################
# BackRestTestBackup_PgExecute
####################################################################################################################################
sub BackRestTestBackup_PgExecute
{
my $strSql = shift;
my $bCheckpoint = shift;
# Log and execute the statement
&log(DEBUG, "SQL: ${strSql}");
my $hStatement = $hDb->prepare($strSql);
$hStatement->execute() or
confess &log(ERROR, "Unable to execute: ${strSql}");
$hStatement->finish();
# Perform a checkpoint if requested
if (defined($bCheckpoint) && $bCheckpoint)
{
BackRestTestBackup_PgExecute('checkpoint');
}
}
####################################################################################################################################
# BackRestTestBackup_ClusterStop
####################################################################################################################################
sub BackRestTestBackup_ClusterStop
{
my $strPath = shift;
# Disconnect user session
BackRestTestBackup_PgDisconnect();
# If postmaster process is running them stop the cluster
if (-e $strPath . '/postmaster.pid')
{
BackRestTestCommon_Execute(BackRestTestCommon_PgSqlBinPathGet() . "/pg_ctl stop -D ${strPath} -w -s -m fast");
}
}
####################################################################################################################################
# BackRestTestBackup_ClusterRestart
####################################################################################################################################
sub BackRestTestBackup_ClusterRestart
{
my $strPath = BackRestTestCommon_DbCommonPathGet();
# Disconnect user session
BackRestTestBackup_PgDisconnect();
# If postmaster process is running them stop the cluster
if (-e $strPath . '/postmaster.pid')
{
BackRestTestCommon_Execute(BackRestTestCommon_PgSqlBinPathGet() . "/pg_ctl restart -D ${strPath} -w -s");
}
# Connect user session
BackRestTestBackup_PgConnect();
}
####################################################################################################################################
# BackRestTestBackup_ClusterCreate
####################################################################################################################################
sub BackRestTestBackup_ClusterCreate
{
my $strPath = shift;
my $iPort = shift;
my $strArchive = BackRestTestCommon_CommandMainGet() . ' --stanza=' . BackRestTestCommon_StanzaGet() .
' --config=' . BackRestTestCommon_DbPathGet() . '/pg_backrest.conf archive-push %p';
BackRestTestCommon_Execute(BackRestTestCommon_PgSqlBinPathGet() . "/initdb -D ${strPath} -A trust");
BackRestTestCommon_Execute(BackRestTestCommon_PgSqlBinPathGet() . "/pg_ctl start -o \"-c port=${iPort} -c " .
"checkpoint_segments=1 -c wal_level=archive -c archive_mode=on -c archive_command='${strArchive}' " .
"-c unix_socket_directories='" . BackRestTestCommon_DbPathGet() . "'\" " .
"-D ${strPath} -l ${strPath}/postgresql.log -w -s");
# Connect user session
BackRestTestBackup_PgConnect();
}
####################################################################################################################################
# BackRestTestBackup_Drop
####################################################################################################################################
sub BackRestTestBackup_Drop
{
# Stop the cluster if one is running
BackRestTestBackup_ClusterStop(BackRestTestCommon_DbCommonPathGet());
# Remove the backrest private directory
if (-e BackRestTestCommon_BackupPathGet())
{
BackRestTestCommon_Execute('rm -rf ' . BackRestTestCommon_BackupPathGet(), true, true);
}
# Remove the test directory
system('rm -rf ' . BackRestTestCommon_TestPathGet()) == 0
or die 'unable to remove ' . BackRestTestCommon_TestPathGet() . 'path';
}
####################################################################################################################################
# BackRestTestBackup_Create
####################################################################################################################################
sub BackRestTestBackup_Create
{
my $bRemote = shift;
my $bCluster = shift;
# Set defaults
$bRemote = defined($bRemote) ? $bRemote : false;
$bCluster = defined($bCluster) ? $bCluster : true;
# Drop the old test directory
BackRestTestBackup_Drop();
# Create the test directory
mkdir(BackRestTestCommon_TestPathGet(), oct('0770'))
or confess 'Unable to create ' . BackRestTestCommon_TestPathGet() . ' path';
# Create the db directory
mkdir(BackRestTestCommon_DbPathGet(), oct('0700'))
or confess 'Unable to create ' . BackRestTestCommon_DbPathGet() . ' path';
# Create the db/common directory
mkdir(BackRestTestCommon_DbCommonPathGet())
or confess 'Unable to create ' . BackRestTestCommon_DbCommonPathGet() . ' path';
# Create the archive directory
mkdir(BackRestTestCommon_ArchivePathGet(), oct('0700'))
or confess 'Unable to create ' . BackRestTestCommon_ArchivePathGet() . ' path';
# Create the backup directory
if ($bRemote)
{
BackRestTestCommon_Execute('mkdir -m 700 ' . BackRestTestCommon_BackupPathGet(), true);
}
else
{
mkdir(BackRestTestCommon_BackupPathGet(), oct('0700'))
or confess 'Unable to create ' . BackRestTestCommon_BackupPathGet() . ' path';
}
# Create the cluster
if ($bCluster)
{
BackRestTestBackup_ClusterCreate(BackRestTestCommon_DbCommonPathGet(), BackRestTestCommon_DbPortGet());
}
}
####################################################################################################################################
# BackRestTestBackup_Test
####################################################################################################################################
sub BackRestTestBackup_Test
{
my $strTest = shift;
# If no test was specified, then run them all
if (!defined($strTest))
{
$strTest = 'all';
}
# Setup global variables
$strTestPath = BackRestTestCommon_TestPathGet();
$strHost = BackRestTestCommon_HostGet();
$strUserBackRest = BackRestTestCommon_UserBackRestGet();
# Setup test variables
my $iRun;
my $bCreate;
my $strStanza = BackRestTestCommon_StanzaGet();
my $strGroup = BackRestTestCommon_GroupGet();
my $strArchiveChecksum = '1c7e00fd09b9dd11fc2966590b3e3274645dd031';
my $iArchiveMax = 3;
my $strXlogPath = BackRestTestCommon_DbCommonPathGet() . '/pg_xlog';
my $strArchiveTestFile = BackRestTestCommon_DataPathGet() . '/test.archive.bin';
my $iThreadMax = 4;
# Print test banner
&log(INFO, 'BACKUP MODULE ******************************************************************');
#-------------------------------------------------------------------------------------------------------------------------------
# Create remote
#-------------------------------------------------------------------------------------------------------------------------------
my $oRemote = BackRest::Remote->new
(
strHost => $strHost,
strUser => $strUserBackRest,
strCommand => BackRestTestCommon_CommandRemoteGet()
);
#-------------------------------------------------------------------------------------------------------------------------------
# Test archive-push
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'archive-push')
{
$iRun = 0;
$bCreate = true;
my $oFile;
&log(INFO, "Test archive-push\n");
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
for (my $bCompress = false; $bCompress <= true; $bCompress++)
{
for (my $bChecksum = false; $bChecksum <= true; $bChecksum++)
{
for (my $bArchiveAsync = false; $bArchiveAsync <= $bRemote; $bArchiveAsync++)
{
for (my $bCompressAsync = false; $bCompressAsync <= true; $bCompressAsync++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, cmp ${bCompress}, chk ${bChecksum}, " .
"arc_async ${bArchiveAsync}, cmp_async ${bCompressAsync}")) {next}
# Create the test directory
if ($bCreate)
{
# Create the file object
$oFile = (BackRest::File->new
(
strStanza => $strStanza,
strBackupPath => BackRestTestCommon_BackupPathGet(),
strRemote => $bRemote ? 'backup' : undef,
oRemote => $bRemote ? $oRemote : undef
))->clone();
BackRestTestBackup_Create($bRemote, false);
#
# # Create the db/common/pg_xlog directory
# mkdir($strXlogPath)
# or confess 'Unable to create ${strXlogPath} path';
$bCreate = false;
}
BackRestTestCommon_ConfigCreate('db',
($bRemote ? REMOTE_BACKUP : undef),
$bCompress,
$bChecksum, # checksum
undef, # hardlink
undef, # thread-max
$bArchiveAsync,
$bCompressAsync);
my $strCommand = BackRestTestCommon_CommandMainGet() . ' --config=' . BackRestTestCommon_DbPathGet() .
'/pg_backrest.conf --stanza=db archive-push';
# Loop through backups
for (my $iBackup = 1; $iBackup <= 3; $iBackup++)
{
my $strArchiveFile;
# Loop through archive files
for (my $iArchive = 1; $iArchive <= $iArchiveMax; $iArchive++)
{
# 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 = uc(sprintf('0000000100000001%08x', $iArchiveNo));
&log(INFO, ' backup ' . sprintf('%02d', $iBackup) .
', archive ' .sprintf('%02x', $iArchive) .
" - ${strArchiveFile}");
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
undef, undef, undef, # Unused params
true); # Create path if it does not exist
BackRestTestCommon_Execute($strCommand . " ${strSourceFile}");
# Build the archive name to check for at the destination
my $strArchiveCheck = $strArchiveFile;
if ($bChecksum)
{
$strArchiveCheck .= "-${strArchiveChecksum}";
}
if ($bCompress)
{
$strArchiveCheck .= '.gz';
}
if (!$oFile->exists(PATH_BACKUP_ARCHIVE, $strArchiveCheck))
{
sleep(1);
if (!$oFile->exists(PATH_BACKUP_ARCHIVE, $strArchiveCheck))
{
confess 'unable to find ' . $oFile->path_get(PATH_BACKUP_ARCHIVE, $strArchiveCheck);
}
}
}
# !!! Need to put in tests for .backup files here
}
}
}
}
}
$bCreate = true;
}
if (BackRestTestCommon_Cleanup())
{
&log(INFO, 'cleanup');
BackRestTestBackup_Drop();
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test archive-get
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'archive-get')
{
$iRun = 0;
$bCreate = true;
my $oFile;
&log(INFO, "Test archive-get\n");
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
for (my $bCompress = false; $bCompress <= true; $bCompress++)
{
for (my $bChecksum = false; $bChecksum <= true; $bChecksum++)
{
for (my $bExists = false; $bExists <= true; $bExists++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, cmp ${bCompress}, chk ${bChecksum}, exists ${bExists}")) {next}
# Create the test directory
if ($bCreate)
{
# Create the file object
$oFile = (BackRest::File->new
(
strStanza => $strStanza,
strBackupPath => BackRestTestCommon_BackupPathGet(),
strRemote => $bRemote ? 'backup' : undef,
oRemote => $bRemote ? $oRemote : undef
))->clone();
BackRestTestBackup_Create($bRemote, false);
# Create the db/common/pg_xlog directory
mkdir($strXlogPath)
or confess 'Unable to create ${strXlogPath} path';
$bCreate = false;
}
BackRestTestCommon_ConfigCreate('db', # local
($bRemote ? REMOTE_BACKUP : undef), # remote
$bCompress, # compress
$bChecksum, # checksum
undef, # hardlink
undef, # thread-max
undef, # archive-async
undef); # compress-async
my $strCommand = BackRestTestCommon_CommandMainGet() . ' --config=' . BackRestTestCommon_DbPathGet() .
'/pg_backrest.conf --stanza=db archive-get';
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;
if ($bChecksum)
{
$strSourceFile .= "-${strArchiveChecksum}";
}
if ($bCompress)
{
$strSourceFile .= '.gz';
}
$oFile->copy(PATH_DB_ABSOLUTE, $strArchiveTestFile, # Source file
PATH_BACKUP_ARCHIVE, $strSourceFile, # Destination file
false, # Source is not compressed
$bCompress, # Destination compress based on test
undef, undef, undef, # Unused params
true); # Create path if it does not exist
my $strDestinationFile = "${strXlogPath}/${strArchiveFile}";
BackRestTestCommon_Execute($strCommand . " ${strArchiveFile} ${strDestinationFile}");
# 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
{
if (BackRestTestCommon_Execute($strCommand . " 000000090000000900000009 ${strXlogPath}/RECOVERYXLOG",
false, true) != 1)
{
confess 'archive-get should return 1 when archive log is not present';
}
}
$bCreate = true;
}
}
}
}
if (BackRestTestCommon_Cleanup())
{
&log(INFO, 'cleanup');
BackRestTestBackup_Drop();
}
}
#-------------------------------------------------------------------------------------------------------------------------------
# Test full
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'full')
{
$iRun = 0;
$bCreate = true;
&log(INFO, "Test Full Backup\n");
for (my $bRemote = false; $bRemote <= true; $bRemote++)
{
for (my $bLarge = false; $bLarge <= false; $bLarge++)
{
for (my $bCompress = false; $bCompress <= false; $bCompress++)
{
for (my $bChecksum = false; $bChecksum <= false; $bChecksum++)
{
for (my $bHardlink = false; $bHardlink <= true; $bHardlink++)
{
for (my $bArchiveAsync = false; $bArchiveAsync <= $bRemote; $bArchiveAsync++)
{
# Increment the run, log, and decide whether this unit test should be run
if (!BackRestTestCommon_Run(++$iRun,
"rmt ${bRemote}, lrg ${bLarge}, cmp ${bCompress}, chk ${bChecksum}, " .
"hardlink ${bHardlink}, arc_async ${bArchiveAsync}")) {next}
# Create the test directory
if ($bCreate)
{
BackRestTestBackup_Create($bRemote);
$bCreate = false;
}
# Create db config
BackRestTestCommon_ConfigCreate('db', # local
$bRemote ? REMOTE_BACKUP : undef, # remote
$bCompress, # compress
$bChecksum, # checksum
defined($bRemote) ? undef : $bHardlink, # hardlink
defined($bRemote) ? undef : $iThreadMax, # thread-max
$bArchiveAsync, # archive-async
undef); # compress-async
# Create backup config
if ($bRemote)
{
BackRestTestCommon_ConfigCreate('backup', # local
$bRemote ? REMOTE_DB : undef, # remote
$bCompress, # compress
$bChecksum, # checksum
$bHardlink, # hardlink
$iThreadMax, # thread-max
undef, # archive-async
undef); # compress-async
}
# Create the backup command
my $strCommand = BackRestTestCommon_CommandMainGet() . ' --config=' .
($bRemote ? BackRestTestCommon_BackupPathGet() : BackRestTestCommon_DbPathGet()) .
"/pg_backrest.conf --test --type=incr --stanza=${strStanza} backup";
# Run the full/incremental tests
for (my $iFull = 1; $iFull <= 1; $iFull++)
{
for (my $iIncr = 0; $iIncr <= 2; $iIncr++)
{
&log(INFO, ' ' . ($iIncr == 0 ? ('full ' . sprintf('%02d', $iFull)) :
(' incr ' . sprintf('%02d', $iIncr))));
# Create a table in each backup to check references
BackRestTestBackup_PgExecute("create table test_backup_${iIncr} (id int)", true);
# Create a table to be dropped to test missing file code
BackRestTestBackup_PgExecute('create table test_drop (id int)');
BackRestTestCommon_ExecuteBegin($strCommand, $bRemote);
if (BackRestTestCommon_ExecuteEnd(TEST_MANIFEST_BUILD))
{
BackRestTestBackup_PgExecute('drop table test_drop', true);
BackRestTestCommon_ExecuteEnd();
}
else
{
confess &log(ERROR, 'test point ' . TEST_MANIFEST_BUILD . ' was not found');
}
}
}
$bCreate = true;
}
}
}
}
}
}
if (BackRestTestCommon_Cleanup())
{
&log(INFO, 'cleanup');
BackRestTestBackup_Drop();
}
}
}
1;

View File

@@ -0,0 +1,436 @@
#!/usr/bin/perl
####################################################################################################################################
# CommonTest.pm - Common globals used for testing
####################################################################################################################################
package BackRestTest::CommonTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings;
use Carp;
use File::Basename;
use Cwd 'abs_path';
use IPC::Open3;
use POSIX ':sys_wait_h';
use IO::Select;
use lib dirname($0) . '/../lib';
use BackRest::Utility;
use BackRest::File;
use Exporter qw(import);
our @EXPORT = qw(BackRestTestCommon_Setup BackRestTestCommon_ExecuteBegin BackRestTestCommon_ExecuteEnd
BackRestTestCommon_Execute BackRestTestCommon_ExecuteBackRest
BackRestTestCommon_ConfigCreate BackRestTestCommon_Run BackRestTestCommon_Cleanup
BackRestTestCommon_PgSqlBinPathGet BackRestTestCommon_StanzaGet BackRestTestCommon_CommandMainGet
BackRestTestCommon_CommandRemoteGet BackRestTestCommon_HostGet BackRestTestCommon_UserGet
BackRestTestCommon_GroupGet BackRestTestCommon_UserBackRestGet BackRestTestCommon_TestPathGet
BackRestTestCommon_DataPathGet BackRestTestCommon_BackupPathGet BackRestTestCommon_ArchivePathGet
BackRestTestCommon_DbPathGet BackRestTestCommon_DbCommonPathGet BackRestTestCommon_DbPortGet);
my $strPgSqlBin;
my $strCommonStanza;
my $strCommonCommandMain;
my $strCommonCommandRemote;
my $strCommonCommandPsql;
my $strCommonHost;
my $strCommonUser;
my $strCommonGroup;
my $strCommonUserBackRest;
my $strCommonTestPath;
my $strCommonDataPath;
my $strCommonBackupPath;
my $strCommonArchivePath;
my $strCommonDbPath;
my $strCommonDbCommonPath;
my $iCommonDbPort;
my $iModuleTestRun;
my $bDryRun;
my $bNoCleanup;
# Execution globals
my $strErrorLog;
my $hError;
my $strOutLog;
my $hOut;
my $pId;
my $strCommand;
####################################################################################################################################
# BackRestTestBackup_Run
####################################################################################################################################
sub BackRestTestCommon_Run
{
my $iRun = shift;
my $strLog = shift;
if (defined($iModuleTestRun) && $iModuleTestRun != $iRun)
{
return false;
}
&log(INFO, 'run ' . sprintf('%03d', $iRun) . ' - ' . $strLog);
if ($bDryRun)
{
return false;
}
return true;
}
####################################################################################################################################
# BackRestTestBackup_Cleanup
####################################################################################################################################
sub BackRestTestCommon_Cleanup
{
return !$bNoCleanup && !$bDryRun;
}
####################################################################################################################################
# BackRestTestBackup_ExecuteBegin
####################################################################################################################################
sub BackRestTestCommon_ExecuteBegin
{
my $strCommandParam = shift;
my $bRemote = shift;
# Set defaults
$bRemote = defined($bRemote) ? $bRemote : false;
if ($bRemote)
{
$strCommand = "ssh ${strCommonUserBackRest}\@${strCommonHost} '${strCommandParam}'";
}
else
{
$strCommand = $strCommandParam;
}
$strErrorLog = '';
$hError = undef;
$strOutLog = '';
$hOut = undef;
&log(DEBUG, "executing command: ${strCommand}");
# Execute the command
$pId = open3(undef, $hOut, $hError, $strCommand);
}
####################################################################################################################################
# BackRestTestBackup_ExecuteEnd
####################################################################################################################################
sub BackRestTestCommon_ExecuteEnd
{
my $strTest = shift;
my $bSuppressError = shift;
# Set defaults
$bSuppressError = defined($bSuppressError) ? $bSuppressError : false;
# Create select objects
my $oErrorSelect = IO::Select->new();
$oErrorSelect->add($hError);
my $oOutSelect = IO::Select->new();
$oOutSelect->add($hOut);
# While the process is running drain the stdout and stderr streams
while(waitpid($pId, WNOHANG) == 0)
{
# Drain the stderr stream
if ($oErrorSelect->can_read(.1))
{
while (my $strLine = readline($hError))
{
$strErrorLog .= $strLine;
}
}
# Drain the stdout stream
if ($oOutSelect->can_read(.1))
{
while (my $strLine = readline($hOut))
{
$strOutLog .= $strLine;
if (defined($strTest) && test_check($strLine, $strTest))
{
&log(DEBUG, "Found test ${strTest}");
return true;
}
}
}
}
# Check the exit status and output an error if needed
my $iExitStatus = ${^CHILD_ERROR_NATIVE} >> 8;
if ($iExitStatus != 0 && !$bSuppressError)
{
confess &log(ERROR, "command '${strCommand}' returned " . $iExitStatus . "\n" .
($strOutLog ne '' ? "STDOUT:\n${strOutLog}" : '') .
($strErrorLog ne '' ? "STDERR:\n${strErrorLog}" : ''));
}
else
{
&log(DEBUG, "suppressed error was ${iExitStatus}");
}
$hError = undef;
$hOut = undef;
return $iExitStatus;
}
####################################################################################################################################
# BackRestTestBackup_Execute
####################################################################################################################################
sub BackRestTestCommon_Execute
{
my $strCommand = shift;
my $bRemote = shift;
my $bSuppressError = shift;
BackRestTestCommon_ExecuteBegin($strCommand, $bRemote);
return BackRestTestCommon_ExecuteEnd(undef, $bSuppressError);
}
####################################################################################################################################
# BackRestTestCommon_Setup
####################################################################################################################################
sub BackRestTestCommon_Setup
{
my $strTestPathParam = shift;
my $strPgSqlBinParam = shift;
my $iModuleTestRunParam = shift;
my $bDryRunParam = shift;
my $bNoCleanupParam = shift;
my $strBasePath = dirname(dirname(abs_path($0)));
$strPgSqlBin = $strPgSqlBinParam;
$strCommonStanza = 'db';
$strCommonHost = '127.0.0.1';
$strCommonUser = getpwuid($<);
$strCommonGroup = getgrgid($();
$strCommonUserBackRest = 'backrest';
if (defined($strTestPathParam))
{
$strCommonTestPath = $strTestPathParam;
}
else
{
$strCommonTestPath = "${strBasePath}/test/test";
}
$strCommonDataPath = "${strBasePath}/test/data";
$strCommonBackupPath = "${strCommonTestPath}/backrest";
$strCommonArchivePath = "${strCommonTestPath}/archive";
$strCommonDbPath = "${strCommonTestPath}/db";
$strCommonDbCommonPath = "${strCommonTestPath}/db/common";
$strCommonCommandMain = "${strBasePath}/bin/pg_backrest.pl";
$strCommonCommandRemote = "${strBasePath}/bin/pg_backrest_remote.pl";
$strCommonCommandPsql = "${strPgSqlBin}/psql -X %option% -h ${strCommonDbPath}";
$iCommonDbPort = 6543;
$iModuleTestRun = $iModuleTestRunParam;
$bDryRun = $bDryRunParam;
$bNoCleanup = $bNoCleanupParam;
}
####################################################################################################################################
# BackRestTestCommon_ConfigCreate
####################################################################################################################################
sub BackRestTestCommon_ConfigCreate
{
my $strLocal = shift;
my $strRemote = shift;
my $bCompress = shift;
my $bChecksum = shift;
my $bHardlink = shift;
my $iThreadMax = shift;
my $bArchiveLocal = shift;
my $bCompressAsync = shift;
my %oParamHash;
if (defined($strRemote))
{
$oParamHash{'global:command'}{'remote'} = $strCommonCommandRemote;
}
$oParamHash{'global:command'}{'psql'} = $strCommonCommandPsql;
if (defined($strRemote) && $strRemote eq REMOTE_BACKUP)
{
$oParamHash{'global:backup'}{'host'} = $strCommonHost;
$oParamHash{'global:backup'}{'user'} = $strCommonUserBackRest;
}
elsif (defined($strRemote) && $strRemote eq REMOTE_DB)
{
$oParamHash{$strCommonStanza}{'host'} = $strCommonHost;
$oParamHash{$strCommonStanza}{'user'} = $strCommonUser;
}
$oParamHash{'global:log'}{'level-console'} = 'error';
$oParamHash{'global:log'}{'level-file'} = 'trace';
if ($strLocal eq REMOTE_BACKUP)
{
if (defined($bHardlink) && $bHardlink)
{
$oParamHash{'global:backup'}{'hardlink'} = 'y';
}
}
elsif ($strLocal eq REMOTE_DB)
{
if (defined($strRemote))
{
$oParamHash{'global:log'}{'level-console'} = 'trace';
}
if ($bArchiveLocal)
{
$oParamHash{'global:archive'}{path} = BackRestTestCommon_ArchivePathGet();
if (!$bCompressAsync)
{
$oParamHash{'global:archive'}{'compress_async'} = 'n';
}
}
}
else
{
confess "invalid local type ${strLocal}";
}
if (($strLocal eq REMOTE_BACKUP) || ($strLocal eq REMOTE_DB && !defined($strRemote)))
{
$oParamHash{'db:command:option'}{'psql'} = "--port=${iCommonDbPort}";
}
if (defined($bCompress) && !$bCompress)
{
$oParamHash{'global:backup'}{'compress'} = 'n';
}
if (defined($bChecksum) && !$bChecksum)
{
$oParamHash{'global:backup'}{'checksum'} = 'n';
}
$oParamHash{$strCommonStanza}{'path'} = $strCommonDbCommonPath;
$oParamHash{'global:backup'}{'path'} = $strCommonBackupPath;
if (defined($iThreadMax))
{
$oParamHash{'global:backup'}{'thread-max'} = $iThreadMax;
}
# Write out the configuration file
my $strFile = BackRestTestCommon_TestPathGet() . '/pg_backrest.conf';
config_save($strFile, \%oParamHash);
# Move the configuration file based on local
if ($strLocal eq 'db')
{
rename($strFile, BackRestTestCommon_DbPathGet() . '/pg_backrest.conf')
or die "unable to move ${strFile} to " . BackRestTestCommon_DbPathGet() . '/pg_backrest.conf path';
}
elsif ($strLocal eq 'backup' && !defined($strRemote))
{
rename($strFile, BackRestTestCommon_BackupPathGet() . '/pg_backrest.conf')
or die "unable to move ${strFile} to " . BackRestTestCommon_BackupPathGet() . '/pg_backrest.conf path';
}
else
{
BackRestTestCommon_Execute("mv ${strFile} " . BackRestTestCommon_BackupPathGet() . '/pg_backrest.conf', true);
}
}
####################################################################################################################################
# Get Methods
####################################################################################################################################
sub BackRestTestCommon_PgSqlBinPathGet
{
return $strPgSqlBin;
}
sub BackRestTestCommon_StanzaGet
{
return $strCommonStanza;
}
sub BackRestTestCommon_CommandMainGet
{
return $strCommonCommandMain;
}
sub BackRestTestCommon_CommandRemoteGet
{
return $strCommonCommandRemote;
}
sub BackRestTestCommon_HostGet
{
return $strCommonHost;
}
sub BackRestTestCommon_UserGet
{
return $strCommonUser;
}
sub BackRestTestCommon_GroupGet
{
return $strCommonGroup;
}
sub BackRestTestCommon_UserBackRestGet
{
return $strCommonUserBackRest;
}
sub BackRestTestCommon_TestPathGet
{
return $strCommonTestPath;
}
sub BackRestTestCommon_DataPathGet
{
return $strCommonDataPath;
}
sub BackRestTestCommon_BackupPathGet
{
return $strCommonBackupPath;
}
sub BackRestTestCommon_ArchivePathGet
{
return $strCommonArchivePath;
}
sub BackRestTestCommon_DbPathGet
{
return $strCommonDbPath;
}
sub BackRestTestCommon_DbCommonPathGet
{
return $strCommonDbCommonPath;
}
sub BackRestTestCommon_DbPortGet
{
return $iCommonDbPort;
}
1;

1150
test/lib/BackRestTest/FileTest.pm Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
#!/usr/bin/perl
####################################################################################################################################
# BackupTest.pl - Unit Tests for BackRest::File
####################################################################################################################################
package BackRestTest::UtilityTest;
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings;
use Carp;
use File::Basename;
use lib dirname($0) . '/../lib';
use BackRest::Utility;
use BackRest::File;
use BackRestTest::CommonTest;
use Exporter qw(import);
our @EXPORT = qw(BackRestTestUtility_Test);
####################################################################################################################################
# BackRestTestUtility_Drop
####################################################################################################################################
sub BackRestTestUtility_Drop
{
# Remove the test directory
system('rm -rf ' . BackRestTestCommon_TestPathGet()) == 0
or die 'unable to remove ' . BackRestTestCommon_TestPathGet() . 'path';
}
####################################################################################################################################
# BackRestTestUtility_Create
####################################################################################################################################
sub BackRestTestUtility_Create
{
# Drop the old test directory
BackRestTestUtility_Drop();
# Create the test directory
mkdir(BackRestTestCommon_TestPathGet(), oct('0770'))
or confess 'Unable to create ' . BackRestTestCommon_TestPathGet() . ' path';
}
####################################################################################################################################
# BackRestTestUtility_Test
####################################################################################################################################
sub BackRestTestUtility_Test
{
my $strTest = shift;
# Setup test variables
my $iRun;
my $bCreate;
my $strTestPath = BackRestTestCommon_TestPathGet();
# Print test banner
&log(INFO, 'UTILITY MODULE ******************************************************************');
#-------------------------------------------------------------------------------------------------------------------------------
# Test config
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'config')
{
$iRun = 0;
$bCreate = true;
my $oFile = BackRest::File->new();
&log(INFO, "Test config\n");
# Increment the run, log, and decide whether this unit test should be run
if (BackRestTestCommon_Run(++$iRun, 'base'))
{
# Create the test directory
if ($bCreate)
{
BackRestTestUtility_Create();
$bCreate = false;
}
# Generate a test config
my %oConfig;
$oConfig{test1}{key1} = 'value';
$oConfig{test1}{key2} = 'value';
$oConfig{test2}{key1} = 'value';
$oConfig{test3}{key1} = 'value';
$oConfig{test3}{key2}{sub1} = 'value';
$oConfig{test3}{key2}{sub2} = 'value';
# Save the test config
my $strFile = "${strTestPath}/config.cfg";
config_save($strFile, \%oConfig);
my $strConfigHash = $oFile->hash(PATH_ABSOLUTE, $strFile);
# Reload the test config
my %oConfigTest;
config_load($strFile, \%oConfigTest);
# Resave the test config and compare hashes
my $strFileTest = "${strTestPath}/config-test.cfg";
config_save($strFileTest, \%oConfigTest);
my $strConfigTestHash = $oFile->hash(PATH_ABSOLUTE, $strFileTest);
if ($strConfigHash ne $strConfigTestHash)
{
confess "config hash ${strConfigHash} != ${strConfigTestHash}";
}
if (BackRestTestCommon_Cleanup())
{
&log(INFO, 'cleanup');
BackRestTestUtility_Drop();
}
}
}
}
1;

View File

@@ -1,244 +1,148 @@
#!/usr/bin/perl
####################################################################################################################################
# test.pl - BackRest Unit Tests
####################################################################################################################################
# /Library/PostgreSQL/9.3/bin/pg_ctl start -o "-c port=7000" -D /Users/backrest/test/backup/db/20140205-103801F/base -l /Users/backrest/test/backup/db/20140205-103801F/base/postgresql.log -w -s
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings;
use Carp;
#use strict;
use DBI;
use IPC::System::Simple qw(capture);
use Config::IniFiles;
use File::Find;
use File::Basename;
use Getopt::Long;
use Cwd 'abs_path';
use Cwd;
sub trim
use lib dirname($0) . '/../lib';
use BackRest::Utility;
use lib dirname($0) . '/lib';
use BackRestTest::CommonTest;
use BackRestTest::UtilityTest;
use BackRestTest::FileTest;
use BackRestTest::BackupTest;
####################################################################################################################################
# Command line parameters
####################################################################################################################################
my $strLogLevel = 'off'; # Log level for tests
my $strModule = 'all';
my $strModuleTest = 'all';
my $iModuleTestRun = undef;
my $bDryRun = false;
my $bNoCleanup = false;
my $strPgSqlBin;
my $strTestPath;
GetOptions ('pgsql-bin=s' => \$strPgSqlBin,
'test-path=s' => \$strTestPath,
'log-level=s' => \$strLogLevel,
'module=s' => \$strModule,
'module-test=s' => \$strModuleTest,
'module-test-run=s' => \$iModuleTestRun,
'dry-run' => \$bDryRun,
'no-cleanup' => \$bNoCleanup)
or die 'error in command line arguments';
####################################################################################################################################
# Setup
####################################################################################################################################
# Set a neutral umask so tests work as expected
umask(0);
# Set console log level to trace for testing
log_level_set(undef, uc($strLogLevel));
if ($strModuleTest ne 'all' && $strModule eq 'all')
{
local($strBuffer) = @_;
$strBuffer =~ s/^\s+|\s+$//g;
return $strBuffer;
confess "--module must be provided for test \"${strModuleTest}\"";
}
sub execute
if (defined($iModuleTestRun) && $strModuleTest eq 'all')
{
local($strCommand) = @_;
my $strOutput;
confess "--module-test must be provided for run \"${iModuleTestRun}\"";
}
print("$strCommand");
$strOutput = trim(capture($strCommand));
# Make sure PG bin has been defined
if (!defined($strPgSqlBin))
{
confess 'pgsql-bin was not defined';
}
if ($strOutput eq "")
{
print(" ... complete\n\n");
}
else
####################################################################################################################################
# Make sure version number matches in README.md and VERSION
####################################################################################################################################
my $hReadMe;
my $strLine;
my $bMatch = false;
my $strVersion = version_get();
if (!open($hReadMe, '<', dirname($0) . '/../README.md'))
{
confess 'unable to open README.md';
}
while ($strLine = readline($hReadMe))
{
if ($strLine =~ /^\#\#\# v/)
{
print(" ... complete\n$strOutput\n\n");
$bMatch = substr($strLine, 5, length($strVersion)) eq $strVersion;
last;
}
return $strOutput;
}
sub pg_create
if (!$bMatch)
{
local($strPgBinPath, $strTestPath, $strTestDir, $strArchiveDir, $strBackupDir) = @_;
execute("mkdir $strTestPath");
execute("mkdir $strTestPath/$strTestDir");
execute("mkdir $strTestPath/$strTestDir/ts1");
execute("mkdir $strTestPath/$strTestDir/ts2");
execute($strPgBinPath . "/initdb -D $strTestPath/$strTestDir/common -A trust -k");
execute("mkdir $strTestPath/$strBackupDir");
# execute("mkdir -p $strTestPath/$strArchiveDir");
confess "unable to find version ${strVersion} as last revision in README.md";
}
sub pg_start
####################################################################################################################################
# Clean whitespace only if test.pl is being run from the test directory in the backrest repo
####################################################################################################################################
my $hVersion;
if (-e './test.pl' && -e '../bin/pg_backrest.pl' && open($hVersion, '<', '../VERSION'))
{
local($strPgBinPath, $strDbPath, $strPort, $strAchiveCommand) = @_;
my $strCommand = "$strPgBinPath/pg_ctl start -o \"-c port=$strPort -c checkpoint_segments=1 -c wal_level=archive -c archive_mode=on -c archive_command=\'$strAchiveCommand\'\" -D $strDbPath -l $strDbPath/postgresql.log -w -s";
execute($strCommand);
}
my $strTestVersion = readline($hVersion);
sub pg_password_set
{
local($strPgBinPath, $strPath, $strUser, $strPort) = @_;
my $strCommand = "$strPgBinPath/psql --port=$strPort -c \"alter user $strUser with password 'password'\" postgres";
execute($strCommand);
}
sub pg_stop
{
local($strPgBinPath, $strPath) = @_;
my $strCommand = "$strPgBinPath/pg_ctl stop -D $strPath -w -s -m fast";
execute($strCommand);
}
sub pg_drop
{
local($strTestPath) = @_;
my $strCommand = "rm -rf $strTestPath";
execute($strCommand);
}
sub pg_execute
{
local($dbh, $strSql) = @_;
print($strSql);
$sth = $dbh->prepare($strSql);
$sth->execute() or die;
$sth->finish();
print(" ... complete\n\n");
}
sub archive_command_build
{
my $strBackRestBinPath = shift;
my $strDestinationPath = shift;
my $bCompression = shift;
my $bChecksum = shift;
my $strCommand = "$strBackRestBinPath/pg_backrest.pl --stanza=db --config=$strBackRestBinPath/pg_backrest.conf";
# if (!$bCompression)
# {
# $strCommand .= " --no-compression"
# }
#
# if (!$bChecksum)
# {
# $strCommand .= " --no-checksum"
# }
return $strCommand . " archive-push %p";
}
sub wait_for_file
{
my $strDir = shift;
my $strRegEx = shift;
my $iSeconds = shift;
my $lTime = time();
my $hDir;
while ($lTime > time() - $iSeconds)
if (defined($strTestVersion) && $strVersion eq trim($strTestVersion))
{
opendir $hDir, $strDir or die "Could not open dir: $!\n";
my @stryFile = grep(/$strRegEx/i, readdir $hDir);
close $hDir;
if (scalar @stryFile == 1)
{
return;
}
sleep(1);
BackRestTestCommon_Execute(
"find .. -type f -not -path \"../.git/*\" -not -path \"*.DS_Store\" -not -path \"../test/test/*\" " .
"-not -path \"../test/data/*\" " .
"-exec sh -c 'for i;do echo \"\$i\" && sed 's/[[:space:]]*\$//' \"\$i\">/tmp/.\$\$ && cat /tmp/.\$\$ " .
"> \"\$i\";done' arg0 {} + > /dev/null", false, true);
}
die "could not find $strDir/$strRegEx after $iSeconds second(s)";
close($hVersion);
}
sub pgbr_backup
####################################################################################################################################
# Runs tests
####################################################################################################################################
BackRestTestCommon_Setup($strTestPath, $strPgSqlBin, $iModuleTestRun, $bDryRun, $bNoCleanup);
# &log(INFO, "Testing with test_path = " . BackRestTestCommon_TestPathGet() . ", host = {strHost}, user = {strUser}, " .
# "group = {strGroup}");
if ($strModule eq 'all' || $strModule eq 'utility')
{
my $strBackRestBinPath = shift;
my $strCluster = shift;
my $strCommand = "$strBackRestBinPath/pg_backrest.pl --config=$strBackRestBinPath/pg_backrest.conf backup $strCluster";
execute($strCommand);
BackRestTestUtility_Test($strModuleTest);
}
my $strUser = execute('whoami');
my $strTestPath = "/Users/dsteele/test";
my $strDbDir = "db";
my $strArchiveDir = "backup/db/archive";
my $strBackupDir = "backup";
my $strPgBinPath = "/Library/PostgreSQL/9.3/bin";
my $strPort = "6001";
my $strBackRestBinPath = "/Users/dsteele/pg_backrest";
my $strArchiveCommand = archive_command_build($strBackRestBinPath, "$strTestPath/$strArchiveDir", 0, 0);
################################################################################
# Stop the current test cluster if it is running and create a new one
################################################################################
eval {pg_stop($strPgBinPath, "$strTestPath/$strDbDir")};
if ($@)
if ($strModule eq 'all' || $strModule eq 'file')
{
print(" ... unable to stop pg server (ignoring): " . trim($@) . "\n\n")
BackRestTestFile_Test($strModuleTest);
}
pg_drop($strTestPath);
pg_create($strPgBinPath, $strTestPath, $strDbDir, $strArchiveDir, $strBackupDir);
pg_start($strPgBinPath, "$strTestPath/$strDbDir/common", $strPort, $strArchiveCommand);
pg_password_set($strPgBinPath, "$strTestPath/$strDbDir/common", $strUser, $strPort);
if ($strModule eq 'all' || $strModule eq 'backup')
{
BackRestTestBackup_Test($strModuleTest);
}
################################################################################
# Connect and start tests
################################################################################
$dbh = DBI->connect("dbi:Pg:dbname=postgres;port=$strPort;host=127.0.0.1", $strUser,
'password', {AutoCommit => 1});
pg_execute($dbh, "create tablespace ts1 location '$strTestPath/$strDbDir/ts1'");
pg_execute($dbh, "create tablespace ts2 location '$strTestPath/$strDbDir/ts2'");
pg_execute($dbh, "create table test (id int)");
pg_execute($dbh, "create table test_ts1 (id int) tablespace ts1");
pg_execute($dbh, "create table test_ts2 (id int) tablespace ts1");
pg_execute($dbh, "insert into test values (1)");
pg_execute($dbh, "select pg_switch_xlog()");
execute("mkdir -p $strTestPath/$strArchiveDir/0000000100000000");
# Test for archive log file 000000010000000000000001
wait_for_file("$strTestPath/$strArchiveDir/0000000100000000", "^000000010000000000000001\$", 5);
# Turn on log checksum for the next test
$dbh->disconnect();
pg_stop($strPgBinPath, "$strTestPath/$strDbDir/common");
$strArchiveCommand = archive_command_build($strBackRestBinPath, "$strTestPath/$strArchiveDir", 0, 1);
pg_start($strPgBinPath, "$strTestPath/$strDbDir/common", $strPort, $strArchiveCommand);
$dbh = DBI->connect("dbi:Pg:dbname=postgres;port=$strPort;host=127.0.0.1", $strUser,
'password', {AutoCommit => 1});
# Write another value into the test table
pg_execute($dbh, "insert into test values (2)");
pg_execute($dbh, "select pg_switch_xlog()");
# Test for archive log file 000000010000000000000002
wait_for_file("$strTestPath/$strArchiveDir/0000000100000000", "^000000010000000000000002-([a-f]|[0-9]){40}\$", 5);
# Turn on log compression and checksum for the next test
$dbh->disconnect();
pg_stop($strPgBinPath, "$strTestPath/$strDbDir/common");
$strArchiveCommand = archive_command_build($strBackRestBinPath, "$strTestPath/$strArchiveDir", 1, 1);
pg_start($strPgBinPath, "$strTestPath/$strDbDir/common", $strPort, $strArchiveCommand);
$dbh = DBI->connect("dbi:Pg:dbname=postgres;port=$strPort;host=127.0.0.1", $strUser,
'password', {AutoCommit => 1});
# Write another value into the test table
pg_execute($dbh, "insert into test values (3)");
pg_execute($dbh, "select pg_switch_xlog()");
# Test for archive log file 000000010000000000000003
wait_for_file("$strTestPath/$strArchiveDir/0000000100000000", "^000000010000000000000003-([a-f]|[0-9]){40}\\.gz\$", 5);
$dbh->disconnect();
################################################################################
# Stop the server
################################################################################
#pg_stop($strPgBinPath, "$strTestPath/$strDbDir/common");
################################################################################
# Start an offline backup
################################################################################
#pgbr_backup($strBackRestBinPath, "db");
if (!$bDryRun)
{
&log(ASSERT, 'TESTS COMPLETED SUCCESSFULLY (DESPITE ANY ERROR MESSAGES YOU SAW)');
}