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

Text execution improvements:

1) Tests for all operating systems can now be run with a single command.
2) Tests can be run in parallel with --process-max.
3) Container generation now integrated into test.pl
4) Some basic test documentation.
This commit is contained in:
David Steele
2016-01-09 08:21:53 -05:00
parent 4a384ffbdb
commit c8f863fbab
12 changed files with 670 additions and 268 deletions

View File

@@ -184,7 +184,6 @@ if (@stryOutput == 0)
if ($oManifest->isBackRest())
{
push(@stryOutput, 'help');
push(@stryOutput, 'markdown');
}
}
@@ -203,7 +202,8 @@ for my $strOutput (@stryOutput)
docProcess("${strBasePath}/xml/index.xml", "${strBasePath}/../README.md", $oManifest);
docProcess("${strBasePath}/xml/change-log.xml", "${strBasePath}/../CHANGELOG.md", $oManifest);
}
elsif ($strOutput eq 'markdown')
if ($strOutput eq 'markdown')
{
my $oMarkdown =
new BackRestDoc::Markdown::DocMarkdown

View File

@@ -198,6 +198,8 @@ sub sectionProcess
my $strMarkdown = '#' . ('#' x $iDepth) . ' ' . $self->processText($oSection->nodeGet('title')->textGet());
my $strLastChild = undef;
foreach my $oChild ($oSection->nodeList())
{
&log(DEBUG, (' ' x ($iDepth + 2)) . 'process child ' . $oChild->nameGet());
@@ -288,14 +290,22 @@ sub sectionProcess
# Add code block
elsif ($oChild->nameGet() eq 'code-block')
{
# $oSectionBodyElement->
# addNew(HTML_DIV, 'code-block',
# {strContent => $oChild->valueGet()});
if ($oChild->paramTest('title'))
{
$strMarkdown .= "\n\n_" . $oChild->paramGet('title') . "_:";
}
$strMarkdown .= "\n```\n" . trim($oChild->valueGet()) . "\n```";
}
# Add descriptive text
elsif ($oChild->nameGet() eq 'p')
{
$strMarkdown .= "\n\n" . $self->processText($oChild->textGet());
if (defined($strLastChild) && $strLastChild ne 'code-block')
{
$strMarkdown .= "\n";
}
$strMarkdown .= "\n" . $self->processText($oChild->textGet());
}
# Add option descriptive text
elsif ($oChild->nameGet() eq 'option-description')
@@ -342,6 +352,8 @@ sub sectionProcess
{
# $self->sectionChildProcess($oSection, $oChild, $iDepth + 1);
}
$strLastChild = $oChild->nameGet();
}
# Return from function and log return values if any

View File

@@ -29,6 +29,7 @@
<source key="index"/>
<source key="user-guide"/>
<source key="reference" type="custom"/>
<source key="test"/>
</source-list>
<render-list>
@@ -42,5 +43,9 @@
<render type="pdf" file="{[pdf-file]}">
<render-source key="user-guide"/>
</render>
<render type="markdown">
<render-source key="test" file="../../../test/README.md"/>
</render>
</render-list>
</doc>

60
doc/xml/test.xml Normal file
View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE doc SYSTEM "doc.dtd">
<doc subtitle="Regression, Unit, &amp; Integration Testing" toc="y">
<section id="introduction">
<title>Introduction</title>
<p><backrest/> uses Docker to run tests and generate documentation. Docker's light-weight virualization provides the a good balance between proper OS emulation and performance (especially startup)</p>
<p>A `Vagrantfile` is provided that contains the complete configuration required to run <backrest/> tests and build documentation. If Vagrant is not suitable then the `Vagrantfile` still contains the configuration steps required to build a test system.</p>
<p>Note that this is not required for normal operation of <backrest/>.</p>
</section>
<section id="testing">
<title>Testing</title>
<p>The easiest way to start testing <backrest/> is with the included `Vagrantfile`.</p>
<code-block title="Build Vagrant and Logon">
cd test
vagrant up
vagrant ssh
</code-block>
<p>The <code>vagrant up</code> step could take some time as a number of Docker containers must also be built. The <code>vagrant up</code> command automatically logs onto the VM.</p>
<code-block title="Run All Tests">
/backrest/test/test.pl
</code-block>
<code-block title="Run Tests for a Specific OS">
/backrest/test/test.pl --vm=co6
</code-block>
<code-block title="Run Tests for a Specific OS and Module">
/backrest/test/test.pl --vm=co6 --module=backup
</code-block>
<code-block title="Run Tests for a Specific OS, Module, and Test">
/backrest/test/test.pl --vm=co6 --module=backup --full
</code-block>
<code-block title="Run Tests for a Specific OS, Module, Test, and Thread Max">
/backrest/test/test.pl --vm=co6 --module=backup --full --thread-max=4
</code-block>
<p>Note that thread-max is only applicable to the <id>synthetic</id> and <id>full</id> tests in the <id>backup</id> module.</p>
<code-block title="Run Tests for a Specific OS, Module, Test, Thread Max, and Database Version">
/backrest/test/test.pl --vm=co6 --module=backup --full --thread-max=4 --db-version=9.4
</code-block>
<p>Note that db-version is only applicable to the <id>full</id> test in the <id>backup</id> module.</p>
<code-block title="Iterate All Possible Test Combinations">
/backrest/test/test.pl --dry-run
</code-block>
</section>
</doc>

View File

@@ -425,6 +425,7 @@ sub log
my $strMessage = shift;
my $iCode = shift;
my $bSuppressLog = shift;
my $iIndent = shift;
# Set defaults
$bSuppressLog = defined($bSuppressLog) ? $bSuppressLog : false;
@@ -459,7 +460,15 @@ sub log
$strMessageFormat = (defined($iCode) ? "[${iCode}]: " : '') . $strMessageFormat;
# Indent subsequent lines of the message if it has more than one line - makes the log more readable
$strMessageFormat =~ s/\n/\n /g;
if (defined($iIndent))
{
my $strIndent = ' ' x $iIndent;
$strMessageFormat =~ s/\n/\n${strIndent}/g;
}
else
{
$strMessageFormat =~ s/\n/\n /g;
}
if ($strLevel eq TRACE || $strLevel eq TEST)
{

View File

@@ -251,7 +251,7 @@ sub lineRead
$self->waitPid();
# Only error if reading from the input stream
if (!defined($bInRead) || $bInRead)
if (defined($bError) && $bError)
{
confess &log(ERROR, "unexpected EOF", ERROR_FILE_READ);
}
@@ -297,7 +297,8 @@ sub lineRead
}
# Return the line that was found and adjust the buffer position
my $strLine = substr($self->{strBuffer}, $self->{iBufferPos}, $iLineFeedPos - $self->{iBufferPos});
my $strLine = $iLineFeedPos - $self->{iBufferPos} == 0 ? '' :
substr($self->{strBuffer}, $self->{iBufferPos}, $iLineFeedPos - $self->{iBufferPos});
$self->{iBufferPos} = $iLineFeedPos + 1;
return $strLine;

58
test/README.md Normal file
View File

@@ -0,0 +1,58 @@
# pgBackRest - Regression, Unit, & Integration Testing
## Introduction
pgBackRest uses Docker to run tests and generate documentation. Docker's light-weight virualization provides the a good balance between proper OS emulation and performance (especially startup)
A `Vagrantfile` is provided that contains the complete configuration required to run pgBackRest tests and build documentation. If Vagrant is not suitable then the `Vagrantfile` still contains the configuration steps required to build a test system.
Note that this is not required for normal operation of pgBackRest.
## Testing
The easiest way to start testing pgBackRest is with the included `Vagrantfile`.
_Build Vagrant and Logon_:
```
cd test
vagrant up
vagrant ssh
```
The `vagrant up` step could take some time as a number of Docker containers must also be built. The `vagrant up` command automatically logs onto the VM.
_Run All Tests_:
```
/backrest/test/test.pl
```
_Run Tests for a Specific OS_:
```
/backrest/test/test.pl --vm=co6
```
_Run Tests for a Specific OS and Module_:
```
/backrest/test/test.pl --vm=co6 --module=backup
```
_Run Tests for a Specific OS, Module, and Test_:
```
/backrest/test/test.pl --vm=co6 --module=backup --full
```
_Run Tests for a Specific OS, Module, Test, and Thread Max_:
```
/backrest/test/test.pl --vm=co6 --module=backup --full --thread-max=4
```
Note that thread-max is only applicable to the `synthetic` and `full` tests in the `backup` module.
_Run Tests for a Specific OS, Module, Test, Thread Max, and Database Version_:
```
/backrest/test/test.pl --vm=co6 --module=backup --full --thread-max=4 --db-version=9.4
```
Note that db-version is only applicable to the `full` test in the `backup` module.
_Iterate All Possible Test Combinations_:
```
/backrest/test/test.pl --dry-run
```

4
test/Vagrantfile vendored
View File

@@ -30,8 +30,8 @@ Vagrant.configure(2) do |config|
/usr/local/texlive/2015/bin/x86_64-linux/tlmgr install caption xcolor listings parskip helvetic ltablex titlesec \
epstopdf courier sectsty pgf ms
# Build docker images
/backrest/test/container.pl
# Build vm images
/backrest/test/test.pl --vm-build
SHELL
# Don't share the default vagrant folder

View File

@@ -21,6 +21,7 @@ use Symbol 'gensym';
use lib dirname($0) . '/../lib';
use BackRest::Common::Log;
use BackRest::Common::Wait;
use BackRest::Protocol::IO;
####################################################################################################################################
# Operation constants
@@ -121,6 +122,9 @@ sub begin
$self->{pId} = open3(undef, $self->{hOut}, $self->{hError}, $self->{strCommand});
# Create select objects
$self->{oIO} = new BackRest::Protocol::IO($self->{hOut}, undef, $self->{hError}, undef, 30, 65536);
if (!defined($self->{hError}))
{
confess 'STDERR handle is undefined';
@@ -138,67 +142,67 @@ sub endRetry
my
(
$strOperation,
$strTest
$strTest,
$bWait
) =
logDebugParam
(
OP_EXECUTE_TEST_END_RETRY, \@_,
{name => 'strTest', required => false, trace => true}
{name => 'strTest', required => false, trace => true},
{name => 'bWait', required => false, default => true, trace => true}
);
# Create select objects
my $oErrorSelect = IO::Select->new();
$oErrorSelect->add($self->{hError});
my $oOutSelect = IO::Select->new();
$oOutSelect->add($self->{hOut});
# Drain the output and error streams and look for test points
# my $iWait = $bWait ? .05 : 0;
while(waitpid($self->{pId}, WNOHANG) == 0)
{
# Drain the stderr stream
if ($oErrorSelect->can_read(0))
my $bFound = false;
# # Drain the stderr stream
# !!! This is a good idea but can only be done with the IO object has separate buffers for stdin and stderr
# while (my $strLine = $self->{oIO}->lineRead(0, false, false))
# {
# $bFound = true;
# $self->{strErrorLog} .= "$strLine\n";
# }
# Drain the stdout stream and look for test points
while (defined(my $strLine = $self->{oIO}->lineRead(0, true, false)))
{
while (my $strLine = readline($self->{hError}))
$self->{strOutLog} .= "$strLine\n";
$bFound = true;
if (defined($strTest) && testCheck($strLine, $strTest))
{
$self->{strErrorLog} .= $strLine;
&log(DEBUG, "Found test ${strTest}");
return true;
}
}
# Drain the stdout stream and look for test points
if ($oOutSelect->can_read(.05))
if (!$bWait)
{
while (my $strLine = readline($self->{hOut}))
{
$self->{strOutLog} .= $strLine;
return undef;
}
if (defined($strTest) && testCheck($strLine, $strTest))
{
&log(DEBUG, "Found test ${strTest}");
return true;
}
}
if (!$bFound)
{
waitHiRes(.05);
}
}
# Check the exit status
my $iExitStatus = ${^CHILD_ERROR_NATIVE} >> 8;
# Drain the stderr stream
if ($oErrorSelect->can_read(0))
# Drain the stdout stream
while (defined(my $strLine = $self->{oIO}->lineRead(0, true, false)))
{
while (my $strLine = readline($self->{hError}))
{
$self->{strErrorLog} .= $strLine;
}
$self->{strOutLog} .= "$strLine\n";
}
# Drain the stdout stream
if ($oOutSelect->can_read(0))
# Drain the stderr stream
while (defined(my $strLine = $self->{oIO}->lineRead(0, false, false)))
{
while (my $strLine = readline($self->{hOut}))
{
$self->{strOutLog} .= $strLine;
}
$self->{strErrorLog} .= "$strLine\n";
}
# Pass the log to the LogTest object
@@ -278,12 +282,14 @@ sub end
my
(
$strOperation,
$strTest
$strTest,
$bWait
) =
logDebugParam
(
OP_EXECUTE_TEST_END, \@_,
{name => 'strTest', required => false}
{name => 'strTest', required => false, trace => true},
{name => 'bWait', required => false, default => true, trace => true}
);
# If retry is not defined then run endRetry() one time
@@ -291,7 +297,7 @@ sub end
if (!defined($self->{iRetrySeconds}))
{
$iExitStatus = $self->endRetry($strTest);
$iExitStatus = $self->endRetry($strTest, $bWait);
}
# Else loop until success or timeout
else
@@ -302,7 +308,7 @@ sub end
{
$self->{bRetry} = false;
$self->begin();
$iExitStatus = $self->endRetry($strTest);
$iExitStatus = $self->endRetry($strTest, $bWait);
if ($self->{bRetry})
{
@@ -314,7 +320,7 @@ sub end
if ($self->{bRetry})
{
$self->begin();
$iExitStatus = $self->endRetry($strTest);
$iExitStatus = $self->endRetry($strTest, $bWait);
}
}

View File

@@ -258,10 +258,13 @@ sub BackRestTestConfig_Test
#-------------------------------------------------------------------------------------------------------------------------------
# Test command-line options
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'option')
my $strThisTest = 'option';
if ($strTest eq 'all' || $strTest eq $strThisTest)
{
$iRun = 0;
&log(INFO, "Option module\n");
&log(INFO, "Test ${strThisTest}\n");
if (BackRestTestCommon_Run(++$iRun, 'backup with no stanza'))
{
@@ -543,10 +546,13 @@ sub BackRestTestConfig_Test
#-------------------------------------------------------------------------------------------------------------------------------
# Test mixed command-line/config
#-------------------------------------------------------------------------------------------------------------------------------
if ($strTest eq 'all' || $strTest eq 'config')
$strThisTest = 'config';
if ($strTest eq 'all' || $strTest eq $strThisTest)
{
$iRun = 0;
&log(INFO, "Config module\n");
&log(INFO, "Test ${strThisTest}\n");
BackRestTestCommon_Create();

View File

@@ -1,7 +1,7 @@
#!/usr/bin/perl
####################################################################################################################################
# container.pl - Build docker containers for testing and documentation
# Container.pm - Build docker containers for testing and documentation
####################################################################################################################################
package BackRestTest::Docker::Container;
####################################################################################################################################
# Perl includes
@@ -10,112 +10,18 @@ use strict;
use warnings FATAL => qw(all);
use Carp qw(confess longmess);
# Convert die to confess to capture the stack trace
$SIG{__DIE__} = sub { Carp::confess @_ };
use Cwd qw(abs_path);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname);
use Getopt::Long qw(GetOptions);
use Scalar::Util qw(blessed);
# use Cwd qw(abs_path);
# use Pod::Usage qw(pod2usage);
# use Scalar::Util qw(blessed);
use lib dirname($0) . '/../lib';
use BackRest::Common::Ini;
# use BackRest::Common::Ini;
use BackRest::Common::Log;
use BackRest::FileCommon;
# use BackRest::Db;
use lib dirname($0) . '/lib';
use BackRestTest::Common::ExecuteTest;
# use BackRestTest::CommonTest;
# use BackRestTest::CompareTest;
# use BackRestTest::ConfigTest;
# use BackRestTest::FileTest;
# use BackRestTest::HelpTest;
####################################################################################################################################
# Usage
####################################################################################################################################
=head1 NAME
container.pl - Docker Container Build
=head1 SYNOPSIS
container.pl [options]
Build Options:
--os os to build (defaults to all)
Configuration Options:
--log-level log level to use for tests (defaults to info)
--quiet, -q equivalent to --log-level=off
General Options:
--version display version and exit
--help display usage and exit
=cut
####################################################################################################################################
# Command line parameters
####################################################################################################################################
my $strLogLevel = 'info';
my $bVersion = false;
my $bHelp = false;
my $bQuiet = false;
GetOptions ('q|quiet' => \$bQuiet,
'version' => \$bVersion,
'help' => \$bHelp,
'log-level=s' => \$strLogLevel)
or pod2usage(2);
# Display version and exit if requested
if ($bVersion || $bHelp)
{
syswrite(*STDOUT, 'pgBackRest ' . BACKREST_VERSION . " Docker Container Build\n");
if ($bHelp)
{
syswrite(*STDOUT, "\n");
pod2usage();
}
exit 0;
}
if (@ARGV > 0)
{
syswrite(*STDOUT, "invalid parameter\n\n");
pod2usage();
}
####################################################################################################################################
# Setup
####################################################################################################################################
# Set a neutral umask so tests work as expected
umask(0);
# Set console log level
if ($bQuiet)
{
$strLogLevel = 'off';
}
logLevelSet(undef, uc($strLogLevel));
# Create temp path
my $strTempPath = dirname(abs_path($0)) . '/.vagrant/docker';
if (!-e $strTempPath)
{
mkdir $strTempPath
or confess &log(ERROR, "unable to create ${strTempPath}");
}
####################################################################################################################################
# Valid OS list
@@ -285,8 +191,17 @@ sub perlInstall
####################################################################################################################################
# Build containers
####################################################################################################################################
eval
sub containerBuild
{
# Create temp path
my $strTempPath = dirname(abs_path($0)) . '/.vagrant/docker';
if (!-e $strTempPath)
{
mkdir $strTempPath
or confess &log(ERROR, "unable to create ${strTempPath}");
}
# Create SSH key (if it does not already exist)
if (-e "${strTempPath}/id_rsa")
{
@@ -296,7 +211,7 @@ eval
{
&log(INFO, "Building SSH keys...");
executeTest("ssh-keygen -f ${strTempPath}/id_rsa -t rsa -b 768 -N ''");
executeTest("ssh-keygen -f ${strTempPath}/id_rsa -t rsa -b 768 -N ''", {bSuppressStdErr => true});
}
foreach my $strOS (@stryOS)
@@ -337,7 +252,9 @@ eval
}
elsif ($strOS eq OS_U12 || $strOS eq OS_U14)
{
$strImage .= "RUN apt-get -y install openssh-server\n";
$strImage .=
"RUN apt-get update\n" .
"RUN apt-get -y install openssh-server\n";
}
$strImage .=
@@ -441,7 +358,8 @@ eval
# Write the image
fileStringWrite("${strTempPath}/${strImageName}", "$strImage\n", false);
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}");
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}",
{bSuppressStdErr => true});
# Db image
###########################################################################################################################
@@ -512,7 +430,9 @@ eval
# Write the image
fileStringWrite("${strTempPath}/${strImageName}", "${strImage}\n", false);
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}");
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}",
{bSuppressStdErr => true});
# Db Doc image
###########################################################################################################################
@@ -527,7 +447,9 @@ eval
# Write the image
fileStringWrite("${strTempPath}/${strImageName}", "${strImage}\n", false);
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}");
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}",
{bSuppressStdErr => true});
# Backup image
###########################################################################################################################
@@ -545,7 +467,9 @@ eval
# Write the image
fileStringWrite("${strTempPath}/${strImageName}", "${strImage}\n", false);
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}");
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}",
{bSuppressStdErr => true});
# Backup Doc image
###########################################################################################################################
@@ -568,7 +492,9 @@ eval
# Write the image
fileStringWrite("${strTempPath}/${strImageName}", "${strImage}\n", false);
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}");
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}",
{bSuppressStdErr => true});
# Test image
###########################################################################################################################
@@ -605,21 +531,12 @@ eval
# Write the image
fileStringWrite("${strTempPath}/${strImageName}", "${strImage}\n", false);
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}");
executeTest("docker build -f ${strTempPath}/${strImageName} -t backrest/${strImageName} ${strTempPath}",
{bSuppressStdErr => true});
}
};
if ($@)
{
my $oMessage = $@;
# If a backrest exception then return the code - don't confess
if (blessed($oMessage) && $oMessage->isa('BackRest::Common::Exception'))
{
syswrite(*STDOUT, $oMessage->trace());
exit $oMessage->code();
}
syswrite(*STDOUT, $oMessage);
exit 255;;
}
push @EXPORT, qw(containerBuild);
1;

View File

@@ -17,11 +17,15 @@ use File::Basename qw(dirname);
use Getopt::Long qw(GetOptions);
use Cwd qw(abs_path);
use Pod::Usage qw(pod2usage);
use POSIX qw(ceil);
use Time::HiRes qw(gettimeofday);
use Scalar::Util qw(blessed);
use lib dirname($0) . '/../lib';
use BackRest::Common::Ini;
use BackRest::Common::Log;
use BackRest::Common::String;
use BackRest::Common::Wait;
use BackRest::Db;
use lib dirname($0) . '/lib';
@@ -30,9 +34,9 @@ use BackRestTest::Common::ExecuteTest;
use BackRestTest::CommonTest;
use BackRestTest::CompareTest;
use BackRestTest::ConfigTest;
use BackRestTest::Docker::Container;
use BackRestTest::FileTest;
use BackRestTest::HelpTest;
# use BackRestTest::IniTest;
####################################################################################################################################
# Usage
@@ -47,7 +51,7 @@ test.pl - pgBackRest Unit Tests
test.pl [options]
Test Options:
--module test module to execute:
--module test module to execute
--test execute the specified test in a module
--run execute only the specified test run
--thread-max max threads to run for backup/restore (default 4)
@@ -58,12 +62,18 @@ test.pl [options]
--log-force force overwrite of current test log files
Configuration Options:
--exe backup executable
--exe pgBackRest executable
--psql-bin path to the psql executables (e.g. /usr/lib/postgresql/9.3/bin/)
--test-path path where tests are executed (defaults to ./test)
--log-level log level to use for tests (defaults to INFO)
--quiet, -q equivalent to --log-level=off
VM Options:
--vm-build build Docker containers
--vm execute in a docker container (u12, u14, co6, co7)
--vm-out Show VM output (default false)
--process-max max VMs to run in parallel (default 1)
General Options:
--version display version and exit
--help display usage and exit
@@ -73,11 +83,13 @@ test.pl [options]
# Command line parameters
####################################################################################################################################
my $strLogLevel = 'info';
my $strOS = undef;
my $strOS = 'all';
my $bVmOut = false;
my $strModule = 'all';
my $strModuleTest = 'all';
my $iModuleTestRun = undef;
my $iThreadMax = undef;
my $iProcessMax = 1;
my $bDryRun = false;
my $bNoCleanup = false;
my $strPgSqlBin;
@@ -89,6 +101,7 @@ my $bQuiet = false;
my $bInfinite = false;
my $strDbVersion = 'all';
my $bLogForce = false;
my $bVmBuild = false;
my $strCommandLine = join(' ', @ARGV);
@@ -99,11 +112,14 @@ GetOptions ('q|quiet' => \$bQuiet,
'exes=s' => \$strExe,
'test-path=s' => \$strTestPath,
'log-level=s' => \$strLogLevel,
'os=s' => \$strOS,
'vm=s' => \$strOS,
'vm-out' => \$bVmOut,
'vm-build' => \$bVmBuild,
'module=s' => \$strModule,
'test=s' => \$strModuleTest,
'run=s' => \$iModuleTestRun,
'thread-max=s' => \$iThreadMax,
'process-max=s' => \$iProcessMax,
'dry-run' => \$bDryRun,
'no-cleanup' => \$bNoCleanup,
'infinite' => \$bInfinite,
@@ -131,27 +147,6 @@ if (@ARGV > 0)
pod2usage();
}
####################################################################################################################################
# Start OS VM and run
####################################################################################################################################
if (defined($strOS))
{
executeTest("docker rm -f ${strOS}-test", {bSuppressError => true});
executeTest("docker run -itd -h ${strOS}-test --name=${strOS}-test -v /backrest:/backrest backrest/${strOS}-test");
$strCommandLine =~ s/\-\-os\=\S*//g;
$strCommandLine =~ s/\-\-test-path\=\S*//g;
system("docker exec -it -u vagrant ${strOS}-test $0 ${strCommandLine} --test-path=/home/vagrant/test");
if (!$bNoCleanup)
{
executeTest("docker rm -f ${strOS}-test");
}
exit 0;
}
####################################################################################################################################
# Setup
####################################################################################################################################
@@ -181,55 +176,21 @@ if (defined($iModuleTestRun) && $strModuleTest eq 'all')
confess "--test must be provided for --run=\"${iModuleTestRun}\"";
}
# Search for psql bin
my @stryTestVersion;
my @stryVersionSupport = versionSupport();
if (!defined($strPgSqlBin))
{
# Distribution-specific paths where the PostgreSQL binaries may be located
my @strySearchPath =
(
'/usr/lib/postgresql/VERSION/bin', # Debian/Ubuntu
'/usr/pgsql-VERSION/bin', # CentOS/RHEL/Fedora
'/Library/PostgreSQL/VERSION/bin', # OSX
'/usr/local/bin' # BSD
);
foreach my $strSearchPath (@strySearchPath)
{
for (my $iVersionIdx = @stryVersionSupport - 1; $iVersionIdx >= 0; $iVersionIdx--)
{
if ($strDbVersion eq 'all' || $strDbVersion eq 'max' && @stryTestVersion == 0 ||
$strDbVersion eq $stryVersionSupport[$iVersionIdx])
{
my $strVersionPath = $strSearchPath;
$strVersionPath =~ s/VERSION/$stryVersionSupport[$iVersionIdx]/g;
if (-e "${strVersionPath}/initdb")
{
&log(INFO, "FOUND pgsql-bin at ${strVersionPath}");
push @stryTestVersion, $strVersionPath;
}
}
}
}
# Make sure at least one version of postgres was found
@stryTestVersion > 0
or confess 'pgsql-bin was not defined and postgres could not be located automatically';
}
else
{
push @stryTestVersion, $strPgSqlBin;
}
# Check thread total
if (defined($iThreadMax) && ($iThreadMax < 1 || $iThreadMax > 32))
{
confess 'thread-max must be between 1 and 32';
}
####################################################################################################################################
# Build Docker containers
####################################################################################################################################
if ($bVmBuild)
{
containerBuild();
exit 0;
}
####################################################################################################################################
# Make sure version number matches in the change log.
####################################################################################################################################
@@ -257,28 +218,396 @@ if (!$bMatch)
confess 'unable to find version ' . BACKREST_VERSION . " as last revision in ${strChangeLogFile}";
}
####################################################################################################################################
# Clean whitespace only if test.pl is being run from the test directory in the backrest repo
####################################################################################################################################
if (-e './test.pl' && -e '../bin/pg_backrest')
{
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);
}
####################################################################################################################################
# Runs tests
####################################################################################################################################
# &log(INFO, "Testing with test_path = " . BackRestTestCommon_TestPathGet() . ", host = {strHost}, user = {strUser}, " .
# "group = {strGroup}");
my $iRun = 0;
eval
{
################################################################################################################################
# Define tests
################################################################################################################################
my $oTestDefinition =
{
module =>
[
# Help tests
{
name => 'help',
test =>
[
{
name => 'help'
}
]
},
# Config tests
{
name => 'config',
test =>
[
{
name => 'option'
},
{
name => 'config'
}
]
},
# File tests
{
name => 'file',
test =>
[
{
name => 'path_create'
},
{
name => 'move'
},
{
name => 'compress'
},
{
name => 'wait'
},
{
name => 'manifest'
},
{
name => 'list'
},
{
name => 'remove'
},
{
name => 'hash'
},
{
name => 'exists'
},
{
name => 'copy'
}
]
},
# Backup tests
{
name => 'backup',
test =>
[
{
name => 'archive-push',
total => 8
},
{
name => 'archive-stop',
total => 6
},
{
name => 'archive-get',
total => 8
},
{
name => 'expire',
total => 1
},
{
name => 'synthetic',
total => 8,
thread => true
},
{
name => 'full',
total => 8,
thread => true
}
]
}
]
};
my $oyTestRun = [];
################################################################################################################################
# Start VM and run
################################################################################################################################
if ($strOS ne 'none')
{
# Determine which tests to run
my $iTestsToRun = 0;
foreach my $oModule (@{$$oTestDefinition{module}})
{
if ($strModule eq $$oModule{name} || $strModule eq 'all')
{
&log(DEBUG, "Select Module $$oModule{name}");
foreach my $oTest (@{$$oModule{test}})
{
if ($strModuleTest eq $$oTest{name} || $strModuleTest eq 'all')
{
&log(DEBUG, " Select Test $$oTest{name}");
my $iTestRunMin = defined($iModuleTestRun) ? $iModuleTestRun : (defined($$oTest{total}) ? 1 : -1);
my $iTestRunMax = defined($iModuleTestRun) ? $iModuleTestRun : (defined($$oTest{total}) ? $$oTest{total} : -1);
if (defined($$oTest{total}) && $iTestRunMax > $$oTest{total})
{
confess &log(ERROR, "invalid run - must be >= 1 and <= $$oTest{total}")
}
for (my $iTestRunIdx = $iTestRunMin; $iTestRunIdx <= $iTestRunMax; $iTestRunIdx++)
{
&log(DEBUG, " Select Run $iTestRunIdx");
my $stryTestOS = [];
if ($strOS eq 'all')
{
$stryTestOS = ['u12', 'u14', 'co6', 'co7'];
}
else
{
$stryTestOS = [$strOS];
}
my $iyThreadMax = [defined($iThreadMax) ? $iThreadMax : 1];
if (defined($$oTest{thread}) && $$oTest{thread} && !defined($iThreadMax))
{
$iyThreadMax = [1, 4];
}
foreach my $iThreadTestMax (@{$iyThreadMax})
{
foreach my $strTestOS (@{$stryTestOS})
{
my $oTestRun =
{
os => $strTestOS,
module => $$oModule{name},
test => $$oTest{name},
run => $iTestRunIdx == -1 ? undef : $iTestRunIdx,
thread => $iThreadTestMax
};
push(@{$oyTestRun}, $oTestRun);
$iTestsToRun++;
}
}
}
}
}
}
}
if ($iTestsToRun == 0)
{
confess &log(ERROR, 'no tests were selected');
}
my $iTestFail = 0;
my $oyProcess = [];
if (!$bDryRun)
{
for (my $iProcessIdx = 0; $iProcessIdx < 8; $iProcessIdx++)
{
# &log(INFO, "stop test-${iProcessIdx}");
push(@{$oyProcess}, undef);
executeTest("docker rm -f test-${iProcessIdx}", {bSuppressError => true});
}
}
my $iTestIdx = 0;
my $iProcessTotal;
my $iTestMax = @{$oyTestRun};
my $lStartTime = time();
# foreach my $oTest (@{$oyTestRun})
do
{
do
{
$iProcessTotal = 0;
for (my $iProcessIdx = 0; $iProcessIdx < $iProcessMax; $iProcessIdx++)
{
if (defined($$oyProcess[$iProcessIdx]))
{
my $oExecDone = $$oyProcess[$iProcessIdx]{exec};
my $strTestDone = $$oyProcess[$iProcessIdx]{test};
my $iTestDoneIdx = $$oyProcess[$iProcessIdx]{idx};
my $iExitStatus = $oExecDone->end(undef, $iProcessMax == 1);
if (defined($iExitStatus))
{
my $fTestElapsedTime = ceil((gettimeofday() - $$oyProcess[$iProcessIdx]{start_time}) * 100) / 100;
if (!($iExitStatus == 0 || $iExitStatus == 255))
{
&log(ERROR, "${strTestDone} (err${iExitStatus}-${fTestElapsedTime}s)" .
(defined($oExecDone->{strOutLog}) ? ":\n\n" . trim($oExecDone->{strOutLog}) . "\n" : ''),
undef, undef, 4);
$iTestFail++;
}
else
{
&log( INFO, "${strTestDone} (${fTestElapsedTime}s)".
($bVmOut ? ":\n\n" . trim($oExecDone->{strOutLog}) . "\n" : ''), undef, undef, 4);
}
if (!$bNoCleanup)
{
executeTest("docker rm -f test-${iProcessIdx}");
}
$$oyProcess[$iProcessIdx] = undef;
}
else
{
$iProcessTotal++;
}
}
}
if ($iProcessTotal == $iProcessMax)
{
waitHiRes(.1);
}
}
while ($iProcessTotal == $iProcessMax);
for (my $iProcessIdx = 0; $iProcessIdx < $iProcessMax; $iProcessIdx++)
{
if (!defined($$oyProcess[$iProcessIdx]) && $iTestIdx < @{$oyTestRun})
{
my $oTest = $$oyTestRun[$iTestIdx];
$iTestIdx++;
my $strTest = sprintf('P%0' . length($iProcessMax) . 'd-T%0' . length($iTestMax) . 'd/%0' .
length($iTestMax) . "d - ", $iProcessIdx, $iTestIdx, $iTestMax) .
"vm=$$oTest{os}, module=$$oTest{module}, test=$$oTest{test}" .
(defined($$oTest{run}) ? ", run=$$oTest{run}" : '') .
(defined($$oTest{thread}) ? ", thread-max=$$oTest{thread}" : '');
my $strImage = 'test-' . $iProcessIdx;
&log($bDryRun ? INFO : DEBUG, $strTest);
if (!$bDryRun)
{
executeTest("docker run -itd -h $$oTest{os}-test --name=${strImage}" .
" -v /backrest:/backrest backrest/$$oTest{os}-test");
}
$strCommandLine =~ s/\-\-os\=\S*//g;
$strCommandLine =~ s/\-\-test-path\=\S*//g;
$strCommandLine =~ s/\-\-module\=\S*//g;
$strCommandLine =~ s/\-\-test\=\S*//g;
$strCommandLine =~ s/\-\-run\=\S*//g;
my $strCommand = "docker exec -i -u vagrant ${strImage} $0 ${strCommandLine} --test-path=/home/vagrant/test" .
" --vm=none --module=$$oTest{module} --test=$$oTest{test}" .
(defined($$oTest{run}) ? " --run=$$oTest{run}" : '') .
(defined($$oTest{thread}) ? " --thread-max=$$oTest{thread}" : '') .
" --no-cleanup";
&log(DEBUG, $strCommand);
if (!$bDryRun)
{
my $fTestStartTime = gettimeofday();
my $oExec = new BackRestTest::Common::ExecuteTest($strCommand, {bSuppressError => true});
$oExec->begin();
my $oProcess =
{
exec => $oExec,
test => $strTest,
idx => $iTestIdx,
start_time => $fTestStartTime
};
$$oyProcess[$iProcessIdx] = $oProcess;
}
$iProcessTotal++;
}
}
}
while ($iProcessTotal > 0);
&log(INFO, 'TESTS COMPLETED ' . ($iTestFail == 0 ? 'SUCCESSFULLY' : "WITH ${iTestFail} FAILURE(S)") .
' (' . (time() - $lStartTime) . 's)');
exit 0;
}
################################################################################################################################
# Search for psql
################################################################################################################################
my @stryTestVersion;
my @stryVersionSupport = versionSupport();
if (!defined($strPgSqlBin))
{
# Distribution-specific paths where the PostgreSQL binaries may be located
my @strySearchPath =
(
'/usr/lib/postgresql/VERSION/bin', # Debian/Ubuntu
'/usr/pgsql-VERSION/bin', # CentOS/RHEL/Fedora
'/Library/PostgreSQL/VERSION/bin', # OSX
'/usr/local/bin' # BSD
);
foreach my $strSearchPath (@strySearchPath)
{
for (my $iVersionIdx = @stryVersionSupport - 1; $iVersionIdx >= 0; $iVersionIdx--)
{
if ($strDbVersion eq 'all' || $strDbVersion eq 'max' && @stryTestVersion == 0 ||
$strDbVersion eq $stryVersionSupport[$iVersionIdx])
{
my $strVersionPath = $strSearchPath;
$strVersionPath =~ s/VERSION/$stryVersionSupport[$iVersionIdx]/g;
if (-e "${strVersionPath}/initdb")
{
&log(INFO, "FOUND pgsql-bin at ${strVersionPath}");
push @stryTestVersion, $strVersionPath;
}
}
}
}
# Make sure at least one version of postgres was found
@stryTestVersion > 0
or confess 'pgsql-bin was not defined and postgres could not be located automatically';
}
else
{
push @stryTestVersion, $strPgSqlBin;
}
################################################################################################################################
# Clean whitespace only if test.pl is being run from the test directory in the backrest repo
################################################################################################################################
# if (-e './test.pl' && -e '../bin/pg_backrest')
# {
# 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);
# }
################################################################################################################################
# Runs tests
################################################################################################################################
# &log(INFO, "Testing with test_path = " . BackRestTestCommon_TestPathGet() . ", host = {strHost}, user = {strUser}, " .
# "group = {strGroup}");
my $iRun = 0;
do
{
if (BackRestTestCommon_Setup($strExe, $strTestPath, $stryTestVersion[0], $iModuleTestRun,
@@ -357,10 +686,9 @@ if ($@)
}
syswrite(*STDOUT, $oMessage);
exit 255;;
exit 250;
}
if (!$bDryRun)
{
&log(INFO, 'TESTS COMPLETED SUCCESSFULLY (DESPITE ANY ERROR MESSAGES YOU SAW)');