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

Storage and IO layer refactor:

Refactor storage layer to allow for new repository filesystems using drivers. (Reviewed by Cynthia Shang.)
Refactor IO layer to allow for new compression formats, checksum types, and other capabilities using filters. (Reviewed by Cynthia Shang.)
This commit is contained in:
David Steele
2017-06-09 17:51:41 -04:00
parent 7e982f05f5
commit de7fc37f88
183 changed files with 17880 additions and 14734 deletions

View File

@@ -15,7 +15,6 @@ use English '-no_match_vars';
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::FileCommon;
use pgBackRest::Version;
use pgBackRestTest::Common::ExecuteTest;
@@ -100,56 +99,77 @@ sub run
################################################################################################################################
if ($self->begin("Ini->new()"))
{
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {(new pgBackRest::Common::Ini($strTestFile, {bIgnoreMissing => true}))->exists()}, false, 'ignore missing');
#---------------------------------------------------------------------------------------------------------------------------
my $oIni = new pgBackRest::Common::Ini(
$strTestFile, {bLoad => false, iInitFormat => 4, strInitVersion => '1.01'});
$self->testResult($oIni->exists(), false, 'file does not exist');
$oIni->save();
$oIni->saveCopy();
$self->testResult($oIni->exists(), false, 'file does not exist after saveCopy()');
$self->testResult(
sub {fileStringRead($strTestFile)},
sub {${storageTest()->get($strTestFile . INI_COPY_EXT)}},
$self->iniHeader(undef, 4, '1.01', '488e5ca1a018cd7cd6d4e15150548f39f493dacd'),
'empty with synthetic format and version');
#---------------------------------------------------------------------------------------------------------------------------
fileStringWrite($strTestFile);
$self->testException(sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_CONFIG, 'no key/value pairs found');
$oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
$oIni->saveCopy();
$self->testResult(
sub {${storageTest()->get($strTestFile . INI_COPY_EXT)}},
$self->iniHeader(undef, BACKREST_FORMAT, BACKREST_VERSION, $oIni->hash()),
'empty with default format and version');
#---------------------------------------------------------------------------------------------------------------------------
$oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
$self->testResult(
sub {storageTest()->list($self->testPath())},
'test.ini.copy',
'only copy is saved');
$oIni->save();
$self->testResult(
sub {fileStringRead($strTestFile)},
$self->iniHeader(undef, BACKREST_FORMAT, BACKREST_VERSION, $oIni->hash()),
'empty with default format and version');
sub {storageTest()->list($self->testPath())},
'(test.ini, test.ini.copy)',
'both versions are saved');
$self->testException(
sub {$oIni->saveCopy()}, ERROR_ASSERT,
"cannot save copy only when '${strTestFile}' exists");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'normal load');
#---------------------------------------------------------------------------------------------------------------------------
my $hIni = iniParse(fileStringRead($strTestFile));
my $hIni = iniParse(${storageTest()->get($strTestFile)});
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = BOGUS;
fileStringWrite($strTestFile, iniRender($hIni));
fileStringWrite($strTestFileCopy, iniRender($hIni));
storageTest()->put($strTestFile, iniRender($hIni));
storageTest()->put($strTestFileCopy, iniRender($hIni));
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_CHECKSUM,
"invalid checksum in '${strTestFile}', expected '" .
$oIni->get(INI_SECTION_BACKREST, INI_KEY_CHECKSUM) . "' but found 'bogus'");
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FILE_MISSING,
"unable to open ${strTestFile} or ${strTestFile}" . INI_COPY_EXT);
# "invalid checksum in '${strTestFile}', expected '" .
# $oIni->get(INI_SECTION_BACKREST, INI_KEY_CHECKSUM) . "' but found 'bogus'");
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = $oIni->hash();
fileStringWrite($strTestFile, iniRender($hIni));
storageTest()->put($strTestFile, iniRender($hIni));
#---------------------------------------------------------------------------------------------------------------------------
$oIni->numericSet(INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, BACKREST_FORMAT - 1);
$oIni->save();
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FORMAT,
"invalid format in '${strTestFile}', expected " . BACKREST_FORMAT . ' but found ' . (BACKREST_FORMAT - 1));
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FILE_MISSING,
"unable to open ${strTestFile} or ${strTestFile}" . INI_COPY_EXT);
# "invalid format in '${strTestFile}', expected " . BACKREST_FORMAT . ' but found ' . (BACKREST_FORMAT - 1));
$oIni->numericSet(INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, BACKREST_FORMAT);
$oIni->save();
@@ -159,17 +179,17 @@ sub run
$oIni->save();
$self->testResult(
sub {fileStringRead($strTestFile)},
sub {${storageTest()->get($strTestFile . INI_COPY_EXT)}},
$self->iniHeader($oIni, undef, '1.01'),
'verify old version was written');
$oIni = new pgBackRest::Common::Ini($strTestFile);
$self->testResult(sub {$oIni->get(INI_SECTION_BACKREST, INI_KEY_VERSION)}, BACKREST_VERSION, 'version is updated on load');
$self->testResult(sub {$oIni->save()}, true, 'save changes');
$oIni->save();
$self->testResult(
sub {fileStringRead($strTestFile)},
sub {${storageTest()->get($strTestFile . INI_COPY_EXT)}},
$self->iniHeader($oIni, undef, BACKREST_VERSION),
'verify version is updated on load');
@@ -177,65 +197,89 @@ sub run
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {new pgBackRest::Common::Ini($strTestFile, {bLoad => false, strContent => fileStringRead($strTestFile)})},
sub {new pgBackRest::Common::Ini($strTestFile, {bLoad => false, strContent => ${storageTest()->get($strTestFile)}})},
'[object]', 'new() passing content as a string');
#---------------------------------------------------------------------------------------------------------------------------
executeTest("rm -rf ${strTestFile}*");
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_UNKNOWN,
"unable to open ${strTestFile}");
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FILE_MISSING,
"unable to open ${strTestFile} or ${strTestFile}" . INI_COPY_EXT);
#---------------------------------------------------------------------------------------------------------------------------
fileStringWrite($strTestFileCopy, BOGUS);
storageTest()->put($strTestFileCopy, BOGUS);
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFileCopy)}, ERROR_CONFIG,
"key/value pair 'bogus' found outside of a section");
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FILE_MISSING,
"unable to open ${strTestFile} or ${strTestFile}" . INI_COPY_EXT);
# "key/value pair 'bogus' found outside of a section");
#---------------------------------------------------------------------------------------------------------------------------
$oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
my $oIniSource = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
fileStringWrite($strTestFileCopy, iniRender($oIni->{oContent}));
storageTest()->put($strTestFileCopy, iniRender($oIniSource->{oContent}));
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFileCopy)}, ERROR_CHECKSUM,
"invalid checksum in '${strTestFileCopy}', expected '" .
$oIni->hash() . "' but found [undef]");
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FILE_MISSING,
"unable to open ${strTestFile} or ${strTestFile}" . INI_COPY_EXT);
# "invalid checksum in '${strTestFileCopy}', expected '" .
# $oIniSource->hash() . "' but found [undef]");
#---------------------------------------------------------------------------------------------------------------------------
$oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
$oIni->{oContent}->{&INI_SECTION_BACKREST}{&INI_KEY_FORMAT} = 0;
$oIniSource = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
$oIniSource->save();
$self->testResult(
sub {$oIni->headerCheck({bIgnoreInvalid => true})}, false,
'ignore invalid header');
storageTest()->put($strTestFile, BOGUS);
# main invalid, copy ok
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'invalid main - load copy');
#---------------------------------------------------------------------------------------------------------------------------
fileRemove($strTestFileCopy);
storageTest()->remove($strTestFileCopy);
fileStringWrite($strTestFile, "[section]\n" . BOGUS);
storageTest()->put($strTestFile, "[section]\n" . BOGUS);
$self->testException(sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_CONFIG, "unable to find '=' in 'bogus'");
# main invalid, copy missing
$self->testException
(sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FILE_MISSING,
"unable to open ${strTestFile} or ${strTestFile}" . INI_COPY_EXT);
#"unable to find '=' in 'bogus'");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {iniParse("[section]\n" . BOGUS, {bIgnoreInvalid => true})}, undef, 'ignore invalid content');
storageTest()->put($strTestFile, iniRender($oIniSource->{oContent}));
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'main ok, copy missing');
#---------------------------------------------------------------------------------------------------------------------------
fileStringWrite($strTestFile, BOGUS);
storageTest()->put($strTestFileCopy, BOGUS);
# main ok, copy invalid
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'main ok, copy invalid');
#---------------------------------------------------------------------------------------------------------------------------
storageTest()->put($strTestFile, BOGUS);
# main invalid, copy invalid
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_CONFIG, "key/value pair 'bogus' found outside of a section");
sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_FILE_MISSING,
"unable to open ${strTestFile} or ${strTestFile}" . INI_COPY_EXT);
# "key/value pair 'bogus' found outside of a section");
#---------------------------------------------------------------------------------------------------------------------------
fileStringWrite($strTestFile, iniRender($hIni));
storageTest()->put($strTestFile, iniRender($hIni));
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = BOGUS;
fileStringWrite($strTestFileCopy, iniRender($hIni));
storageTest()->put($strTestFileCopy, iniRender($hIni));
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'invalid header - load main');
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'invalid copy header - load main');
#---------------------------------------------------------------------------------------------------------------------------
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = $oIni->hash();
storageTest()->put($strTestFileCopy, iniRender($hIni));
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = BOGUS;
storageTest()->put($strTestFile, iniRender($hIni));
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'invalid main header - load copy');
}
################################################################################################################################

View File

@@ -0,0 +1,259 @@
####################################################################################################################################
# CommonIoBufferedTest.pm - tests for Common::Io::Buffered module
####################################################################################################################################
package pgBackRestTest::Module::Common::CommonIoBufferedTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use IO::Socket::UNIX;
use Time::HiRes qw(usleep);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Io::Buffered;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# socketServer
####################################################################################################################################
sub socketServer
{
my $self = shift;
my $strSocketFile = shift;
my $fnServer = shift;
# Fork off the server
if (fork() == 0)
{
# Open the domain socket
my $oSocketServer = IO::Socket::UNIX->new(Type => SOCK_STREAM(), Local => $strSocketFile, Listen => 1);
&log(INFO, " * socket server open");
# Wait for a connection and create IO object
my $oConnection = $oSocketServer->accept();
my $oIoHandle = new pgBackRest::Common::Io::Handle('socket server', $oConnection, $oConnection);
&log(INFO, " * socket server connected");
# Run server function
$fnServer->($oIoHandle);
# Shutdown server
$oSocketServer->close();
&log(INFO, " * socket server closed");
unlink($strSocketFile);
exit 0;
}
# Wait for client socket
my $oWait = waitInit(5);
while (!-e $strSocketFile && waitMore($oWait)) {};
# Open the client socket
my $oClient = IO::Socket::UNIX->new(Type => SOCK_STREAM(), Peer => $strSocketFile);
if (!defined($oClient))
{
logErrorResult(ERROR_FILE_OPEN, 'unable to open client socket', $OS_ERROR);
}
&log(INFO, " * socket client connected");
return $oClient;
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test data
my $strSocketFile = $self->testPath() . qw{/} . 'domain.socket';
################################################################################################################################
if ($self->begin('new() & timeout() & bufferMax()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIoBuffered = $self->testResult(
sub {new pgBackRest::Common::Io::Buffered(
new pgBackRest::Common::Io::Handle('test'), 10, 2048)}, '[object]', 'new - no handles');
$self->testResult(sub {$oIoBuffered->timeout()}, 10, ' check timeout');
$self->testResult(sub {$oIoBuffered->bufferMax()}, 2048, ' check buffer max');
}
################################################################################################################################
if ($self->begin('readLine() & writeLine()'))
{
my $oClient = $self->socketServer($strSocketFile, sub
{
my $oIoHandle = shift;
my $tResponse = '';
my $tData;
# Ack after timeout
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
# Write 8 char line
$tResponse = '';
$tData = "12345678\n";
$oIoHandle->write(\$tData);
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
# Write 3 lines
$tResponse = '';
$tData = "1\n2\n345678\n";
$oIoHandle->write(\$tData);
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
# Write blank line
$tResponse = '';
$tData = "\n";
$oIoHandle->write(\$tData);
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
# Write char with no linefeed
$tResponse = '';
$tData = "A";
$oIoHandle->write(\$tData);
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
# Write linefeed
$tResponse = '';
$tData = "\n";
$oIoHandle->write(\$tData);
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
});
#---------------------------------------------------------------------------------------------------------------------------
my $oIoBuffered = $self->testResult(
sub {new pgBackRest::Common::Io::Buffered(
new pgBackRest::Common::Io::Handle('socket client', $oClient, $oClient), 1, 4)}, '[object]', 'open');
$self->testException(
sub {$oIoBuffered->readLine()}, ERROR_FILE_READ, 'unable to read line after 1 second(s) from socket client');
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoBuffered->readLine()}, '12345678', 'read 8 char line');
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoBuffered->readLine()}, '1', 'read 1 char line');
$self->testResult(sub {$oIoBuffered->readLine()}, '2', 'read 1 char line');
$self->testResult(sub {$oIoBuffered->readLine()}, '345678', 'read 6 char line');
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoBuffered->readLine()}, '', 'read blank line');
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoBuffered->readLine(undef, false)}, undef, 'read ignoring error');
$self->testException(
sub {$oIoBuffered->readLine(undef, true)}, ERROR_FILE_READ,
'unable to read line after 1 second(s) from socket client');
# Clear buffer so EOF tests pass
$oIoBuffered->writeLine();
$oIoBuffered->readLine();
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(sub {$oIoBuffered->readLine()}, ERROR_FILE_READ, 'unexpected EOF reading line from socket client');
$self->testException(
sub {$oIoBuffered->readLine(false)}, ERROR_FILE_READ, 'unexpected EOF reading line from socket client');
$self->testResult(sub {$oIoBuffered->readLine(true)}, undef, 'ignore EOF');
}
################################################################################################################################
if ($self->begin('read'))
{
my $tBuffer;
my $oClient = $self->socketServer($strSocketFile, sub
{
my $oIoHandle = shift;
my $tResponse = '';
my $tData;
# Ack after timeout
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
# Write mixed buffer
$tResponse = '';
$tData = "123\n123\n1";
$oIoHandle->write(\$tData);
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
# Write buffer
$tResponse = '';
$tData = "23456789";
$oIoHandle->write(\$tData);
# Wait before writing the rest to force client to loop
usleep(500);
$tData = "0";
$oIoHandle->write(\$tData);
while (length($tResponse) != 1) {$oIoHandle->read(\$tResponse, 1)};
});
#---------------------------------------------------------------------------------------------------------------------------
my $oIoBuffered = $self->testResult(
sub {new pgBackRest::Common::Io::Buffered(
new pgBackRest::Common::Io::Handle('socket client', $oClient, $oClient), 1, 8)}, '[object]', 'open');
$self->testException(
sub {$oIoBuffered->read(\$tBuffer, 1, true)}, ERROR_FILE_READ,
'unable to read 1 byte(s) after 1 second(s) from socket client');
$self->testResult(sub {$oIoBuffered->read(\$tBuffer, 1)}, 0, ' read 0 char buffer');
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoBuffered->readLine()}, '123', ' read 3 char line');
undef($tBuffer);
$self->testResult(sub {$oIoBuffered->read(\$tBuffer, 1)}, 1, ' read 1 char buffer');
$self->testResult(sub {$tBuffer}, '1', ' check 1 char buffer');
$self->testResult(sub {$oIoBuffered->read(\$tBuffer, 3)}, 3, ' read 3 char buffer');
$self->testResult(sub {$tBuffer}, "123\n", ' check 3 char buffer');
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
undef($tBuffer);
$self->testResult(sub {$oIoBuffered->read(\$tBuffer, 10)}, 10, ' read 10 char buffer');
$self->testResult(sub {$tBuffer}, '1234567890', ' check 10 char buffer');
$oIoBuffered->writeLine();
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoBuffered->read(\$tBuffer, 5)}, 0, ' expect EOF');
$self->testException(
sub {$oIoBuffered->read(\$tBuffer, 5, true)}, ERROR_FILE_READ,
'unable to read 5 byte(s) due to EOF from socket client');
}
}
1;

View File

@@ -0,0 +1,216 @@
####################################################################################################################################
# CommonIoHandleTest.pm - tests for Common::Io::Handle module
####################################################################################################################################
package pgBackRestTest::Module::Common::CommonIoHandleTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use Fcntl qw(O_RDONLY O_WRONLY O_CREAT O_TRUNC O_NONBLOCK);
use pgBackRest::Common::Exception;
use pgBackRest::Common::Io::Base;
use pgBackRest::Common::Io::Handle;
use pgBackRest::Common::Log;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test data
my $strFile = $self->testPath() . qw{/} . 'file.txt';
my $strFileContent = 'TESTDATA';
my $iFileLength = length($strFileContent);
my $iFileLengthHalf = int($iFileLength / 2);
################################################################################################################################
if ($self->begin('new() & handleRead() & handleReadSet() & handleWrite() & handleWriteSet()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle('test', undef, undef)}, '[object]', 'new - no handles');
$self->testResult(sub {$oIoHandle->id()}, 'test', ' check error msg');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$oIoHandle->handleReadSet(1)}, 1, ' set read handle');
$self->testResult(sub {$oIoHandle->handleRead()}, 1, ' check read handle');
$self->testResult(sub {$oIoHandle->handleWrite()}, undef, ' check write handle');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {$oIoHandle->handleWriteSet(2)}, 2, ' set write handle');
$self->testResult(sub {$oIoHandle->handleRead()}, 1, ' check read handle');
$self->testResult(sub {$oIoHandle->handleWrite()}, 2, ' check write handle');
#---------------------------------------------------------------------------------------------------------------------------
$oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle('test', 1, undef)}, '[object]', 'new - read handle');
$self->testResult(sub {$oIoHandle->handleRead()}, 1, ' check read handle');
$self->testResult(sub {$oIoHandle->handleWrite()}, undef, ' check write handle');
#---------------------------------------------------------------------------------------------------------------------------
$oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle('test', undef, 2)}, '[object]', 'new - write handle');
$self->testResult(sub {$oIoHandle->handleRead()}, undef, ' check read handle');
$self->testResult(sub {$oIoHandle->handleWrite()}, 2, ' check write handle');
#---------------------------------------------------------------------------------------------------------------------------
$oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle('test', 1, 2)}, '[object]', 'new - read/write handle');
$self->testResult(sub {$oIoHandle->handleRead()}, 1, ' check read handle');
$self->testResult(sub {$oIoHandle->handleWrite()}, 2, ' check write handle');
}
################################################################################################################################
if ($self->begin('read()'))
{
my $tContent;
my $fhRead;
#---------------------------------------------------------------------------------------------------------------------------
executeTest("echo -n '${strFileContent}' | tee ${strFile}");
sysopen($fhRead, $strFile, O_RDONLY)
or confess &log(ERROR, "unable to open '${strFile}'");
my $oIoHandle = $self->testResult(sub {new pgBackRest::Common::Io::Handle("'$strFile'", $fhRead)}, '[object]', 'open');
$self->testException(
sub {$oIoHandle->read(\$tContent, -1)}, ERROR_FILE_READ, "unable to read from '${strFile}': Negative length");
#---------------------------------------------------------------------------------------------------------------------------
$tContent = undef;
sysopen($fhRead, $strFile, O_RDONLY)
or confess &log(ERROR, "unable to open '${strFile}'");
$oIoHandle = $self->testResult(sub {new pgBackRest::Common::Io::Handle("'$strFile'", $fhRead)}, '[object]', 'open');
$self->testResult(sub {$oIoHandle->read(\$tContent, $iFileLengthHalf)}, $iFileLengthHalf, ' read part 1');
$self->testResult($tContent, substr($strFileContent, 0, $iFileLengthHalf), ' check read');
$self->testResult(
sub {$oIoHandle->read(
\$tContent, $iFileLength - $iFileLengthHalf)}, $iFileLength - $iFileLengthHalf, ' read part 2');
$self->testResult($tContent, $strFileContent, ' check read');
$self->testResult(sub {$oIoHandle->read(\$tContent, 1)}, 0, ' eof');
}
################################################################################################################################
if ($self->begin('write()'))
{
my $tContent;
my $fhRead;
my $fhWrite;
#---------------------------------------------------------------------------------------------------------------------------
sysopen($fhWrite, $strFile, O_WRONLY | O_CREAT | O_TRUNC)
or confess &log(ERROR, "unable to open '${strFile}'");
my $oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle("'$strFile'", undef, $fhWrite)}, '[object]', 'open write');
$self->testException(
sub {$oIoHandle->write(undef)}, ERROR_FILE_WRITE,
"unable to write to '${strFile}': Can't use an undefined value as a SCALAR reference");
#---------------------------------------------------------------------------------------------------------------------------
$tContent = substr($strFileContent, 0, $iFileLengthHalf);
$self->testResult(sub {$oIoHandle->write(\$tContent)}, $iFileLengthHalf, ' write part 1');
$self->testResult(sub {$oIoHandle->size()}, $iFileLengthHalf, ' check part 1 size');
$tContent = substr($strFileContent, $iFileLengthHalf);
$self->testResult(sub {$oIoHandle->write(\$tContent)}, $iFileLength - $iFileLengthHalf, ' write part 2');
$self->testResult(sub {$oIoHandle->size()}, $iFileLength, ' check part 2 size');
$self->testResult(sub {$oIoHandle->close()}, true, ' close');
sysopen($fhRead, $strFile, O_RDONLY)
or confess &log(ERROR, "unable to open '${strFile}'");
$oIoHandle = $self->testResult(sub {new pgBackRest::Common::Io::Handle("'$strFile'", $fhRead)}, '[object]', 'open read');
$tContent = undef;
$self->testResult(sub {$oIoHandle->read(\$tContent, $iFileLength)}, $iFileLength, ' read');
$self->testResult($tContent, $strFileContent, ' check write content');
}
################################################################################################################################
if ($self->begin('result() & resultSet()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle('test', undef, undef)}, '[object]', 'new - no handles');
$self->testResult(sub {$oIoHandle->resultSet('Module::1', 1)}, 1, ' set int result');
$self->testResult(sub {$oIoHandle->resultSet('Module::2', {value => 2})}, '{value => 2}', ' set hash result');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoHandle->result('Module::1')}, 1, ' check int result');
$self->testResult(sub {$oIoHandle->result('Module::2')}, '{value => 2}', ' check hash result');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIoHandle->{rhResult}}, '{Module::1 => 1, Module::2 => {value => 2}}', ' check all results');
}
################################################################################################################################
if ($self->begin('isA()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle('test', undef, undef)}, '[object]', 'new - no handles');
$self->testResult(
sub {$oIoHandle->isA()}, '(' . COMMON_IO_HANDLE . ', ' . COMMON_IO_BASE . ')', ' check isA');
}
################################################################################################################################
if ($self->begin('className()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIoHandle = $self->testResult(
sub {new pgBackRest::Common::Io::Handle('test', undef, undef)}, '[object]', 'new - no handles');
$self->testResult(sub {$oIoHandle->className()}, COMMON_IO_HANDLE, ' check class name');
}
################################################################################################################################
if ($self->begin('close()'))
{
my $tContent;
my $fhRead;
#---------------------------------------------------------------------------------------------------------------------------
executeTest("echo -n '${strFileContent}' | tee ${strFile}");
sysopen($fhRead, $strFile, O_RDONLY)
or confess &log(ERROR, "unable to open '${strFile}'");
my $oIoHandle = $self->testResult(sub {new pgBackRest::Common::Io::Handle("'$strFile'", $fhRead)}, '[object]', 'open read');
$self->testResult(sub {$oIoHandle->read(\$tContent, $iFileLengthHalf)}, $iFileLengthHalf, ' read');
$self->testResult(sub {$oIoHandle->close()}, true, ' close');
$self->testResult(sub {$oIoHandle->result(COMMON_IO_HANDLE)}, $iFileLengthHalf, ' check result');
# Destroy and to make sure close runs again
undef($oIoHandle);
}
}
1;

View File

@@ -0,0 +1,59 @@
####################################################################################################################################
# CommonIoProcessTest.pm - tests for Common::Io::Process module
####################################################################################################################################
package pgBackRestTest::Module::Common::CommonIoProcessTest;
use parent 'pgBackRestTest::Common::RunTest';
####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Common::Exception;
use pgBackRest::Common::Io::Buffered;
use pgBackRest::Common::Io::Process;
use pgBackRest::Common::Log;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test data
my $strFile = $self->testPath() . qw{/} . 'file.txt';
my $strFileContent = 'TESTDATA';
################################################################################################################################
if ($self->begin('new() & processId()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIoProcess = $self->testResult(sub {
new pgBackRest::Common::Io::Process(
new pgBackRest::Common::Io::Buffered(
new pgBackRest::Common::Io::Handle('test'), 1, 32), "echo '${strFileContent}'")}, '[object]', 'new - echo');
$self->testResult(sub {defined($oIoProcess->processId())}, true, ' process id defined');
}
################################################################################################################################
if ($self->begin('close() & error()'))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIoProcess =
new pgBackRest::Common::Io::Process(
new pgBackRest::Common::Io::Buffered(
new pgBackRest::Common::Io::Handle('test'), 1, 32), "echo '${strFileContent}'");
$oIoProcess->close();
$self->testException(
sub {$oIoProcess->error()}, ERROR_ASSERT, 'cannot call error() after process has been closed');
}
}
1;