1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2026-05-22 10:15:16 +02:00

Improvements to Ini.pm.

* Refactor Ini.pm to facilitate testing.
* Complete statement/branch coverage for Ini.pm.
* Improved functions used to test/munge manifest and info files.
This commit is contained in:
David Steele
2017-04-10 13:24:45 -04:00
parent 367e06f4be
commit 3d84f2ce5e
24 changed files with 1082 additions and 636 deletions
@@ -77,6 +77,11 @@ use constant TESTDEF_MODULE_ARCHIVE_PUSH_FILE => TESTDEF_M
use constant TESTDEF_MODULE_BACKUP_COMMON => 'BackupCommon';
push @EXPORT, qw(TESTDEF_MODULE_BACKUP_COMMON);
use constant TESTDEF_MODULE_COMMON => 'Common';
push @EXPORT, qw(TESTDEF_MODULE_COMMON);
use constant TESTDEF_MODULE_COMMON_INI => TESTDEF_MODULE_COMMON . '/Ini';
push @EXPORT, qw(TESTDEF_MODULE_COMMON_INI);
use constant TESTDEF_MODULE_INFO => 'Info';
push @EXPORT, qw(TESTDEF_MODULE_INFO);
@@ -206,6 +211,16 @@ my $oTestDef =
&TESTDEF_TEST =>
[
{
&TESTDEF_NAME => 'ini-unit',
&TESTDEF_CONTAINER => true,
&TESTDEF_TOTAL => 10,
&TESTDEF_COVERAGE =>
{
&TESTDEF_MODULE_COMMON_INI => TESTDEF_COVERAGE_FULL,
},
},
{
&TESTDEF_NAME => 'unit',
&TESTDEF_TOTAL => 1,
@@ -13,6 +13,7 @@ use Carp qw(confess);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname);
use Storable qw(dclone);
use pgBackRest::BackupCommon;
@@ -240,8 +241,8 @@ sub backupEnd
# because a running database is always in flux. Even so, it allows us test many things.
if (!$self->synthetic())
{
$oExpectedManifest =
iniLoad($self->repoPath() . '/backup/' . $self->stanza() . "/${strBackup}/" . FILE_MANIFEST, $oExpectedManifest);
$oExpectedManifest = iniParse(
fileStringRead($self->repoPath() . '/backup/' . $self->stanza() . "/${strBackup}/" . FILE_MANIFEST));
}
# Make sure tablespace links are correct
@@ -494,8 +495,8 @@ sub backupCompare
my $strTestPath = $self->testPath();
iniSave("${strTestPath}/actual.manifest", $oActualManifest->{oContent});
iniSave("${strTestPath}/expected.manifest", $oExpectedManifest);
fileStringWrite("${strTestPath}/actual.manifest", iniRender($oActualManifest->{oContent}));
fileStringWrite("${strTestPath}/expected.manifest", iniRender($oExpectedManifest));
executeTest("diff ${strTestPath}/expected.manifest ${strTestPath}/actual.manifest");
@@ -1032,7 +1033,10 @@ sub configCreate
}
# Write out the configuration file
iniSave($self->backrestConfig(), \%oParamHash, true);
fileStringWrite($self->backrestConfig(), iniRender(\%oParamHash, true));
# Modify the file permissions so it can be read/saved by all test users
executeTest('sudo chmod 660 ' . $self->backrestConfig());
}
####################################################################################################################################
@@ -1050,59 +1054,48 @@ sub manifestMunge
(
$strOperation,
$strBackup,
$strSection,
$strKey,
$strSubKey,
$strValue,
$hParam,
$bCache,
) =
logDebugParam
(
__PACKAGE__ . '->manifestMunge', \@_,
{name => '$strBackup'},
{name => '$strSection'},
{name => '$strKey'},
{name => '$strSubKey', required => false},
{name => '$strValue', required => false},
{name => 'strBackup'},
{name => '$hParam'},
{name => 'bCache', default => true},
);
my $strManifestFile = "${strBackup}/" . FILE_MANIFEST;
$self->infoMunge($self->{oFile}->pathGet(PATH_BACKUP_CLUSTER, "${strBackup}/" . FILE_MANIFEST), $hParam, $bCache);
# Change mode on the backup path so it can be read/written
if ($self->nameTest(HOST_BACKUP))
{
executeTest('sudo chmod g+w ' . $self->{oFile}->pathGet(PATH_BACKUP_CLUSTER, $strManifestFile));
}
# Return from function and log return values if any
return logDebugReturn($strOperation);
}
# Read the manifest
my %oManifest;
iniLoad($self->{oFile}->pathGet(PATH_BACKUP_CLUSTER, $strManifestFile), \%oManifest);
####################################################################################################################################
# manifestRestore
####################################################################################################################################
sub manifestRestore
{
my $self = shift;
# Write in the munged value
if (defined($strSubKey))
{
if (defined($strValue))
{
$oManifest{$strSection}{$strKey}{$strSubKey} = $strValue;
}
else
{
delete($oManifest{$strSection}{$strKey}{$strSubKey});
}
}
else
{
if (defined($strValue))
{
$oManifest{$strSection}{$strKey} = $strValue;
}
else
{
delete($oManifest{$strSection}{$strKey});
}
}
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$strBackup,
$bSave,
) =
logDebugParam
(
__PACKAGE__ . '->manifestRestore', \@_,
{name => 'strBackup'},
{name => 'bSave', default => true},
);
# Resave the manifest
$self->iniSaveChecksum($self->{oFile}->pathGet(PATH_BACKUP_CLUSTER, $strManifestFile), \%oManifest, true);
$self->infoRestore($self->{oFile}->pathGet(PATH_BACKUP_CLUSTER, "${strBackup}/" . FILE_MANIFEST), $bSave);
# Return from function and log return values if any
return logDebugReturn($strOperation);
}
####################################################################################################################################
@@ -1121,38 +1114,64 @@ sub infoMunge
(
$strOperation,
$strFileName,
$hParam
$hParam,
$bCache,
) =
logDebugParam
(
__PACKAGE__ . '->infoMunge', \@_,
{name => 'strFileName'},
{name => 'hParam'}
{name => 'hParam'},
{name => 'bCache', default => true},
);
# If the original file content does not exist then load it
if (!defined($self->{hInfoFile}{$strFileName}))
{
$self->{hInfoFile}{$strFileName} = iniLoad($strFileName);
# Modify the file permissions so it can be saved
executeTest("sudo chmod 660 ${strFileName}");
$self->{hInfoFile}{$strFileName} = new pgBackRest::Common::Ini($strFileName);
}
# Make a copy of the original file contents
my $hContent = dclone($self->{hInfoFile}{$strFileName});
my $oMungeIni = new pgBackRest::Common::Ini(
$strFileName, {bLoad => false, strContent => iniRender($self->{hInfoFile}{$strFileName}->{oContent})});
# Load params
foreach my $strSection (sort(keys(%{$hParam})))
foreach my $strSection (keys(%{$hParam}))
{
foreach my $strKey (keys(%{$$hParam{$strSection}}))
foreach my $strKey (keys(%{$hParam->{$strSection}}))
{
# Munge the copy with the new parameter values
$$hContent{$strSection}{$strKey} = $$hParam{$strSection}{$strKey};
if (ref($hParam->{$strSection}{$strKey}) eq 'HASH')
{
foreach my $strSubKey (keys(%{$hParam->{$strSection}{$strKey}}))
{
# Munge the copy with the new parameter values
$oMungeIni->set($strSection, $strKey, $strSubKey, $hParam->{$strSection}{$strKey}{$strSubKey});
}
}
else
{
# Munge the copy with the new parameter values
$oMungeIni->set($strSection, $strKey, undef, $hParam->{$strSection}{$strKey});
}
}
}
# Modify the file/directory permissions so it can be saved
executeTest("sudo rm -f ${strFileName} && sudo chmod 770 " . dirname($strFileName));
# Save the munged data to the file
$self->iniSaveChecksum($strFileName, \%{$hContent}, true);
$oMungeIni->save();
# Fix permissions
executeTest(
"sudo chmod 640 ${strFileName} && sudo chmod 750 " . dirname($strFileName) .
' && sudo chown ' . $self->userGet() . " ${strFileName}");
# Clear the cache is requested
if (!$bCache)
{
delete($self->{hInfoFile}{$strFileName});
}
# Return from function and log return values if any
return logDebugReturn($strOperation);
@@ -1179,7 +1198,7 @@ sub infoRestore
(
__PACKAGE__ . '->infoRestore', \@_,
{name => 'strFileName'},
{name => 'bSave', default => true, required => false},
{name => 'bSave', default => true},
);
# If the original file content exists in the global hash, then save it to the file
@@ -1187,7 +1206,15 @@ sub infoRestore
{
if ($bSave)
{
iniSave($strFileName, $self->{hInfoFile}{$strFileName});
# Modify the file/directory permissions so it can be saved
executeTest("sudo rm -f ${strFileName} && sudo chmod 770 " . dirname($strFileName));
# Save the munged data to the file
$self->{hInfoFile}{$strFileName}->{bModified} = true;
$self->{hInfoFile}{$strFileName}->save();
# Fix permissions
executeTest("sudo chmod 640 ${strFileName} && sudo chmod 750 " . dirname($strFileName));
}
}
else
@@ -1202,33 +1229,6 @@ sub infoRestore
return logDebugReturn($strOperation);
}
####################################################################################################################################
# iniSaveChecksum
#
# Save an ini file an optionall recalculate the checksum so it's valid.
####################################################################################################################################
sub iniSaveChecksum
{
my $self = shift;
my $strFileName = shift;
my $oIniRef = shift;
my $bChecksum = shift;
# Calculate a new checksum if requested
if (defined($bChecksum) && $bChecksum)
{
delete($$oIniRef{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM});
my $oSHA = Digest::SHA->new('sha1');
my $oJSON = JSON::PP->new()->canonical()->allow_nonref();
$oSHA->add($oJSON->encode($oIniRef));
$$oIniRef{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = $oSHA->hexdigest();
}
iniSave($strFileName, $oIniRef);
}
####################################################################################################################################
# Getters
####################################################################################################################################
@@ -15,6 +15,7 @@ use DBI;
use Exporter qw(import);
our @EXPORT = qw();
use Fcntl ':mode';
use File::Basename qw(dirname);
use Storable qw(dclone);
use pgBackRest::Common::Exception;
@@ -181,16 +182,7 @@ sub configRecovery
my $strStanza = $self->stanza();
# Load db config file
my %oConfig;
iniLoad($self->backrestConfig(), \%oConfig, true);
# Load backup config file
my %oRemoteConfig;
if ($oHostBackup->nameTest(HOST_BACKUP))
{
iniLoad($oHostBackup->backrestConfig(), \%oRemoteConfig, true);
}
my $oConfig = iniParse(fileStringRead($self->backrestConfig()), {bRelaxed => true});
# Rewrite recovery options
my @stryRecoveryOption;
@@ -202,17 +194,11 @@ sub configRecovery
if (@stryRecoveryOption)
{
$oConfig{$strStanza}{&OPTION_RESTORE_RECOVERY_OPTION} = \@stryRecoveryOption;
$oConfig->{$strStanza}{&OPTION_RESTORE_RECOVERY_OPTION} = \@stryRecoveryOption;
}
# Save db config file
iniSave($self->backrestConfig(), \%oConfig, true);
# Save backup config file
if ($oHostBackup->nameTest(HOST_BACKUP))
{
iniSave($oHostBackup->backrestConfig(), \%oRemoteConfig, true);
}
fileStringWrite($self->backrestConfig(), iniRender($oConfig, true));
}
####################################################################################################################################
@@ -228,22 +214,21 @@ sub configRemap
my $strStanza = $self->stanza();
# Load db config file
my %oConfig;
iniLoad($self->backrestConfig(), \%oConfig, true);
my $oConfig = iniParse(fileStringRead($self->backrestConfig()), {bRelaxed => true});
# Load backup config file
my %oRemoteConfig;
my $oRemoteConfig;
my $oHostBackup =
!$self->standby() && !$self->nameTest($self->backupDestination()) ?
hostGroupGet()->hostGet($self->backupDestination()) : undef;
if (defined($oHostBackup))
{
iniLoad($oHostBackup->backrestConfig(), \%oRemoteConfig, true);
$oRemoteConfig = iniParse(fileStringRead($oHostBackup->backrestConfig()), {bRelaxed => true});
}
# Rewrite recovery section
delete($oConfig{"${strStanza}:restore"}{&OPTION_TABLESPACE_MAP});
delete($oConfig->{"${strStanza}:restore"}{&OPTION_TABLESPACE_MAP});
my @stryTablespaceMap;
foreach my $strRemap (sort(keys(%$oRemapHashRef)))
@@ -252,13 +237,13 @@ sub configRemap
if ($strRemap eq MANIFEST_TARGET_PGDATA)
{
$oConfig{$strStanza}{&OPTION_DB_PATH} = $strRemapPath;
$oConfig->{$strStanza}{&OPTION_DB_PATH} = $strRemapPath;
${$oManifestRef}{&MANIFEST_SECTION_BACKUP_TARGET}{&MANIFEST_TARGET_PGDATA}{&MANIFEST_SUBKEY_PATH} = $strRemapPath;
if (defined($oHostBackup))
{
my $bForce = $oHostBackup->nameTest(HOST_BACKUP) && defined(hostGroupGet()->hostGet(HOST_DB_STANDBY, true));
$oRemoteConfig{$strStanza}{optionIndex(OPTION_DB_PATH, 1, $bForce)} = $strRemapPath;
$oRemoteConfig->{$strStanza}{optionIndex(OPTION_DB_PATH, 1, $bForce)} = $strRemapPath;
}
}
else
@@ -273,16 +258,25 @@ sub configRemap
if (@stryTablespaceMap)
{
$oConfig{"${strStanza}:restore"}{&OPTION_TABLESPACE_MAP} = \@stryTablespaceMap;
$oConfig->{"${strStanza}:restore"}{&OPTION_TABLESPACE_MAP} = \@stryTablespaceMap;
}
# Save db config file
iniSave($self->backrestConfig(), \%oConfig, true);
fileStringWrite($self->backrestConfig(), iniRender($oConfig, true));
# Save backup config file (but not is this is the standby which is not the source of backups)
if (defined($oHostBackup))
{
iniSave($oHostBackup->backrestConfig(), \%oRemoteConfig, true);
# Modify the file permissions so it can be read/saved by all test users
executeTest(
'sudo chmod 660 ' . $oHostBackup->backrestConfig() . ' && sudo chmod 770 ' . dirname($oHostBackup->backrestConfig()));
fileStringWrite($oHostBackup->backrestConfig(), iniRender($oRemoteConfig, true));
# Fix permissions
executeTest(
'sudo chmod 660 ' . $oHostBackup->backrestConfig() . ' && sudo chmod 770 ' . dirname($oHostBackup->backrestConfig()) .
' && sudo chown ' . $oHostBackup->userGet() . ' ' . $oHostBackup->backrestConfig());
}
}
@@ -676,9 +670,8 @@ sub restoreCompare
if ($self->synthetic())
{
$oActualManifest->remove(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TIMESTAMP_COPY_START);
$oActualManifest->remove(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL);
$oActualManifest->remove(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_TYPE);
$oActualManifest->remove(MANIFEST_SECTION_BACKUP);
delete($oExpectedManifestRef->{&MANIFEST_SECTION_BACKUP});
}
else
{
@@ -730,8 +723,8 @@ sub restoreCompare
$self->manifestDefault($oExpectedManifestRef);
iniSave("${strTestPath}/actual.manifest", $oActualManifest->{oContent});
iniSave("${strTestPath}/expected.manifest", $oExpectedManifestRef);
fileStringWrite("${strTestPath}/actual.manifest", iniRender($oActualManifest->{oContent}));
fileStringWrite("${strTestPath}/expected.manifest", iniRender($oExpectedManifestRef));
executeTest("diff ${strTestPath}/expected.manifest ${strTestPath}/actual.manifest");
@@ -15,6 +15,7 @@ use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Config::Config;
use pgBackRest::FileCommon;
use pgBackRestTest::Common::RunTest;
@@ -71,7 +72,7 @@ sub run
{
$oConfig = {};
$$oConfig{$self->stanza() . ':' . &CMD_BACKUP}{&OPTION_PROCESS_MAX} = 2;
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -85,7 +86,7 @@ sub run
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_PROCESS_MAX} = 3;
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -99,7 +100,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{'thread-max'} = 2;
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -113,7 +114,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_PROCESS_MAX} = 5;
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -126,7 +127,7 @@ sub run
if ($self->begin('default - option ' . OPTION_PROCESS_MAX))
{
$oConfig = {};
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -140,7 +141,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_PROCESS_MAX} = 9;
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -155,7 +156,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{&OPTION_HARDLINK} = 'Y';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -168,7 +169,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_LOG_LEVEL_CONSOLE} = BOGUS;
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -181,7 +182,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_LOG_LEVEL_CONSOLE} = lc(INFO);
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -202,7 +203,7 @@ sub run
{
$oConfig = {};
$$oConfig{$self->stanza() . ':' . &CMD_EXPIRE}{&OPTION_RETENTION_FULL} = 2;
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
@@ -215,7 +216,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_BACKUP}{&OPTION_COMPRESS} = 'n';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -229,7 +230,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} = 'bogus=';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -242,7 +243,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} = '=bogus';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -256,7 +257,7 @@ sub run
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL . ':' . &CMD_RESTORE}{&OPTION_RESTORE_RECOVERY_OPTION} =
'archive-command=/path/to/pgbackrest';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -270,7 +271,7 @@ sub run
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_RESTORE_RECOVERY_OPTION} = ['standby-mode=on', 'a=b'];
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -285,7 +286,7 @@ sub run
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
@@ -298,7 +299,7 @@ sub run
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
@@ -313,7 +314,7 @@ sub run
{
$oConfig = {};
$$oConfig{$self->stanza()}{&OPTION_DB_PATH} = '/path/to/db';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_CONFIG, $strConfigFile);
@@ -326,7 +327,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_REPO_PATH} = '/repo';
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -340,7 +341,7 @@ sub run
{
$oConfig = {};
$$oConfig{&CONFIG_SECTION_GLOBAL}{&OPTION_REPO_PATH} = ['/repo', '/repo2'];
iniSave($strConfigFile, $oConfig, true);
fileStringWrite($strConfigFile, iniRender($oConfig, true));
$self->optionSetTest($oOption, OPTION_STANZA, $self->stanza());
$self->optionSetTest($oOption, OPTION_DB_PATH, '/db');
@@ -11,9 +11,10 @@ use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use File::Basename qw(dirname);
use File::Basename qw(basename dirname);
use pgBackRest::Archive::ArchiveInfo;
use pgBackRest::BackupCommon;
use pgBackRest::BackupInfo;
use pgBackRest::DbVersion;
use pgBackRest::Common::Exception;
@@ -429,14 +430,16 @@ sub run
testPathMove($oHostBackup->repoPath() . '/backup/' . $self->stanza() . "/${strFullBackup}", $strTmpPath);
my $oMungeManifest = new pgBackRest::Manifest("$strTmpPath/backup.manifest");
$oMungeManifest->remove(MANIFEST_SECTION_TARGET_FILE, MANIFEST_TARGET_PGDATA . '/' . DB_FILE_PGVERSION, 'checksum');
$oMungeManifest->save();
$oHostBackup->infoMunge(
"$strTmpPath/" . FILE_MANIFEST,
{&MANIFEST_SECTION_TARGET_FILE =>
{(&MANIFEST_TARGET_PGDATA . '/' . &DB_FILE_PGVERSION) => {&MANIFEST_SUBKEY_CHECKSUM => undef}}},
false);
# Create a temp file in backup temp root to be sure it's deleted correctly
executeTest("touch ${strTmpPath}/file.tmp" . ($bCompress ? '.gz' : ''),
{bRemote => $bRemote});
executeTest("sudo chmod -R g+w " . dirname($strTmpPath));
executeTest("sudo chown -R " . $oHostBackup->userGet() . ' ' . dirname($strTmpPath));
$strFullBackup = $oHostBackup->backup(
$strType, 'resume',
@@ -567,9 +570,10 @@ sub run
# Munge the user to make sure it gets reset on the next run
$oHostBackup->manifestMunge(
$strFullBackup, MANIFEST_SECTION_TARGET_FILE, MANIFEST_FILE_PGCONTROL, MANIFEST_SUBKEY_USER, 'bogus');
$oHostBackup->manifestMunge(
$strFullBackup, MANIFEST_SECTION_TARGET_FILE, MANIFEST_FILE_PGCONTROL, MANIFEST_SUBKEY_GROUP, 'bogus');
$strFullBackup,
{&MANIFEST_SECTION_TARGET_FILE =>
{&MANIFEST_FILE_PGCONTROL => {&MANIFEST_SUBKEY_USER => 'bogus', &MANIFEST_SUBKEY_GROUP => 'bogus'}}},
false);
# Restore succeeds
$oHostDbMaster->manifestLinkMap(\%oManifest, MANIFEST_TARGET_PGDATA . '/pg_stat');
@@ -760,11 +764,13 @@ sub run
$strTmpPath = $oHostBackup->repoPath() . '/temp/' .$self->stanza() . '.tmp';
testPathMove($oHostBackup->repoPath() . '/backup/' . $self->stanza() . "/${strBackup}", $strTmpPath);
executeTest("sudo chmod -R g+w " . dirname($strTmpPath));
executeTest("sudo chown -R " . $oHostBackup->userGet() . ' ' . dirname($strTmpPath));
$oMungeManifest = new pgBackRest::Manifest("$strTmpPath/" . FILE_MANIFEST);
$oMungeManifest->set(MANIFEST_SECTION_TARGET_FILE, MANIFEST_TARGET_PGDATA . '/badchecksum.txt', 'checksum', 'bogus');
$oMungeManifest->save();
$oHostBackup->infoMunge(
"$strTmpPath/" . FILE_MANIFEST,
{&MANIFEST_SECTION_TARGET_FILE =>
{(&MANIFEST_TARGET_PGDATA . '/badchecksum.txt') => {&MANIFEST_SUBKEY_CHECKSUM => BOGUS}}},
false);
# Add tablespace 2
$oHostDbMaster->manifestTablespaceCreate(\%oManifest, 2);
@@ -800,7 +806,7 @@ sub run
$strTmpPath = $oHostBackup->repoPath() . '/temp/' . $self->stanza() . '.tmp';
testPathMove($oHostBackup->repoPath() . '/backup/' . $self->stanza() . "/${strBackup}", $strTmpPath);
executeTest("sudo chmod -R g+w " . dirname($strTmpPath));
executeTest("sudo chown -R " . $oHostBackup->userGet() . ' ' . dirname($strTmpPath));
$strBackup = $oHostBackup->backup(
$strType, 'cannot resume - new diff',
@@ -814,7 +820,7 @@ sub run
$strTmpPath = $oHostBackup->repoPath() . '/temp/' . $self->stanza() . '.tmp';
testPathMove($oHostBackup->repoPath() . '/backup/' . $self->stanza() . "/${strBackup}", $strTmpPath);
executeTest("sudo chmod -R g+w " . dirname($strTmpPath));
executeTest("sudo chown -R " . $oHostBackup->userGet() . ' ' . dirname($strTmpPath));
$strBackup = $oHostBackup->backup(
$strType, 'cannot resume - disabled / no repo link',
@@ -831,22 +837,6 @@ sub run
$strBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
'fail on used path', ERROR_PATH_NOT_EMPTY, '--log-level-console=detail');
# Fail on undef format
$oHostBackup->manifestMunge($strBackup, INI_SECTION_BACKREST, INI_KEY_FORMAT);
$oHostDbMaster->restore(
$strBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
'fail on undef format', ERROR_FORMAT, '--log-level-console=detail');
# Fail on mismatch format
$oHostBackup->manifestMunge($strBackup, INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, 0);
$oHostDbMaster->restore(
$strBackup, \%oManifest, undef, $bDelta, $bForce, undef, undef, undef, undef, undef, undef,
'fail on mismatch format', ERROR_FORMAT, '--log-level-console=detail');
$oHostBackup->manifestMunge($strBackup, INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, BACKREST_FORMAT);
# Remap the base and tablespace paths
my %oRemapHash;
$oRemapHash{&MANIFEST_TARGET_PGDATA} = $oHostDbMaster->dbBasePath(2);
@@ -887,7 +877,7 @@ sub run
'e324463005236d83e6e54795dbddd20a74533bf3', $lTime, undef, undef, false);
# Munge the version to make sure it gets corrected on the next run
$oHostBackup->manifestMunge($strBackup, INI_SECTION_BACKREST, INI_KEY_VERSION, undef, '0.00');
$oHostBackup->manifestMunge($strBackup, {&INI_SECTION_BACKREST => {&INI_KEY_VERSION => '0.00'}}, false);
$strBackup = $oHostBackup->backup(
$strType, 'add files and remove tablespace 2',
@@ -1035,7 +1025,8 @@ sub run
# Munge the prior manifest so that option-checksum-page is missing to be sure the logic works for backups before page
# checksums were introduced
$oHostBackup->manifestMunge($strFullBackup, MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_CHECKSUM_PAGE, undef, undef);
$oHostBackup->manifestMunge(
$strFullBackup, {&MANIFEST_SECTION_BACKUP_OPTION => {&MANIFEST_KEY_CHECKSUM_PAGE => undef}}, false);
$strBackup = $oHostBackup->backup(
$strType, 'add file', {oExpectedManifest => \%oManifest,
@@ -0,0 +1,471 @@
####################################################################################################################################
# InfoIniUnitTest.pm - Unit tests for Ini module
####################################################################################################################################
package pgBackRestTest::Info::InfoIniUnitTest;
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::Ini;
use pgBackRest::Common::Log;
use pgBackRest::FileCommon;
use pgBackRest::Version;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# iniHeader
####################################################################################################################################
sub iniHeader
{
my $self = shift;
my $oIni = shift;
my $iFormat = shift;
my $iVersion = shift;
my $strChecksum = shift;
return
"[backrest]" .
"\nbackrest-checksum=\"" .
(defined($strChecksum) ? $strChecksum : $oIni->get(INI_SECTION_BACKREST, INI_KEY_CHECKSUM)) . "\"" .
"\nbackrest-format=" . (defined($iFormat) ? $iFormat : $oIni->get(INI_SECTION_BACKREST, INI_KEY_FORMAT)) .
"\nbackrest-version=\"" . (defined($iVersion) ? $iVersion : $oIni->get(INI_SECTION_BACKREST, INI_KEY_VERSION)) . "\"" .
"\n";
}
####################################################################################################################################
# run
####################################################################################################################################
sub run
{
my $self = shift;
# Test ini file
my $strTestFile = $self->testPath() . '/test.ini';
my $strTestFileCopy = "${strTestFile}.copy";
# Test keys, values
my $strSection = 'test-section';
my $strKey = 'test-key';
my $strSubKey = 'test-subkey';
my $strValue = 'test-value';
################################################################################################################################
if ($self->begin("iniRender()"))
{
#---------------------------------------------------------------------------------------------------------------------------
my $oIni = {section1 => {key => 'value'}, section2 => {key => ['value1', 'value2']}};
$self->testResult(
sub {iniRender($oIni, true)}, "[section1]\nkey=value\n\n[section2]\nkey=value1\nkey=value2\n",
'relaxed formatting');
$self->testResult(
sub {iniRender($oIni, false)}, "[section1]\nkey=\"value\"\n\n[section2]\nkey=[\"value1\",\"value2\"]\n",
'strict formatting');
}
################################################################################################################################
if ($self->begin("iniParse()"))
{
#---------------------------------------------------------------------------------------------------------------------------
my $strIni = "[section]\nkey";
$self->testException(sub {iniParse($strIni)}, ERROR_CONFIG, "unable to find '=' in 'key'");
#---------------------------------------------------------------------------------------------------------------------------
$strIni = "[section]\n# comment\n\nkey=\"value\"";
$self->testResult(sub {iniParse($strIni)}, '{section => {key => value}}', 'ignore blank lines and comments');
#---------------------------------------------------------------------------------------------------------------------------
$strIni = "[section]\nkey=value";
$self->testResult(sub {iniParse($strIni, {bRelaxed => true})}, '{section => {key => value}}', 'relaxed value read');
#---------------------------------------------------------------------------------------------------------------------------
$strIni = "[section]\nkey=value1\nkey=value2\nkey=value3";
$self->testResult(
sub {iniParse($strIni, {bRelaxed => true})}, '{section => {key => (value1, value2, value3)}}', 'relaxed array read');
}
################################################################################################################################
if ($self->begin("Ini->new()"))
{
#---------------------------------------------------------------------------------------------------------------------------
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();
$self->testResult(
sub {fileStringRead($strTestFile)},
$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->save();
$self->testResult(
sub {fileStringRead($strTestFile)},
$self->iniHeader(undef, BACKREST_FORMAT, BACKREST_VERSION, $oIni->hash()),
'empty with default format and version');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'normal load');
#---------------------------------------------------------------------------------------------------------------------------
my $hIni = iniParse(fileStringRead($strTestFile));
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = BOGUS;
fileStringWrite($strTestFile, iniRender($hIni));
fileStringWrite($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'");
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = $oIni->hash();
fileStringWrite($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));
$oIni->numericSet(INI_SECTION_BACKREST, INI_KEY_FORMAT, undef, BACKREST_FORMAT);
$oIni->save();
#---------------------------------------------------------------------------------------------------------------------------
$oIni->set(INI_SECTION_BACKREST, INI_KEY_VERSION, undef, '1.01');
$oIni->save();
$self->testResult(
sub {fileStringRead($strTestFile)},
$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');
$self->testResult(
sub {fileStringRead($strTestFile)},
$self->iniHeader($oIni, undef, BACKREST_VERSION),
'verify version is updated on load');
$self->testResult(sub {$oIni->save()}, false, 'save again with no changes');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {new pgBackRest::Common::Ini($strTestFile, {bLoad => false, strContent => fileStringRead($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}");
#---------------------------------------------------------------------------------------------------------------------------
fileStringWrite($strTestFileCopy, BOGUS);
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFileCopy)}, ERROR_CONFIG,
"key/value pair 'bogus' found outside of a section");
#---------------------------------------------------------------------------------------------------------------------------
$oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
fileStringWrite($strTestFileCopy, iniRender($oIni->{oContent}));
$self->testException(
sub {new pgBackRest::Common::Ini($strTestFileCopy)}, ERROR_CHECKSUM,
"invalid checksum in '${strTestFileCopy}', expected '" .
$oIni->hash() . "' but found [undef]");
#---------------------------------------------------------------------------------------------------------------------------
$oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
$oIni->{oContent}->{&INI_SECTION_BACKREST}{&INI_KEY_FORMAT} = 0;
$self->testResult(
sub {$oIni->headerCheck({bIgnoreInvalid => true})}, false,
'ignore invalid header');
#---------------------------------------------------------------------------------------------------------------------------
fileRemove($strTestFileCopy);
fileStringWrite($strTestFile, "[section]\n" . BOGUS);
$self->testException(sub {new pgBackRest::Common::Ini($strTestFile)}, ERROR_CONFIG, "unable to find '=' in 'bogus'");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(
sub {iniParse("[section]\n" . BOGUS, {bIgnoreInvalid => true})}, undef, 'ignore invalid content');
#---------------------------------------------------------------------------------------------------------------------------
fileStringWrite($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");
#---------------------------------------------------------------------------------------------------------------------------
fileStringWrite($strTestFile, iniRender($hIni));
$hIni->{&INI_SECTION_BACKREST}{&INI_KEY_CHECKSUM} = BOGUS;
fileStringWrite($strTestFileCopy, iniRender($hIni));
$self->testResult(sub {new pgBackRest::Common::Ini($strTestFile)}, '[object]', 'invalid header - load main');
}
################################################################################################################################
if ($self->begin("Ini->set()"))
{
my $oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(sub {$oIni->set()}, ERROR_ASSERT, 'strSection and strKey are required');
$self->testException(sub {$oIni->set($strSection)}, ERROR_ASSERT, 'strSection and strKey are required');
$self->testException(sub {$oIni->set(undef, $strKey)}, ERROR_ASSERT, 'strSection and strKey are required');
#---------------------------------------------------------------------------------------------------------------------------
$oIni->{bModified} = false;
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, $strValue)}, true, 'set key value');
$self->testResult($oIni->modified(), true, ' check changed flag = true');
#---------------------------------------------------------------------------------------------------------------------------
$oIni->{bModified} = false;
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, $strValue)}, false, 'set same key value');
$self->testResult($oIni->modified(), false, ' check changed flag remains false');
#---------------------------------------------------------------------------------------------------------------------------
$oIni->{bModified} = false;
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, "${strValue}2")}, true, 'set different key value');
$self->testResult($oIni->modified(), true, ' check changed flag = true');
$self->testResult(sub {$oIni->get($strSection, $strKey)}, "${strValue}2", 'get last key value');
#---------------------------------------------------------------------------------------------------------------------------
$oIni->{bModified} = false;
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, undef)}, true, 'set undef key value');
$self->testResult($oIni->modified(), true, ' check changed flag = true');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->set($strSection, "${strKey}2", $strSubKey, $strValue)}, true, 'set subkey value');
}
################################################################################################################################
if ($self->begin("Ini->get()"))
{
my $oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(sub {$oIni->get()}, ERROR_ASSERT, 'strSection is required');
$self->testException(sub {$oIni->get($strSection)}, ERROR_ASSERT, "strSection '${strSection}' is required but not defined");
$self->testException(
sub {$oIni->get($strSection, $strKey, undef)}, ERROR_ASSERT,
"strSection '${strSection}', strKey '${strKey}' is required but not defined");
$self->testException(
sub {$oIni->get($strSection, undef, $strSubKey)}, ERROR_ASSERT,
"strKey is required when strSubKey '${strSubKey}' is requested");
$self->testException(
sub {$oIni->get($strSection, $strKey, $strSubKey, true)}, ERROR_ASSERT,
"strSection '${strSection}', strKey '${strKey}', strSubKey '${strSubKey}' is required but not defined");
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->get($strSection, undef, undef, false)}, '[undef]', 'section value is not required');
$self->testResult(sub {$oIni->get($strSection, undef, undef, false, $strValue)}, $strValue, 'section value is defaulted');
#---------------------------------------------------------------------------------------------------------------------------
$oIni->set($strSection, $strKey, $strSubKey, $strValue);
$self->testResult(sub {$oIni->get($strSection, "${strKey}2", "${strSubKey}2", false)}, undef, 'missing key value');
$self->testResult(sub {$oIni->get($strSection, $strKey, "${strSubKey}2", false)}, undef, 'missing subkey value');
$self->testResult(sub {$oIni->get($strSection, $strKey, $strSubKey)}, $strValue, 'get subkey value');
$self->testResult(sub {$oIni->get($strSection, $strKey)}, "{${strSubKey} => ${strValue}}", 'get key value');
$self->testResult(sub {$oIni->get($strSection)}, "{${strKey} => {${strSubKey} => ${strValue}}}", 'get section value');
}
################################################################################################################################
if ($self->begin("Ini->remove()"))
{
my $oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
#---------------------------------------------------------------------------------------------------------------------------
$self->testException(sub {$oIni->remove()}, ERROR_ASSERT, 'strSection is required');
$self->testException(
sub {$oIni->remove($strSection, undef, $strSubKey)}, ERROR_ASSERT,
"strKey is required when strSubKey '${strSubKey}' is requested");
#---------------------------------------------------------------------------------------------------------------------------
$oIni->{bModified} = false;
$self->testResult(sub {$oIni->remove($strSection)}, false, 'undefined section is not removed');
$self->testResult($oIni->modified(), '0', ' check changed flag remains false');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, $strValue)}, true, 'set key');
$oIni->{bModified} = false;
$self->testResult(sub {$oIni->remove($strSection, $strKey)}, true, ' remove key');
$self->testResult($oIni->modified(), '1', ' check changed flag = true');
$self->testResult(sub {$oIni->test($strSection, $strKey)}, false, ' test key');
$self->testResult(sub {$oIni->test($strSection)}, false, ' test section');
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, $strValue)}, true, 'set key 1');
$self->testResult(sub {$oIni->set($strSection, "${strKey}2", undef, $strValue)}, true, ' set key 2');
$self->testResult(sub {$oIni->remove($strSection, $strKey)}, true, ' remove key 1');
$self->testResult(sub {$oIni->test($strSection, $strKey)}, false, ' test key');
$self->testResult(sub {$oIni->test($strSection)}, true, ' test section');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, $strValue)}, true, 'set key');
$self->testResult(sub {$oIni->remove($strSection)}, true, ' remove section');
$self->testResult(sub {$oIni->test($strSection)}, false, ' test section');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->set($strSection, $strKey, $strSubKey, $strValue)}, true, 'set subkey');
$self->testResult(sub {$oIni->remove($strSection, $strKey, $strSubKey)}, true, ' remove subkey');
$self->testResult(sub {$oIni->test($strSection, $strKey, $strSubKey)}, false, ' test subkey');
$self->testResult(sub {$oIni->test($strSection, $strKey)}, true, ' test key');
$self->testResult(sub {$oIni->test($strSection)}, true, ' test section');
$self->testResult(sub {$oIni->set($strSection, $strKey, $strSubKey, $strValue)}, true, 'set subkey 1');
$self->testResult(sub {$oIni->set($strSection, $strKey, "${strSubKey}2", $strValue)}, true, 'set subkey 2');
$self->testResult(sub {$oIni->remove($strSection, $strKey, $strSubKey)}, true, ' remove subkey');
$self->testResult(sub {$oIni->test($strSection, $strKey, $strSubKey)}, false, ' test subkey');
$self->testResult(sub {$oIni->test($strSection, $strKey)}, true, ' test key');
$self->testResult(sub {$oIni->test($strSection)}, true, ' test section');
}
################################################################################################################################
if ($self->begin("Ini->test()"))
{
my $oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->test($strSection, $strKey)}, false, 'test undefined key');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->set($strSection, $strKey, undef, $strValue)}, true, 'define key');
$self->testResult(sub {$oIni->test($strSection, $strKey)}, true, 'test key exists');
$self->testResult(sub {$oIni->test($strSection, $strKey, undef, $strValue)}, true, 'test key value');
$self->testResult(sub {$oIni->test($strSection, $strKey, undef, BOGUS)}, false, 'test key invalid value');
}
################################################################################################################################
if ($self->begin("Ini->boolSet() & Ini->boolGet() & Ini->boolTest()"))
{
my $oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->boolTest($strSection, $strKey)}, false, 'test bool on undefined key');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->boolGet($strSection, $strKey, undef, false, false)}, false, 'get bool default false value');
$self->testResult(sub {$oIni->boolGet($strSection, $strKey, undef, false, true)}, true, 'get bool default true value');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->boolSet($strSection, $strKey, undef, undef)}, true, 'set bool false (undef) value');
$self->testResult(sub {$oIni->boolGet($strSection, $strKey)}, false, ' check bool false value');
$self->testResult(sub {$oIni->boolTest($strSection, $strKey, undef, false)}, true, 'test bool on false key');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->boolSet($strSection, $strKey, undef, false)}, false, 'set bool false value');
$self->testResult(sub {$oIni->boolGet($strSection, $strKey)}, false, ' check value');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->boolSet($strSection, $strKey, undef, true)}, true, 'set bool false value');
$self->testResult(sub {$oIni->boolGet($strSection, $strKey)}, true, ' check value');
$self->testResult(sub {$oIni->boolTest($strSection, $strKey, undef, true)}, true, 'test bool on true key');
}
################################################################################################################################
if ($self->begin("Ini->numericSet() & Ini->numericGet() & Ini->numericTest()"))
{
my $oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->numericSet($strSection, $strKey)}, true, 'set numeric undef value');
$self->testResult(sub {$oIni->test($strSection, $strKey)}, false, 'test numeric undef value');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->numericGet($strSection, $strKey, undef, false, 1000)}, 1000, 'get numeric default value');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->numericSet($strSection, $strKey, undef, 0)}, true, 'set numeric 0 value');
$self->testResult(sub {$oIni->numericGet($strSection, $strKey)}, 0, ' check value');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->numericSet($strSection, $strKey, undef, 0)}, false, 'set numeric 0 value again');
$self->testResult(sub {$oIni->numericGet($strSection, $strKey)}, 0, ' check value');
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->numericSet($strSection, $strKey, undef, -100)}, true, 'set numeric -100 value');
$self->testResult(sub {$oIni->numericGet($strSection, $strKey)}, -100, ' check value');
}
################################################################################################################################
if ($self->begin("Ini->keys()"))
{
my $oIni = new pgBackRest::Common::Ini($strTestFile, {bLoad => false});
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->keys($strSection)}, '[undef]', 'section undefined');
#---------------------------------------------------------------------------------------------------------------------------
$oIni->set($strSection, 'a', undef, $strValue);
$oIni->set($strSection, 'c', undef, $strValue);
$oIni->set($strSection, 'b', undef, $strValue);
#---------------------------------------------------------------------------------------------------------------------------
$self->testResult(sub {$oIni->keys($strSection)}, '(a, b, c)', 'sort forward (default)');
$self->testResult(sub {$oIni->keys($strSection, INI_SORT_FORWARD)}, '(a, b, c)', 'sort forward');
$self->testResult(sub {$oIni->keys($strSection, INI_SORT_REVERSE)}, '(c, b, a)', 'sort reverse');
$self->testResult(sub {sort($oIni->keys($strSection, INI_SORT_NONE))}, '(a, b, c)', 'sort none');
$self->testException(sub {sort($oIni->keys($strSection, BOGUS))}, ERROR_ASSERT, "invalid strSortOrder '" . BOGUS . "'");
}
}
1;