####################################################################################################################################
# Tests for Backup File module
####################################################################################################################################
package pgBackRestTest::Module::Backup::BackupFileUnitPerlTest;
use parent 'pgBackRestTest::Env::HostEnvTest';

####################################################################################################################################
# Perl includes
####################################################################################################################################
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);

use File::Basename qw(dirname);
use Storable qw(dclone);

use pgBackRest::Backup::File;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::String;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::DbVersion;
use pgBackRest::Manifest;
use pgBackRest::Protocol::Helper;
use pgBackRest::Protocol::Storage::Helper;
use pgBackRest::Storage::Helper;

use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Env::Host::HostBackupTest;

####################################################################################################################################
# initModule
####################################################################################################################################
sub initModule
{
    my $self = shift;

    $self->{strDbPath} = $self->testPath() . '/db';
    $self->{strRepoPath} = $self->testPath() . '/repo';
    $self->{strBackupPath} = "$self->{strRepoPath}/backup/" . $self->stanza();
    $self->{strPgControl} = $self->{strDbPath} . '/' . DB_FILE_PGCONTROL;

    # Create backup path
    storageTest()->pathCreate($self->{strBackupPath}, {bIgnoreExists => true, bCreateParent => true});

    # Generate pg_control file
    storageTest()->pathCreate($self->{strDbPath} . '/' . DB_PATH_GLOBAL, {bCreateParent => true});
    $self->controlGenerate($self->{strDbPath}, PG_VERSION_94);
}

####################################################################################################################################
# run
####################################################################################################################################
sub run
{
    my $self = shift;

    $self->optionTestSet(CFGOPT_STANZA, $self->stanza());
    $self->optionTestSet(CFGOPT_PG_PATH, $self->{strDbPath});
    $self->optionTestSet(CFGOPT_REPO_PATH, $self->{strRepoPath});
    $self->optionTestSet(CFGOPT_LOG_PATH, $self->testPath());

    $self->optionTestSetBool(CFGOPT_ONLINE, false);

    $self->optionTestSet(CFGOPT_DB_TIMEOUT, 5);
    $self->optionTestSet(CFGOPT_PROTOCOL_TIMEOUT, 6);
    $self->optionTestSet(CFGOPT_COMPRESS_LEVEL, 3);

    $self->configTestLoad(CFGCMD_BACKUP);

    # Repo
    my $strRepoBackupPath = storageRepo()->pathGet(STORAGE_REPO_BACKUP);
    my $strBackupLabel = "20180724-182750F";

    # File
    my $strFileName = "12345";
    my $strFileDb = $self->{strDbPath} . "/$strFileName";
    my $strFileHash = '1c7e00fd09b9dd11fc2966590b3e3274645dd031';
    my $strFileRepo = storageRepo()->pathGet(
        STORAGE_REPO_BACKUP . "/$strBackupLabel/" . MANIFEST_TARGET_PGDATA . "/$strFileName");
    my $strRepoFile = MANIFEST_TARGET_PGDATA . "/$strFileName";
    my $strRepoPgControl = MANIFEST_FILE_PGCONTROL;
    my $strPgControlRepo = storageRepo()->pathGet(STORAGE_REPO_BACKUP . "/$strBackupLabel/$strRepoPgControl");
    my $strPgControlHash =
        $self->archBits() == 32 ? '8107e546c59c72a8c1818fc3610d7cc1e5623660' : '4c77c900f7af0d9ab13fa9982051a42e0b637f6c';

    # Copy file to db path
    executeTest('cp ' . $self->dataPath() . "/filecopy.archive2.bin ${strFileDb}");

    # Get size and data info for the files in the db path
    my $hManifest = storageDb()->manifest($self->{strDbPath});
    my $lFileSize = $hManifest->{$strFileName}{size} + 0;
    my $lFileTime = $hManifest->{$strFileName}{modification_time} + 0;
    my $lPgControlSize = $hManifest->{&DB_FILE_PGCONTROL}{size} + 0;
    my $lPgControlTime = $hManifest->{&DB_FILE_PGCONTROL}{modification_time} + 0;

    my $lRepoFileCompressSize = 3646899;

    my $strBackupPath = $self->{strBackupPath} . "/$strBackupLabel";
    my $strHost = "host";
    my $iLocalId = 1;

    # Initialize the manifest
    my $oBackupManifest = new pgBackRest::Manifest("$strBackupPath/" . FILE_MANIFEST,
        {bLoad => false, strDbVersion => PG_VERSION_94, iDbCatalogVersion => 201409291});
    $oBackupManifest->build(storageDb(), $self->{strDbPath}, undef, true, false);

    # Set the initial size values for backupManifestUpdate - running size and size for when to save the file
    my $lSizeCurrent = 0;
    my $lSizeTotal = 16785408;
    my $lManifestSaveCurrent = 0;
    my $lManifestSaveSize = int($lSizeTotal / 100);

    # Result variables
    my $iResultCopyResult;
    my $lResultCopySize;
    my $lResultRepoSize;
    my $strResultCopyChecksum;
    my $rResultExtra;

    ################################################################################################################################
    if ($self->begin('backupFile(), backupManifestUpdate()'))
    {
        #---------------------------------------------------------------------------------------------------------------------------
        # Copy pg_control and confirm manifestUpdate does not save the manifest yet
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($self->{strPgControl}, MANIFEST_FILE_PGCONTROL, $lPgControlSize, undef, false, $strBackupLabel, false,
            cfgOption(CFGOPT_COMPRESS_LEVEL), $lPgControlTime, true, undef, false, false, undef);

        $self->testResult(sub {storageTest()->exists($strPgControlRepo)}, true, 'pg_control file exists in repo');

        $self->testResult(($iResultCopyResult == BACKUP_FILE_COPY && $strResultCopyChecksum eq $strPgControlHash &&
            $lResultCopySize == $lPgControlSize && $lResultRepoSize == $lPgControlSize), true,
            'pg_control file copied to repo successfully');

        ($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $self->{strPgControl},
            $strRepoPgControl,
            $lPgControlSize,
            undef,
            false,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent);

        # Accumulators should be same size as pg_control
        $self->testResult(($lSizeCurrent == $lPgControlSize && $lManifestSaveCurrent == $lPgControlSize), true,
            "file size in repo and repo size equal pg_control size");

        $self->testResult(sub {$oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, MANIFEST_FILE_PGCONTROL,
            MANIFEST_SUBKEY_CHECKSUM, $strPgControlHash)}, true, "manifest updated for pg_control");

        # Neither backup.manifest nor backup.manifest.copy written because size threshold not met
        $self->testException(sub {storageRepo()->openRead("$strBackupPath/" . FILE_MANIFEST . INI_COPY_EXT)}, ERROR_FILE_MISSING,
            "unable to open '$strBackupPath/" . FILE_MANIFEST . INI_COPY_EXT . "': No such file or directory");
        $self->testException(sub {storageRepo()->openRead("$strBackupPath/" . FILE_MANIFEST)}, ERROR_FILE_MISSING,
            "unable to open '$strBackupPath/" . FILE_MANIFEST . "': No such file or directory");

        #---------------------------------------------------------------------------------------------------------------------------
        # No prior checksum, no compression, no page checksum, no extra, no delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize, undef, false, $strBackupLabel, false,
            cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, false, false, undef);

        $self->testResult(sub {storageTest()->exists($strFileRepo)}, true, 'non-compressed file exists in repo');

        $self->testResult(($iResultCopyResult == BACKUP_FILE_COPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize && $lResultRepoSize == $lFileSize), true,
            'file copied to repo successfully');

        $self->testException(sub {storageRepo()->openRead("$strFileRepo.gz")}, ERROR_FILE_MISSING,
            "unable to open '$strFileRepo.gz': No such file or directory");

        ($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            false,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent);

        # Accumulator includes size of pg_control and file. Manifest saved so ManifestSaveCurrent returns to 0
        $self->testResult(($lSizeCurrent == ($lPgControlSize + $lFileSize) && $lManifestSaveCurrent == 0), true,
            "repo size increased and ManifestSaveCurrent returns to 0");

        $self->testResult(sub {$oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, $strRepoFile,
            MANIFEST_SUBKEY_CHECKSUM, $strFileHash)}, true, "manifest updated for $strRepoFile");

        # Backup.manifest not written but backup.manifest.copy written because size threshold  met
        $self->testResult(sub {storageTest()->exists("$strBackupPath/" . FILE_MANIFEST . INI_COPY_EXT)}, true,
            'backup.manifest.copy exists in repo');
        $self->testException(sub {storageRepo()->openRead("$strBackupPath/" . FILE_MANIFEST)}, ERROR_FILE_MISSING,
            "unable to open '$strBackupPath/" . FILE_MANIFEST . "': No such file or directory");

        storageTest()->remove($strFileRepo);
        storageTest()->remove($strPgControlRepo);

        #---------------------------------------------------------------------------------------------------------------------------
        # No prior checksum, yes compression, yes page checksum, no extra, no delta, no hasReference
        $self->testException(sub {backupFile($strFileDb, $strRepoFile, $lFileSize, undef, true,
            $strBackupLabel, true, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, false, false, undef)}, ERROR_ASSERT,
            "iWalId is required in Backup::Filter::PageChecksum->new");

        # Build the lsn start parameter to pass to the extra function
        my $hStartLsnParam =
        {
            iWalId => 0xFFFF,
            iWalOffset => 0xFFFF,
        };

        #---------------------------------------------------------------------------------------------------------------------------
        # No prior checksum, yes compression, yes page checksum, yes extra, no delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize, undef, true, $strBackupLabel, true,
            cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, $hStartLsnParam, false, false, undef);

        $self->testResult(sub {storageTest()->exists("$strFileRepo.gz")}, true, 'compressed file exists in repo');

        $self->testResult(($iResultCopyResult == BACKUP_FILE_COPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultRepoSize == $lRepoFileCompressSize && $rResultExtra->{bValid}), true, 'file copied to repo successfully');

        # Only the compressed version of the file exists
        $self->testException(sub {storageRepo()->openRead("$strFileRepo")}, ERROR_FILE_MISSING,
            "unable to open '$strFileRepo': No such file or directory");

        ($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            true,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent);

        # File is compressed in repo so make sure repo-size added to manifest
        $self->testResult(sub {$oBackupManifest->test(
            MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REPO_SIZE, $lResultRepoSize)},
            true, "repo-size set");
        $self->testResult(sub {$oBackupManifest->test(
            MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM_PAGE, $rResultExtra->{bValid})},
            true, "checksum page set");

        # Save the compressed file for later test
        executeTest('mv ' . "$strFileRepo.gz $strFileRepo.gz.SAVE");

        #---------------------------------------------------------------------------------------------------------------------------
        # Add a segment number for bChecksumPage code coverage
        executeTest('cp ' . "$strFileDb $strFileDb.1");

        # No prior checksum, no compression, yes page checksum, yes extra, no delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile("$strFileDb.1", "$strRepoFile.1", $lFileSize, undef, true, $strBackupLabel, false,
            cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, $hStartLsnParam, false, false, undef);

        $self->testResult(sub {storageTest()->exists("$strFileRepo.1")}, true, 'non-compressed segment file exists in repo');

        $self->testResult(($iResultCopyResult == BACKUP_FILE_COPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultRepoSize == $lFileSize && $rResultExtra->{bValid}), true, 'segment file copied to repo successfully');

        # Set a section in  the manifest to ensure it is removed in the next test
        $oBackupManifest->set(
            MANIFEST_SECTION_TARGET_FILE, "$strRepoFile.1", MANIFEST_SUBKEY_CHECKSUM, $strResultCopyChecksum);

        $self->testResult(sub {$oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, MANIFEST_TARGET_PGDATA . "/$strFileName.1")},
            true, MANIFEST_TARGET_PGDATA . "/$strFileName.1 section exists in manifest");

        #---------------------------------------------------------------------------------------------------------------------------
        # Remove the db file and try to back it up
        storageTest()->remove("$strFileDb.1");

        # No prior checksum, no compression, no page checksum, no extra, No delta, no hasReference, no db file
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile("$strFileDb.1", "$strRepoFile.1", $lFileSize, undef, false, $strBackupLabel,
            false, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, false, false, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_SKIP && !defined($strResultCopyChecksum) &&
            !defined($lResultRepoSize) && !defined($lResultCopySize)), true, "db file missing - $strRepoFile.1 file skipped");

        # Delta not set so file still exists in repo
        $self->testResult(sub {storageTest()->exists("$strFileRepo.1")}, true, '    delta not set - file exists in repo');

        ($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            "$strFileDb.1",
            "$strRepoFile.1",
            $lFileSize,
            $strFileHash,
            false,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent);

        $self->testResult(sub {$oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, "$strRepoFile.1")},
            false, "    $strRepoFile.1 section removed from manifest");

        # Yes prior checksum, no compression, no page checksum, no extra, yes delta, no hasReference, no db file
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile("$strFileDb.1", MANIFEST_TARGET_PGDATA . "/$strFileName.1", $lFileSize, $strFileHash, false, $strBackupLabel,
            false, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, false, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_SKIP && !defined($strResultCopyChecksum) &&
            !defined($lResultRepoSize)), true, "db file missing - delta $strRepoFile.1 file skipped");

        $self->testResult(sub {storageTest()->exists("$strFileRepo.1")}, false, '   delta set - file removed from repo');

        # Code path for host not defined for logged message of skipped file
        ($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(
            $oBackupManifest,
            undef,
            $iLocalId,
            "$strFileDb.1",
            MANIFEST_TARGET_PGDATA . "/$strFileName.1",
            $lFileSize,
            $strFileHash,
            false,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent);

        # Yes prior checksum, no compression, no page checksum, no extra, yes delta, no hasReference, no db file,
        # do not ignoreMissing
        $self->testException(sub {backupFile("$strFileDb.1", "$strRepoFile.1", $lFileSize, $strFileHash,
            false, $strBackupLabel, false, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, false, undef, true, false, undef)},
            ERROR_FILE_MISSING, "unable to open '$strFileDb.1': No such file or directory");

        #---------------------------------------------------------------------------------------------------------------------------
        # Restore the compressed file
        executeTest('mv ' . "$strFileRepo.gz.SAVE $strFileRepo.gz");

        # Yes prior checksum, yes compression, no page checksum, no extra, yes delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize, $strFileHash, false, $strBackupLabel,
            true, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, false, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_CHECKSUM && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize), true, 'db checksum and repo same - no copy file');

        #---------------------------------------------------------------------------------------------------------------------------
        # DB Checksum mismatch
        storageTest()->remove("$strFileRepo", {bIgnoreMissing => true});
        # Save the compressed file for later test
        executeTest('mv ' . "$strFileRepo.gz $strFileRepo.gz.SAVE");

        # Yes prior checksum, no compression, no page checksum, no extra, yes delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize, $strFileHash . "ff", false,
            $strBackupLabel, false, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, false, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_COPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize && $lResultRepoSize == $lFileSize), true, 'db checksum mismatch - copy file');

        #---------------------------------------------------------------------------------------------------------------------------
        # DB file size mismatch
        # Yes prior checksum, no compression, no page checksum, no extra, yes delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize + 1, $strFileHash, false, $strBackupLabel, false,
            cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, false, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_COPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize && $lResultRepoSize == $lFileSize), true, 'db file size mismatch - copy file');

        #---------------------------------------------------------------------------------------------------------------------------
        # Repo mismatch

        # Restore the compressed file as if non-compressed so checksum won't match
        executeTest('cp ' . "$strFileRepo.gz.SAVE $strFileRepo");

        # Yes prior checksum, no compression, no page checksum, no extra, yes delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize, $strFileHash, false, $strBackupLabel, false,
            cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, false, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_RECOPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize && $lResultRepoSize == $lFileSize), true, 'repo checksum mismatch - recopy file');

        # Restore the compressed file
        executeTest('mv ' . "$strFileRepo.gz.SAVE $strFileRepo.gz");

        # Yes prior checksum, yes compression, no page checksum, no extra, no delta, no hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize + 1, $strFileHash, false,
            $strBackupLabel, true, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, false, false, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_RECOPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize), true, 'repo size mismatch - recopy file');

        #---------------------------------------------------------------------------------------------------------------------------
        # Has reference
        # Set a reference in the manifest to ensure it is removed after backupManifestUpdate
        $oBackupManifest->set(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REFERENCE, BOGUS);

        $self->testResult(sub {$oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REFERENCE,
            BOGUS)}, true, "$strRepoFile reference section exists in manifest");

        # Yes prior checksum, no compression, no page checksum, no extra, yes delta, yes hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize + 1, $strFileHash, false,
            $strBackupLabel, false, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, true, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_COPY && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize && $lResultRepoSize == $lFileSize), true, 'db file size mismatch has reference - copy');

        # Code path to ensure reference is removed
        ($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            false,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent);

        # Confirm reference to prior backup removed
        $self->testResult(sub {$oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, MANIFEST_TARGET_PGDATA . "/$strFileName.",
            MANIFEST_SUBKEY_REFERENCE)},
            false, "reference to prior backup in manifest removed");

        #---------------------------------------------------------------------------------------------------------------------------
        # BACKUP_FILE_NOOP
        # Yes prior checksum, no compression, no page checksum, no extra, yes delta, yes hasReference
        ($iResultCopyResult, $lResultCopySize, $lResultRepoSize, $strResultCopyChecksum, $rResultExtra) =
            backupFile($strFileDb, $strRepoFile, $lFileSize, $strFileHash, false,
            $strBackupLabel, false, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, true, undef);

        $self->testResult(($iResultCopyResult == BACKUP_FILE_NOOP && $strResultCopyChecksum eq $strFileHash &&
            $lResultCopySize == $lFileSize), true, 'db file same has reference - noop');

        # Calculate running counts
        my $lSizeCurrentAfter = $lSizeCurrent + $lFileSize;
        my $lManifestSaveCurrentAfter = $lManifestSaveCurrent + $lFileSize;

        # Increase manifest save size, so manifest will not be saved so counts can be tested
        $lManifestSaveSize = $lFileSize * 2;

        ($lSizeCurrent, $lManifestSaveCurrent) = backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            false,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent);

        $self->testResult(($lSizeCurrent ==$lSizeCurrentAfter && $lManifestSaveCurrent == $lManifestSaveCurrentAfter),
            true, '    running counts updated');

        #---------------------------------------------------------------------------------------------------------------------------
        # Remove file from repo. No reference so should hard error since this means sometime between the building of the manifest
        # for the aborted backup, the file went missing from the aborted backup dir.
        storageTest()->remove("$strFileRepo", {bIgnoreMissing => true});

        $self->testException(sub {backupFile($strFileDb, $strRepoFile, $lFileSize, $strFileHash,
            false, $strBackupLabel, false, cfgOption(CFGOPT_COMPRESS_LEVEL), $lFileTime, true, undef, true, false, undef)},
            ERROR_FILE_MISSING, "unable to open '$strFileRepo': No such file or directory");
    }

    ################################################################################################################################
    # This section for for code coverage that is not covered in the above tests
    if ($self->begin('backupManifestUpdate()'))
    {
        $oBackupManifest = new pgBackRest::Manifest("$strBackupPath/" . FILE_MANIFEST,
            {bLoad => false, strDbVersion => PG_VERSION_94, iDbCatalogVersion => 201409291});

        #---------------------------------------------------------------------------------------------------------------------------
        # Check BACKUP_FILE_RECOPY warning
        $iResultCopyResult = BACKUP_FILE_RECOPY;
        $lResultCopySize = 0;
        $lResultRepoSize = $lResultCopySize + 1;
        $strResultCopyChecksum = $strFileHash;
        $lSizeCurrent = 0;
        $lManifestSaveSize = $lFileSize * 2;
        $lManifestSaveCurrent = 0;

        $self->testResult(sub {backupManifestUpdate(
            $oBackupManifest,
            undef,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            false,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent)}, "($lFileSize, $lFileSize)",
            'backup file recopy warning', {strLogExpect =>
            "WARN: resumed backup file $strRepoFile does not have expected checksum $strFileHash. The file will be recopied and" .
            " backup will continue but this may be an issue unless the resumed backup path in the repository is known to be" .
            " corrupted.\n" .
            "NOTE: this does not indicate a problem with the PostgreSQL page checksums."});

        # Check size code paths
        $self->testResult(
            $oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_SIZE, $lResultCopySize),
            true, "    copy size set");
        $self->testResult(
            $oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_REPO_SIZE, $lResultRepoSize),
            true, "    repo size set");
        $self->testResult(
            $oBackupManifest->test(MANIFEST_SECTION_TARGET_FILE, $strRepoFile, MANIFEST_SUBKEY_CHECKSUM, $strResultCopyChecksum),
            false, "    checksum not set since copy size 0");

        #---------------------------------------------------------------------------------------------------------------------------
        # Checkum page exception
        $iResultCopyResult = BACKUP_FILE_COPY;

        $self->testException(sub {backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            true,
            $iResultCopyResult,
            $lResultCopySize,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent)},
            ERROR_ASSERT, "$strFileDb should have calculated page checksums");

        $rResultExtra->{bValid} = false;
        $self->testException(sub {backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            true,
            $iResultCopyResult,
            $lResultCopySize + 1,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent)},
            ERROR_ASSERT, "bAlign flag should have been set for misaligned page");

        $rResultExtra->{bAlign} = true;
        $self->testException(sub {backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            true,
            $iResultCopyResult,
            $lResultCopySize + 1,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent)},
            ERROR_ASSERT, "bAlign flag should have been set for misaligned page");

        $rResultExtra->{bAlign} = false;
        $self->testResult(sub {backupManifestUpdate(
            $oBackupManifest,
            $strHost,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            true,
            $iResultCopyResult,
            $lResultCopySize + 1,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent)},
            "($lFileSize, $lFileSize)",
            'page misalignment warning - host defined', {strLogExpect =>
            "WARN: page misalignment in file $strHost:$strFileDb: file size " . ($lResultCopySize + 1) .
            " is not divisible by page size " . PG_PAGE_SIZE});

        $self->testResult(sub {backupManifestUpdate(
            $oBackupManifest,
            undef,
            $iLocalId,
            $strFileDb,
            $strRepoFile,
            $lFileSize,
            $strFileHash,
            true,
            $iResultCopyResult,
            $lResultCopySize + 1,
            $lResultRepoSize,
            $strResultCopyChecksum,
            $rResultExtra,
            $lSizeTotal,
            $lSizeCurrent,
            $lManifestSaveSize,
            $lManifestSaveCurrent)},
            "($lFileSize, $lFileSize)",
            'page misalignment warning - host not defined', {strLogExpect =>
            "WARN: page misalignment in file $strFileDb: file size " . ($lResultCopySize + 1) .
            " is not divisible by page size " . PG_PAGE_SIZE});
    }
}

1;