VOID LogTestBase::BasicIOTest()

in src/prod/src/data/logicallog/LogTestBase.cpp [660:1163]


    VOID LogTestBase::BasicIOTest(__in KtlLoggerMode, __in KGuid const & physicalLogId)
    {
        NTSTATUS status;

        ILogManagerHandle::SPtr logManager;
        status = CreateAndOpenLogManager(logManager);
        VERIFY_STATUS_SUCCESS("CreateAndOpenLogManager", status);

        LogManager* logManagerService = nullptr;
        {
            LogManagerHandle* logManagerServiceHandle = dynamic_cast<LogManagerHandle*>(logManager.RawPtr());
            if (logManagerServiceHandle != nullptr)
            {
                logManagerService = logManagerServiceHandle->owner_.RawPtr();
            }
        }
        VerifyState(
            logManagerService,
            1,
            0,
            TRUE);

        KString::SPtr physicalLogName;
        GenerateUniqueFilename(physicalLogName);

        // Don't care if this fails
        SyncAwait(logManager->DeletePhysicalLogAsync(*physicalLogName, physicalLogId, CancellationToken::None));

        IPhysicalLogHandle::SPtr physicalLog;
        status = CreatePhysicalLog(*logManager, physicalLogId, *physicalLogName, physicalLog);
        VERIFY_STATUS_SUCCESS("CreatePhysicalLog", status);

        VerifyState(
            logManagerService,
            1,
            1,
            TRUE,
            dynamic_cast<PhysicalLogHandle*>(physicalLog.RawPtr()) != nullptr ? &(static_cast<PhysicalLogHandle&>(*physicalLog).Owner) : nullptr,
            1,
            0,
            TRUE,
            dynamic_cast<PhysicalLogHandle*>(physicalLog.RawPtr()),
            TRUE);


        KString::SPtr logicalLogName;
        GenerateUniqueFilename(logicalLogName);

        KGuid logicalLogId;
        logicalLogId.CreateNew();

        ILogicalLog::SPtr logicalLog;
        status = CreateLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("CreateLogicalLog", status);
        VerifyState(
            logManagerService,
            1,
            1,
            TRUE,
            dynamic_cast<PhysicalLogHandle*>(physicalLog.RawPtr()) != nullptr ? &(static_cast<PhysicalLogHandle&>(*physicalLog).Owner) : nullptr,
            1,
            1,
            TRUE,
            dynamic_cast<PhysicalLogHandle*>(physicalLog.RawPtr()),
            TRUE,
            nullptr,
            -1,
            -1,
            FALSE,
            nullptr,
            FALSE,
            FALSE,
            dynamic_cast<LogicalLog*>(logicalLog.RawPtr()),
            TRUE);
        VERIFY_ARE_EQUAL(0, logicalLog->Length);
        VERIFY_ARE_EQUAL(0, logicalLog->ReadPosition);
        VERIFY_ARE_EQUAL(0, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(-1, logicalLog->HeadTruncationPosition);

        static const LONG MaxLogBlkSize = 128 * 1024;


        // Prove simple recovery
        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        status = OpenLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("OpenLogicalLog", status);
        VERIFY_ARE_EQUAL(0, logicalLog->Length);
        VERIFY_ARE_EQUAL(0, logicalLog->ReadPosition);
        VERIFY_ARE_EQUAL(0, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(-1, logicalLog->HeadTruncationPosition);

        const int recordSize = 131;

        // Prove we can write bytes
        KBuffer::SPtr x;
        PUCHAR xPtr;
        AllocBuffer(recordSize, x, xPtr);
        BuildDataBuffer(*x, 0, 0, recordSize);
        ValidateDataBuffer(*x, x->QuerySize(), 0, 0, 0, recordSize);

        VERIFY_ARE_EQUAL(0, logicalLog->WritePosition);
        status = SyncAwait(logicalLog->AppendAsync(*x, 0, x->QuerySize(), CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::AppendAsync", status);
        VERIFY_ARE_EQUAL(recordSize, logicalLog->WritePosition);

        status = SyncAwait(logicalLog->FlushWithMarkerAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::FlushWithMarkerAsync", status);

        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        // Prove simple (non-null recovery - only one (asn 1) physical record in the log)
        status = OpenLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("OpenLogicalLog", status);
        VERIFY_ARE_EQUAL(recordSize, logicalLog->Length);
        VERIFY_ARE_EQUAL(0, logicalLog->ReadPosition);
        VERIFY_ARE_EQUAL(recordSize, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(-1, logicalLog->HeadTruncationPosition);

        status = SyncAwait(logicalLog->AppendAsync(*x, 0, x->QuerySize(), CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::AppendAsync", status);
        VERIFY_ARE_EQUAL(recordSize * 2, logicalLog->WritePosition);
        status = SyncAwait(logicalLog->FlushWithMarkerAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::FlushWithMarkerAsync", status);
        
        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        // Prove simple (non-null recovery)
        status = OpenLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("OpenLogicalLog", status);
        VERIFY_ARE_EQUAL(recordSize * 2, logicalLog->Length);
        VERIFY_ARE_EQUAL(0, logicalLog->ReadPosition);
        VERIFY_ARE_EQUAL(recordSize * 2, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(-1, logicalLog->HeadTruncationPosition);

        // Prove simple read and positioning works
        AllocBuffer(recordSize, x, xPtr);
        LONG bytesRead;
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(recordSize, bytesRead);
        ValidateDataBuffer(*x, x->QuerySize(), 0, 0, 0, recordSize);

        VERIFY_ARE_EQUAL(recordSize, logicalLog->ReadPosition);

        // Prove next sequential physical read works
        AllocBuffer(recordSize, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(recordSize, bytesRead);
        ValidateDataBuffer(*x, x->QuerySize(), 0, 0, 0, recordSize);

        VERIFY_ARE_EQUAL(recordSize * 2, logicalLog->ReadPosition);

        // Positioning and reading partial and across physical records work
        int offset = 1;
        status = logicalLog->SeekForRead(offset, Common::SeekOrigin::Enum::Begin);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        AllocBuffer(recordSize, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(recordSize, bytesRead);
        ValidateDataBuffer(*x, x->QuerySize(), 0, offset, 0, recordSize);

        VERIFY_ARE_EQUAL(recordSize + offset, logicalLog->ReadPosition);

        // Prove reading a short record works
        x->Zero();
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(recordSize - 1, bytesRead);
        ValidateDataBuffer(*x, recordSize - 1, 0, offset, 0, recordSize);

        VERIFY_ARE_EQUAL(logicalLog->WritePosition, logicalLog->ReadPosition);

        // Prove reading at EOS works
        AllocBuffer(recordSize, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(0, bytesRead);
        VERIFY_ARE_EQUAL(logicalLog->WritePosition, logicalLog->ReadPosition);

        // Prove position beyond and just EOS and reading works
        LONGLONG currentPos;
        status = logicalLog->SeekForRead(1, Common::SeekOrigin::Enum::End);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        currentPos = logicalLog->ReadPosition;
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(0, bytesRead);

        LONGLONG eos;
        status = logicalLog->SeekForRead(0, Common::SeekOrigin::Enum::End);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        eos = logicalLog->ReadPosition;
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(0, bytesRead);

        VERIFY_ARE_EQUAL(logicalLog->WritePosition, eos);

        status = logicalLog->SeekForRead(-1, Common::SeekOrigin::Enum::End);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        currentPos = logicalLog->ReadPosition;
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, 1, 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(1, bytesRead);
        ValidateDataBuffer(*x, 1, 0, currentPos, 0, recordSize);

        status = logicalLog->SeekForRead(-1, Common::SeekOrigin::Enum::End);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        currentPos = logicalLog->ReadPosition;
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, 2, 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(1, bytesRead);
        ValidateDataBuffer(*x, 1, 0, currentPos, 0, recordSize);

        status = logicalLog->SeekForRead(-2, Common::SeekOrigin::Enum::End);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        currentPos = logicalLog->ReadPosition;
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, 2, 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(2, bytesRead);
        ValidateDataBuffer(*x, 2, 0, currentPos, 0, recordSize);

        // Prove we can position forward to every location
        currentPos = eos - 1;

        while (currentPos >= 0)
        {
            status = logicalLog->SeekForRead(currentPos, Common::SeekOrigin::Enum::Begin);
            VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
            VERIFY_ARE_EQUAL(currentPos, logicalLog->ReadPosition);
            KBuffer::SPtr rBuff;
            status = KBuffer::Create((ULONG)(eos - currentPos), rBuff, GetThisAllocator());
            VERIFY_STATUS_SUCCESS("KBuffer::Create", status);
            status = SyncAwait(logicalLog->ReadAsync(bytesRead, *rBuff, 0, rBuff->QuerySize(), 0, CancellationToken::None));
            VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
            VERIFY_ARE_EQUAL(rBuff->QuerySize(), (ULONG)bytesRead);
            currentPos--;
        }

        // Prove Stream read abstraction works through a BufferedStream
        // (since we don't have a BufferedStream, use KStream)
        ILogicalLogReadStream::SPtr readStream;
        status = logicalLog->CreateReadStream(readStream, 0);
        VERIFY_STATUS_SUCCESS("LogicalLog::CreateReadStream", status);
        ktl::io::KStream::SPtr bStream = readStream.RawPtr();

        bStream->Position = bStream->Length + 1; // seek to end + 1
        currentPos = bStream->Position;
        ULONG uBytesRead;
        status = SyncAwait(bStream->ReadAsync(*x, uBytesRead, 0, x->QuerySize()));
        VERIFY_STATUS_SUCCESS("KStream::ReadAsync", status);
        VERIFY_ARE_EQUAL(0, uBytesRead);

        bStream->Position = bStream->Length; // seek to end
        eos = bStream->Position;
        VERIFY_ARE_EQUAL(logicalLog->WritePosition, eos);

        bStream->Position = bStream->Length - 1; // seek to end - 1
        currentPos = bStream->Position;
        status = SyncAwait(bStream->ReadAsync(*x, uBytesRead, 0, 1));
        VERIFY_STATUS_SUCCESS("KStream::ReadAsync", status);
        VERIFY_ARE_EQUAL(1, uBytesRead);
        ValidateDataBuffer(*x, 1, 0, currentPos, 0, recordSize);

        bStream->Position = bStream->Length - 1; // seek to end - 1
        currentPos = bStream->Position;
        status = SyncAwait(bStream->ReadAsync(*x, uBytesRead, 0, 2));
        VERIFY_STATUS_SUCCESS("KStream::ReadAsync", status);
        VERIFY_ARE_EQUAL(1, uBytesRead);
        ValidateDataBuffer(*x, 1, 0, currentPos, 0, recordSize);

        bStream->Position = bStream->Length - 2; // seek to end - 2
        currentPos = bStream->Position;
        status = SyncAwait(bStream->ReadAsync(*x, uBytesRead, 0, 2));
        VERIFY_STATUS_SUCCESS("KStream::ReadAsync", status);
        VERIFY_ARE_EQUAL(2, uBytesRead);
        ValidateDataBuffer(*x, 2, 0, currentPos, 0, recordSize);

        currentPos = eos - 1;

        while (currentPos >= 0)
        {
            bStream->Position = currentPos;
            KBuffer::SPtr rBuff;
            status = KBuffer::Create((ULONG)(eos - currentPos), rBuff, GetThisAllocator());
            VERIFY_STATUS_SUCCESS("KBuffer::Create", status);
            status = SyncAwait(bStream->ReadAsync(*rBuff, uBytesRead, 0, rBuff->QuerySize()));
            VERIFY_STATUS_SUCCESS("KStream::ReadAsync", status);
            VERIFY_ARE_EQUAL(rBuff->QuerySize(), uBytesRead);

            currentPos--;
        }

        status = SyncAwait(bStream->CloseAsync());
        VERIFY_STATUS_SUCCESS("KStream::CloseAsync", status);
        bStream = nullptr;
        readStream->Dispose(); // idempotent
        readStream = nullptr;

        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        status = SyncAwait(physicalLog->DeleteLogicalLogOnlyAsync(logicalLogId, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("IPhysicalLogHandle::DeleteLogicalLogOnlyAsync", status);

        // Prove some large I/O
        status = CreateLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("CreateLogicalLog", status);
        VerifyState(
            logManagerService,
            1,
            1,
            TRUE,
            dynamic_cast<PhysicalLogHandle*>(physicalLog.RawPtr()) != nullptr ? &(static_cast<PhysicalLogHandle&>(*physicalLog).Owner) : nullptr,
            1,
            1,
            TRUE,
            dynamic_cast<PhysicalLogHandle*>(physicalLog.RawPtr()),
            TRUE,
            nullptr,
            -1,
            -1,
            FALSE,
            nullptr,
            FALSE,
            FALSE,
            dynamic_cast<LogicalLog*>(logicalLog.RawPtr()),
            TRUE);
        VERIFY_ARE_EQUAL(0, logicalLog->Length);
        VERIFY_ARE_EQUAL(0, logicalLog->ReadPosition);
        VERIFY_ARE_EQUAL(0, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(-1, logicalLog->HeadTruncationPosition);

        AllocBuffer(1024 * 1024, x, xPtr);
        BuildDataBuffer(*x, 0, 0, recordSize);

        status = SyncAwait(logicalLog->AppendAsync(*x, 0, x->QuerySize(), CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::AppendAsync", status);
        VERIFY_ARE_EQUAL(1024 * 1024, logicalLog->WritePosition);
        status = SyncAwait(logicalLog->FlushWithMarkerAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::FlushWithMarkerAsync", status);

        AllocBuffer(1024 * 1024, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(x->QuerySize(), (ULONG)bytesRead);
        ValidateDataBuffer(*x, x->QuerySize(), 0, 0, 0, recordSize);

        // Prove basic head truncation behavior
        status = SyncAwait(logicalLog->TruncateHead(128 * 1024));
        VERIFY_STATUS_SUCCESS("LogicalLog::TruncateHead", status);
        VERIFY_ARE_EQUAL(128 * 1024, logicalLog->HeadTruncationPosition);

        // Force a record out to make sure the head truncation point is captured
        const KStringView testStr1(L"Test String Record");
        AllocBuffer(testStr1.LengthInBytes(), x, xPtr);
        LPCWSTR str = testStr1;
        KMemCpySafe(xPtr, x->QuerySize(), str, testStr1.LengthInBytes());
        status = SyncAwait(logicalLog->AppendAsync(*x, 0, x->QuerySize(), CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::AppendAsync", status);
        status = SyncAwait(logicalLog->FlushWithMarkerAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::FlushAsync", status);

        // Prove head truncation point is recovered
        LONGLONG currentLength = logicalLog->WritePosition;
        LONGLONG currentTrucPoint = logicalLog->HeadTruncationPosition;

        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        status = OpenLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("OpenLogicalLog", status);
        VERIFY_ARE_EQUAL(currentLength, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(currentTrucPoint, logicalLog->HeadTruncationPosition);

        // Prove basic tail truncation behavior
        status = logicalLog->SeekForRead(currentLength - x->QuerySize(), Common::SeekOrigin::Enum::Begin);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        LONGLONG newEos = logicalLog->ReadPosition;
        AllocBuffer(256, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        KString::SPtr chars;
        status = KString::Create(chars, GetThisAllocator(), bytesRead / sizeof(WCHAR));
        VERIFY_STATUS_SUCCESS("KString::Create", status);
        chars->SetLength(chars->BufferSizeInChars());
        KMemCpySafe((PBYTE)(PVOID)*chars, chars->LengthInBytes(), xPtr, (ULONG)bytesRead);
        VERIFY_ARE_EQUAL(testStr1, *chars);

        // Chop off the last and part of the 2nd to last records
        newEos = newEos - bytesRead - 1;
        status = SyncAwait(logicalLog->TruncateTail(newEos, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::TruncateTail", status);
        VERIFY_ARE_EQUAL(newEos, logicalLog->WritePosition);

        // Prove we can read valid up to the new EOS but not beyond
        status = logicalLog->SeekForRead(newEos - 128, Common::SeekOrigin::Enum::Begin);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        AllocBuffer(256, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(128, bytesRead);
        ValidateDataBuffer(*x, bytesRead, 0, newEos - 128, 0, recordSize);

        // Prove we recover the correct tail position (WritePosition)
        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        status = OpenLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("OpenLogicalLog", status);
        VERIFY_ARE_EQUAL(newEos, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(currentTrucPoint, logicalLog->HeadTruncationPosition);

        // Prove we can truncate all content via tail truncation
        VERIFY_ARE_NOT_EQUAL(0, logicalLog->Length);
        status = SyncAwait(logicalLog->TruncateTail(currentTrucPoint + 1, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::TruncateTail", status);
        VERIFY_ARE_EQUAL(0, logicalLog->Length);
        VERIFY_ARE_EQUAL(currentTrucPoint + 1, logicalLog->WritePosition);

        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        status = OpenLogicalLog(*physicalLog, logicalLogId, *logicalLogName, logicalLog);
        VERIFY_STATUS_SUCCESS("OpenLogicalLog", status);
        VERIFY_ARE_EQUAL(currentTrucPoint + 1, logicalLog->WritePosition);
        VERIFY_ARE_EQUAL(0, logicalLog->Length);
        VERIFY_ARE_EQUAL(currentTrucPoint, logicalLog->HeadTruncationPosition);

        // Add proof for truncating all but one byte - read it for the right value
        LONGLONG currentWritePos = logicalLog->WritePosition;
        AllocBuffer(1024 * 1024, x, xPtr);
        BuildDataBuffer(*x, currentWritePos, 0, recordSize);

        status = SyncAwait(logicalLog->AppendAsync(*x, 0, x->QuerySize(), CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::AppendAsync", status);
        VERIFY_ARE_EQUAL(currentWritePos + (1024 * 1024), logicalLog->WritePosition);
        status = SyncAwait(logicalLog->FlushWithMarkerAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::FlushWithMarkerAsync", status);

        status = logicalLog->SeekForRead(currentWritePos, Common::SeekOrigin::Enum::Begin);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        VERIFY_ARE_EQUAL(currentWritePos, logicalLog->ReadPosition);
        AllocBuffer(1024 * 1024, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(x->QuerySize(), (ULONG)bytesRead);
        ValidateDataBuffer(*x, x->QuerySize(), 0, currentWritePos, 0, recordSize);

        status = SyncAwait(logicalLog->TruncateHead(currentWritePos + (1024 * 513)));
        VERIFY_STATUS_SUCCESS("LogicalLog::TruncateHead", status);
        VERIFY_ARE_EQUAL(currentWritePos + (1024 * 513), logicalLog->HeadTruncationPosition);

        status = logicalLog->SeekForRead(currentWritePos + (1024 * 513) + 1, Common::SeekOrigin::Enum::Begin);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        VERIFY_ARE_EQUAL(currentWritePos + (1024 * 513) + 1, logicalLog->ReadPosition);
        AllocBuffer((ULONG)logicalLog->Length, x, xPtr);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, x->QuerySize(), 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(x->QuerySize(), (ULONG)bytesRead);
        ValidateDataBuffer(*x, x->QuerySize(), 0, currentWritePos + (1024 * 513) + 1, 0, recordSize);

        status = SyncAwait(logicalLog->TruncateTail(currentWritePos + (1024 * 513) + 2, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::TruncateTail", status);
        VERIFY_ARE_EQUAL(1, logicalLog->Length);

        status = logicalLog->SeekForRead(currentWritePos + (1024 * 513) + 1, Common::SeekOrigin::Enum::Begin);
        VERIFY_STATUS_SUCCESS("LogicalLog::SeekForRead", status);
        VERIFY_ARE_EQUAL(currentWritePos + (1024 * 513) + 1, logicalLog->ReadPosition);
        ((PBYTE)x->GetBuffer())[0] = ~(((PBYTE)x->GetBuffer())[0]);
        status = SyncAwait(logicalLog->ReadAsync(bytesRead, *x, 0, 1, 0, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::ReadAsync", status);
        VERIFY_ARE_EQUAL(1, bytesRead);
        ValidateDataBuffer(*x, 1, 0, currentWritePos + (1024 * 513) + 1, 0, recordSize);

        // Prove close down
        status = SyncAwait(logicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogicalLog::CloseAsync", status);
        logicalLog = nullptr;

        status = SyncAwait(physicalLog->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("PhysicalLog::CloseAsync", status);
        physicalLog = nullptr;

        status = SyncAwait(logManager->DeletePhysicalLogAsync(*physicalLogName, physicalLogId, CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogManager::DeletePhysicalLogAsync", status);

        status = SyncAwait(logManager->CloseAsync(CancellationToken::None));
        VERIFY_STATUS_SUCCESS("LogManager::CloseAsync", status);
        logManager = nullptr;
    }