1423 lines
42 KiB
C++
1423 lines
42 KiB
C++
|
|
||
|
|
||
|
//
|
||
|
// The following tests need to be added:
|
||
|
//
|
||
|
// - Run the bad-shutdown test on an empty log (forcing a special path
|
||
|
// in CalcLogInfo).
|
||
|
//
|
||
|
|
||
|
|
||
|
|
||
|
#include "pch.cxx"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#define TRKDATA_ALLOCATE
|
||
|
#include "trkwks.hxx"
|
||
|
|
||
|
|
||
|
|
||
|
BOOL g_fNotified = FALSE;
|
||
|
extern ULONG g_Debug;
|
||
|
BOOL g_fTLogDebug = FALSE;
|
||
|
CTrkWksConfiguration g_cTrkWksConfiguration;
|
||
|
CWorkManager g_WorkManager;
|
||
|
CTestLog g_cTestLog( &g_cTrkWksConfiguration, &g_WorkManager );
|
||
|
|
||
|
inline void TestRaise( HRESULT hr, TCHAR *ptszMessage, va_list Arguments )
|
||
|
{
|
||
|
CHAR szHR[8];
|
||
|
|
||
|
sprintf( szHR, "%#08X", hr );
|
||
|
|
||
|
if( NULL != ptszMessage )
|
||
|
TrkLogErrorRoutineInternal( TRKDBG_ERROR, szHR, ptszMessage, Arguments );
|
||
|
|
||
|
RaiseException( hr, 0, 0, NULL );
|
||
|
}
|
||
|
|
||
|
inline void TestRaiseException( HRESULT hr, TCHAR *ptszMessage = NULL, ... )
|
||
|
{
|
||
|
va_list Arguments;
|
||
|
va_start( Arguments, ptszMessage );
|
||
|
|
||
|
TestRaise( hr, ptszMessage, Arguments );
|
||
|
}
|
||
|
|
||
|
class CTestLogCallback : public PLogCallback
|
||
|
{
|
||
|
void OnEntriesAvailable();
|
||
|
};
|
||
|
|
||
|
void
|
||
|
CTestLogCallback::OnEntriesAvailable()
|
||
|
{
|
||
|
g_fNotified = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
CTestLog::CTestLog( CTrkWksConfiguration *pTrkWksConfiguration, CWorkManager *pWorkManager )
|
||
|
{
|
||
|
|
||
|
_pTrkWksConfiguration = pTrkWksConfiguration;
|
||
|
_pWorkManager = pWorkManager;
|
||
|
*_tszLogFile = TEXT('\0');
|
||
|
|
||
|
} // CTestLog::CTestLog
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::ReInitialize()
|
||
|
{
|
||
|
_cLogFile.UnInitialize();
|
||
|
DeleteFile( _tszLogFile );
|
||
|
_cLogFile.Initialize( _tszLogFile, _pTrkWksConfiguration, &_cSimpleTimer );
|
||
|
_cLog.Initialize( NULL, _pTrkWksConfiguration, &_cLogFile );
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CTestLog::GenerateLogName()
|
||
|
{
|
||
|
DWORD dw;
|
||
|
TCHAR tszRootPath[ MAX_PATH + 1 ];
|
||
|
DWORD cSectorsPerCluster, cNumberOfFreeClusters, cTotalNumberOfClusters;
|
||
|
|
||
|
if( TEXT('\0') != *_tszLogFile )
|
||
|
return;
|
||
|
|
||
|
// Generate a log file name
|
||
|
|
||
|
if( !GetCurrentDirectory( sizeof(_tszLogFile), _tszLogFile ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get the current directory") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( TEXT('\\') != _tszLogFile[ _tcslen(_tszLogFile) ] )
|
||
|
_tcscat( _tszLogFile, TEXT("\\") );
|
||
|
_tcscat( _tszLogFile, TEXT("TLog.log") );
|
||
|
|
||
|
// Calculate the sector size
|
||
|
|
||
|
TrkAssert( TEXT(':') == _tszLogFile[1] );
|
||
|
|
||
|
_tcsncpy( tszRootPath, _tszLogFile, sizeof("a:\\") - 1 );
|
||
|
tszRootPath[ sizeof("a:\\") - 1 ] = TEXT('\0');
|
||
|
|
||
|
if( !GetDiskFreeSpace( tszRootPath,
|
||
|
&cSectorsPerCluster, &_cbSector,
|
||
|
&cNumberOfFreeClusters, &cTotalNumberOfClusters ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get bytes-per-sector value on %s"), tszRootPath ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
} // CTestLog::GenerateLogName()
|
||
|
|
||
|
|
||
|
const TCHAR*
|
||
|
CTestLog::LogFileName()
|
||
|
{
|
||
|
return( _tszLogFile );
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::DataSectorOffset() const
|
||
|
{
|
||
|
return( _cLogFile._header.NumSectors() * CBSector() );
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CTestLog::CreateLog( PLogCallback *pLogCallback, BOOL fValidate )
|
||
|
{
|
||
|
TCHAR tszLogFile[ MAX_PATH + 1 ];
|
||
|
|
||
|
GenerateLogName();
|
||
|
DeleteFile( _tszLogFile );
|
||
|
|
||
|
_pWorkManager->Initialize();
|
||
|
_cSimpleTimer.Initialize( this, _pWorkManager, 0, 0, NULL );
|
||
|
_cLogFile.Initialize( _tszLogFile, _pTrkWksConfiguration, &_cSimpleTimer );
|
||
|
_cLog.Initialize( pLogCallback, _pTrkWksConfiguration, &_cLogFile );
|
||
|
StartTestWorkerThread(_pWorkManager);
|
||
|
|
||
|
if( fValidate )
|
||
|
ValidateLog();
|
||
|
|
||
|
} // CTestLog::CreateLog()
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::Timer( DWORD dwTimerId )
|
||
|
{
|
||
|
_cLog.Flush( FLUSH_TO_CACHE );
|
||
|
_cLogFile.SetShutdown( TRUE );
|
||
|
_cLogFile.Flush( FLUSH_THROUGH_CACHE );
|
||
|
_cLogFile.OnLogCloseTimer();
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::OpenLog( PLogCallback *pLogCallback, BOOL fValidate )
|
||
|
{
|
||
|
GenerateLogName();
|
||
|
|
||
|
_pWorkManager->Initialize();
|
||
|
_cSimpleTimer.Initialize( this, _pWorkManager, 0, 0, NULL );
|
||
|
_cLogFile.Initialize( _tszLogFile, _pTrkWksConfiguration, &_cSimpleTimer );
|
||
|
_cLog.Initialize( pLogCallback, _pTrkWksConfiguration, &_cLogFile );
|
||
|
StartTestWorkerThread(_pWorkManager);
|
||
|
|
||
|
if( fValidate )
|
||
|
ValidateLog();
|
||
|
|
||
|
} // CTestLog::CreateLog()
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::CloseLog()
|
||
|
{
|
||
|
_pWorkManager->StopWorkerThread();
|
||
|
WaitTestThreadExit();
|
||
|
|
||
|
_cLog.Flush( FLUSH_TO_CACHE );
|
||
|
_cLogFile.SetShutdown( TRUE );
|
||
|
_cLogFile.Flush( FLUSH_THROUGH_CACHE );
|
||
|
|
||
|
_cLogFile.UnInitialize();
|
||
|
_cSimpleTimer.UnInitialize();
|
||
|
_pWorkManager->UnInitialize();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::Append( ULONG cMoves, const TRKSVR_MOVE_NOTIFICATION rgNotifications[] )
|
||
|
{
|
||
|
LogMoveNotification lmn;
|
||
|
SequenceNumber seqOriginal, seqFinal;
|
||
|
|
||
|
if( _cLog.IsEmpty() )
|
||
|
seqOriginal = -1;
|
||
|
else
|
||
|
{
|
||
|
_cLogFile.ReadMoveNotification( _cLog._loginfo.ilogLast, &lmn );
|
||
|
seqOriginal = lmn.seq;
|
||
|
}
|
||
|
|
||
|
g_fNotified = FALSE;
|
||
|
|
||
|
for( ULONG i = 0; i < cMoves; i++ )
|
||
|
{
|
||
|
_cLog.Append( rgNotifications[i].droidCurrent,
|
||
|
rgNotifications[i].droidNew,
|
||
|
rgNotifications[i].droidBirth );
|
||
|
}
|
||
|
|
||
|
_cLogFile.ReadMoveNotification( _cLog._loginfo.ilogLast, &lmn );
|
||
|
seqFinal = lmn.seq;
|
||
|
|
||
|
if( seqFinal != (SequenceNumber)(seqOriginal + cMoves) )
|
||
|
TestRaiseException( E_FAIL, TEXT("Incorrect sequence numbers after Append (%d + %d = %d?)\n"),
|
||
|
seqOriginal, cMoves, seqFinal );
|
||
|
|
||
|
if( !g_fNotified )
|
||
|
TestRaiseException( E_FAIL, TEXT("Didn't receive a notification during an append\n") );
|
||
|
|
||
|
} // CTestLog::Append()
|
||
|
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::Read( ULONG cRead, TRKSVR_MOVE_NOTIFICATION rgNotifications[], SequenceNumber *pseqFirst )
|
||
|
{
|
||
|
_cLog.Read( rgNotifications, pseqFirst, &cRead );
|
||
|
return( cRead );
|
||
|
|
||
|
} // CTestLog::ReadLog()
|
||
|
|
||
|
void
|
||
|
CTestLog::ReadExtendedHeader( ULONG iOffset, void *pv, ULONG cb )
|
||
|
{
|
||
|
_cLogFile.ReadExtendedHeader( iOffset, pv, cb );
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::WriteExtendedHeader( ULONG iOffset, const void *pv, ULONG cb )
|
||
|
{
|
||
|
_cLogFile.WriteExtendedHeader( iOffset, pv, cb );
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CTestLog::ReadAndValidate( ULONG cToRead, ULONG cExpected,
|
||
|
const TRKSVR_MOVE_NOTIFICATION rgNotificationsExpected[],
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsRead[],
|
||
|
SequenceNumber seqExpected )
|
||
|
{
|
||
|
ULONG cLogEntriesRead = 0;
|
||
|
SequenceNumber seq;
|
||
|
|
||
|
memset( rgNotificationsRead, 0, sizeof(*rgNotificationsRead) * cExpected );
|
||
|
|
||
|
cLogEntriesRead = Read( cToRead, rgNotificationsRead, &seq );
|
||
|
|
||
|
if( cLogEntriesRead != cExpected )
|
||
|
{
|
||
|
TestRaiseException( E_FAIL, TEXT("Bad read from log; expected %d entries, got %d\n"),
|
||
|
cExpected, cLogEntriesRead );
|
||
|
}
|
||
|
|
||
|
if( seq != seqExpected
|
||
|
&&
|
||
|
0 != cExpected
|
||
|
)
|
||
|
{
|
||
|
TestRaiseException( E_FAIL, TEXT("Invalid sequence number from log (got %d, expected %d)\n"),
|
||
|
seq, seqExpected );
|
||
|
}
|
||
|
|
||
|
if( 0 != cExpected )
|
||
|
{
|
||
|
|
||
|
for( ULONG i = 0; i < cExpected; i++ )
|
||
|
if( memcmp( &rgNotificationsExpected[i], &rgNotificationsRead[i], sizeof(rgNotificationsRead[i]) ))
|
||
|
{
|
||
|
TestRaiseException( E_FAIL, TEXT("Log entries read don't match that which was expected\n") );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // CTestLog::ReadAndValidate()
|
||
|
|
||
|
|
||
|
SequenceNumber
|
||
|
CTestLog::GetNextSeqNumber( )
|
||
|
{
|
||
|
return( _cLog.GetNextSeqNumber() );
|
||
|
|
||
|
} // CTestLog::GetLatestSeqNumber()
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
CTestLog::Search( const CDomainRelativeObjId &droid, TRKSVR_MOVE_NOTIFICATION *pNotification )
|
||
|
{
|
||
|
pNotification->droidCurrent = droid;
|
||
|
|
||
|
return( _cLog.Search( pNotification->droidCurrent,
|
||
|
&pNotification->droidNew,
|
||
|
&pNotification->droidBirth ) );
|
||
|
|
||
|
|
||
|
} // CTestLog::Search()
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::Seek( SequenceNumber seq )
|
||
|
{
|
||
|
|
||
|
SequenceNumber seqOriginal;
|
||
|
LogIndex ilogOriginal;
|
||
|
LogMoveNotification lmn;
|
||
|
|
||
|
ilogOriginal = _cLog._loginfo.ilogRead;
|
||
|
if( ilogOriginal != _cLog._loginfo.ilogWrite )
|
||
|
{
|
||
|
_cLogFile.ReadMoveNotification( _cLog._loginfo.ilogRead, &lmn );
|
||
|
seqOriginal = lmn.seq;
|
||
|
}
|
||
|
|
||
|
g_fNotified = FALSE;
|
||
|
_cLog.Seek( seq );
|
||
|
|
||
|
if( seq != _cLog._loginfo.seqNext )
|
||
|
{
|
||
|
_cLogFile.ReadMoveNotification( _cLog._loginfo.ilogRead, &lmn );
|
||
|
|
||
|
if( ilogOriginal == _cLog._loginfo.ilogWrite
|
||
|
||
|
||
|
seqOriginal > lmn.seq
|
||
|
)
|
||
|
{
|
||
|
|
||
|
if( !g_fNotified && !_cLog.IsEmpty() )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Didn't receive a notification after a backwards seek") ));
|
||
|
TrkRaiseException( E_FAIL );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} // CTestLog::Seek( SequenceNumber ... )
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::Seek( int origin, int iSeek )
|
||
|
{
|
||
|
SequenceNumber seqOriginal;
|
||
|
LogMoveNotification lmn;
|
||
|
|
||
|
_cLogFile.ReadMoveNotification( _cLog._loginfo.ilogRead, &lmn );
|
||
|
|
||
|
seqOriginal = _cLog._loginfo.ilogRead == _cLog._loginfo.ilogWrite
|
||
|
? _cLog._loginfo.seqNext
|
||
|
: lmn.seq;
|
||
|
|
||
|
g_fNotified = FALSE;
|
||
|
_cLog.Seek( origin, iSeek );
|
||
|
|
||
|
_cLogFile.ReadMoveNotification( _cLog._loginfo.ilogRead, &lmn );
|
||
|
|
||
|
if( !_cLog.IsRead()
|
||
|
&&
|
||
|
seqOriginal > lmn.seq
|
||
|
)
|
||
|
{
|
||
|
if( !g_fNotified && !_cLog.IsEmpty() )
|
||
|
TestRaiseException( E_FAIL, TEXT("Didn't receive a notification after a backwards seek") );
|
||
|
}
|
||
|
|
||
|
} // CTestLog::Seek( origin ... )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::ValidateLog()
|
||
|
{
|
||
|
ULONG cEntries = _cLogFile.NumEntriesInFile();
|
||
|
LogIndex ilogEntry, i, j;
|
||
|
ULONG *rgiNext = NULL;
|
||
|
ULONG *rgiPrev = NULL;
|
||
|
|
||
|
__try
|
||
|
{
|
||
|
rgiNext = (ULONG*) new ULONG[ cEntries ];
|
||
|
rgiPrev = (ULONG*) new ULONG[ cEntries ];
|
||
|
|
||
|
for( ilogEntry = 0; ilogEntry < cEntries; ilogEntry++ )
|
||
|
{
|
||
|
rgiNext[ ilogEntry ] = _cLogFile._sector.GetLogEntry( ilogEntry )->ilogNext;
|
||
|
rgiPrev[ ilogEntry ] = _cLogFile._sector.GetLogEntry( ilogEntry )->ilogPrevious;
|
||
|
}
|
||
|
|
||
|
|
||
|
for( i = 0; i < cEntries; i++ )
|
||
|
{
|
||
|
// Validate that the entry pointed to by i->next, points
|
||
|
// back to i with its prev pointer.
|
||
|
|
||
|
if( rgiPrev[ rgiNext[i] ] != i )
|
||
|
TestRaiseException( E_FAIL, TEXT("Two entries don't point to each other: %d, %d, %d\n"),
|
||
|
i, rgiNext[i], rgiPrev[ rgiNext[i] ] );
|
||
|
|
||
|
// Verify that noone else's next/prev pointers point to
|
||
|
// i's next/prev pointers.
|
||
|
|
||
|
for( j = i+1; j < cEntries; j++ )
|
||
|
{
|
||
|
if( rgiNext[i] == rgiNext[j] )
|
||
|
TestRaiseException( E_FAIL, TEXT("Two entries in the log have the same next pointer: %d and %d (point to %d)\n"),
|
||
|
i, j, rgiNext[i] );
|
||
|
|
||
|
if( rgiPrev[i] == rgiPrev[j] )
|
||
|
TestRaiseException( E_FAIL, TEXT("Two entries in the log have the same prev pointer: %d and %d (point to %d)\n"),
|
||
|
i, j, rgiPrev[i] );
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
__finally
|
||
|
{
|
||
|
delete[] rgiNext;
|
||
|
delete[] rgiPrev;
|
||
|
}
|
||
|
|
||
|
|
||
|
} // CTestLog::ValidateLog()
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::GetCbLog()
|
||
|
{
|
||
|
return( _cLogFile._cbLogFile );
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CTestLog::DelayUntilClose()
|
||
|
{
|
||
|
_tprintf( TEXT(" Sleeping so that the log auto-closes\n") );
|
||
|
Sleep( 1500 * _pTrkWksConfiguration->GetLogFileOpenTime() );
|
||
|
|
||
|
if( _cLogFile.IsOpen() )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("After delaying, log file did not close") ));
|
||
|
TrkRaiseException( E_FAIL );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
CTestLog::MakeEntryOld()
|
||
|
{
|
||
|
_cLogFile._sector.GetLogEntry( _cLog._loginfo.ilogStart )->move.DateWritten
|
||
|
-= _pTrkWksConfiguration->_dwLogOverwriteAge + 1;
|
||
|
_cLogFile.Flush( FLUSH_UNCONDITIONALLY );
|
||
|
|
||
|
} // CTestLog::MakeStartOld()
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::GetNumEntries()
|
||
|
{
|
||
|
return( _cLogFile.NumEntriesInFile() );
|
||
|
|
||
|
} // CTestLog::GetNumEntries()
|
||
|
|
||
|
|
||
|
LogIndex
|
||
|
CTestLog::GetStartIndex()
|
||
|
{
|
||
|
return( _cLog._loginfo.ilogStart );
|
||
|
}
|
||
|
|
||
|
LogIndex
|
||
|
CTestLog::GetEndIndex()
|
||
|
{
|
||
|
return( _cLog._loginfo.ilogEnd );
|
||
|
}
|
||
|
|
||
|
LogIndex
|
||
|
CTestLog::GetReadIndex()
|
||
|
{
|
||
|
return( _cLog._loginfo.ilogRead );
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CTestLog::SetReadIndex( LogIndex ilogRead )
|
||
|
{
|
||
|
_cLog._loginfo.ilogRead = ilogRead;
|
||
|
_cLog.RefreshHeader();
|
||
|
_cLogFile.Flush();
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
CTestLog::IsEmpty()
|
||
|
{
|
||
|
BOOL fReturn;
|
||
|
fReturn = _cLog.IsEmpty();
|
||
|
return( fReturn );
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::NumEntriesInFile( )
|
||
|
{
|
||
|
ULONG cSectors = _cLogFile._cbLogFile / _cbSector - NUM_HEADER_SECTORS;
|
||
|
ULONG cEntriesPerSector = (_cbSector-sizeof(LogEntryHeader)) / sizeof(LogEntry);
|
||
|
|
||
|
return( cSectors * cEntriesPerSector );
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::NumEntriesPerSector()
|
||
|
{
|
||
|
return( ( _cbSector-sizeof(LogEntryHeader) ) / sizeof(LogEntry) );
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::NumEntriesPerKB()
|
||
|
{
|
||
|
return( (1024 / _cbSector) * NumEntriesPerSector() );
|
||
|
}
|
||
|
|
||
|
ULONG
|
||
|
CTestLog::CBSector() const
|
||
|
{
|
||
|
return( _cbSector );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void ReadTest( ULONG cEntries,
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsExpected[],
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsRead[],
|
||
|
SequenceNumber seqExpected )
|
||
|
{
|
||
|
g_cTestLog.ReadAndValidate( cEntries + 1, cEntries,
|
||
|
rgNotificationsExpected, rgNotificationsRead,
|
||
|
seqExpected );
|
||
|
|
||
|
g_cTestLog.ReadAndValidate( cEntries, cEntries,
|
||
|
rgNotificationsExpected, rgNotificationsRead,
|
||
|
seqExpected );
|
||
|
|
||
|
if( cEntries > 1 )
|
||
|
{
|
||
|
g_cTestLog.ReadAndValidate( cEntries - 1, cEntries - 1,
|
||
|
rgNotificationsExpected, rgNotificationsRead,
|
||
|
seqExpected );
|
||
|
|
||
|
|
||
|
g_cTestLog.ReadAndValidate( 1, 1,
|
||
|
rgNotificationsExpected, rgNotificationsRead,
|
||
|
seqExpected );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
ExerciseLog( ULONG cEntries, SequenceNumber seqFirst,
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsExpected[],
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsRead[] )
|
||
|
{
|
||
|
SequenceNumber seqExpected = seqFirst;
|
||
|
SequenceNumber seqRead;
|
||
|
ULONG cRead;
|
||
|
|
||
|
ULONG iReadOriginal = g_cTestLog.GetReadIndex();
|
||
|
|
||
|
ReadTest( cEntries, rgNotificationsExpected, rgNotificationsRead, seqExpected );
|
||
|
|
||
|
if( 0 != cEntries )
|
||
|
{
|
||
|
// Skip forward by one entry
|
||
|
|
||
|
g_cTestLog.Seek( SEEK_CUR, 1 );
|
||
|
seqExpected++;
|
||
|
|
||
|
ReadTest( cEntries-1, &rgNotificationsExpected[1], &rgNotificationsRead[1], seqExpected );
|
||
|
|
||
|
if( cEntries > 1 )
|
||
|
{
|
||
|
// Skip forward by one entry, but using an absolute seek.
|
||
|
|
||
|
g_cTestLog.Seek( SEEK_SET, 2 );
|
||
|
seqExpected++;
|
||
|
|
||
|
ReadTest( cEntries-2, &rgNotificationsExpected[2], &rgNotificationsRead[2], seqExpected );
|
||
|
|
||
|
// Do a relative seek back in the log.
|
||
|
|
||
|
g_cTestLog.Seek( SEEK_CUR, -1 );
|
||
|
seqExpected--;
|
||
|
|
||
|
ReadTest( cEntries-1, &rgNotificationsExpected[1], &rgNotificationsRead[1], seqExpected );
|
||
|
|
||
|
}
|
||
|
|
||
|
// Do a relative seek back to the beginning of the log
|
||
|
|
||
|
g_cTestLog.Seek( SEEK_CUR, -1000 );
|
||
|
seqExpected--;
|
||
|
|
||
|
ReadTest( cEntries, &rgNotificationsExpected[0], &rgNotificationsRead[0], seqExpected );
|
||
|
|
||
|
// Skip forward by the remaining entries
|
||
|
|
||
|
g_cTestLog.Seek( SEEK_CUR, cEntries );
|
||
|
seqExpected += cEntries;
|
||
|
|
||
|
cRead = g_cTestLog.Read( 1, rgNotificationsRead, &seqRead );
|
||
|
if( 0 != cRead )
|
||
|
TestRaiseException( E_FAIL, TEXT("Shouldn't have been able to read an already-read log\n") );
|
||
|
|
||
|
// Seek to the end (which is where the read index already is), to ensure
|
||
|
// that nothing happens.
|
||
|
|
||
|
g_fNotified = FALSE;
|
||
|
g_cTestLog.Seek( seqFirst + cEntries );
|
||
|
|
||
|
if( g_fNotified )
|
||
|
TestRaiseException( E_FAIL, TEXT("A seek-to-current shouldn't have caused a notification") );
|
||
|
|
||
|
cRead = g_cTestLog.Read( 1, rgNotificationsRead, &seqRead );
|
||
|
if( 0 != cRead )
|
||
|
TestRaiseException( E_FAIL, TEXT("Shouldn't have been able to read an already-read log\n") );
|
||
|
|
||
|
// Over-seek to the end.
|
||
|
|
||
|
g_fNotified = FALSE;
|
||
|
g_cTestLog.Seek( SEEK_CUR, 1000 );
|
||
|
|
||
|
if( g_fNotified )
|
||
|
TestRaiseException( E_FAIL, TEXT("A seek-to-current shouldn't have caused a notification") );
|
||
|
|
||
|
cRead = g_cTestLog.Read( 1, rgNotificationsRead, &seqRead );
|
||
|
if( 0 != cRead )
|
||
|
TestRaiseException( E_FAIL, TEXT("Shouldn't have been able to read an already-read log\n") );
|
||
|
|
||
|
}
|
||
|
|
||
|
// Seek to the start of the log
|
||
|
|
||
|
g_cTestLog.Seek( seqFirst );
|
||
|
seqExpected = seqFirst;
|
||
|
ReadTest( cEntries, rgNotificationsExpected, rgNotificationsRead, seqExpected );
|
||
|
|
||
|
if( 0 != cEntries )
|
||
|
{
|
||
|
// Seek to the end of the log
|
||
|
|
||
|
seqExpected = seqFirst + (ULONG)(cEntries - 1);
|
||
|
g_cTestLog.Seek( seqExpected );
|
||
|
ReadTest( 1, &rgNotificationsExpected[cEntries-1], &rgNotificationsRead[cEntries-1], seqExpected );
|
||
|
|
||
|
g_cTestLog.Seek( SEEK_CUR, 1 );
|
||
|
}
|
||
|
|
||
|
// Search for each of the log entries
|
||
|
|
||
|
for( ULONG i = 0; i < cEntries; i++ )
|
||
|
{
|
||
|
if( !g_cTestLog.Search( rgNotificationsExpected[i].droidCurrent,
|
||
|
rgNotificationsRead ))
|
||
|
{
|
||
|
TestRaiseException( E_FAIL, TEXT("Search failed to find entry") );
|
||
|
}
|
||
|
|
||
|
if( memcmp( &rgNotificationsExpected[i], rgNotificationsRead, sizeof(*rgNotificationsRead) ))
|
||
|
TestRaiseException( E_FAIL, TEXT("Search failed on entry %d"), i );
|
||
|
}
|
||
|
|
||
|
g_cTestLog.SetReadIndex( iReadOriginal );
|
||
|
|
||
|
} // ExerciseLog()
|
||
|
|
||
|
|
||
|
void
|
||
|
FillAndExerciseLog( ULONG cEntriesOriginal, ULONG cEntriesTotal, SequenceNumber seqFirst,
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsWrite[],
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsRead[] )
|
||
|
{
|
||
|
// Test the log as-is
|
||
|
|
||
|
ExerciseLog( cEntriesOriginal, seqFirst, rgNotificationsWrite, rgNotificationsRead );
|
||
|
|
||
|
// Add an entry to the log and re-test
|
||
|
|
||
|
g_cTestLog.Append( 1, &rgNotificationsWrite[ cEntriesOriginal ] );
|
||
|
ExerciseLog( cEntriesOriginal + 1, seqFirst,
|
||
|
rgNotificationsWrite, rgNotificationsRead );
|
||
|
|
||
|
// Test a full log
|
||
|
|
||
|
g_cTestLog.Append( cEntriesTotal - cEntriesOriginal - 2,
|
||
|
&rgNotificationsWrite[ cEntriesOriginal + 1] );
|
||
|
ExerciseLog( cEntriesTotal - 1, seqFirst,
|
||
|
rgNotificationsWrite, rgNotificationsRead );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG
|
||
|
LogIndex2SectorIndex( ULONG cbSector, LogIndex ilog )
|
||
|
{
|
||
|
ULONG cEntriesPerSector = ( cbSector - sizeof(LogEntryHeader) ) / sizeof(LogEntry);
|
||
|
return( ilog / cEntriesPerSector + NUM_HEADER_SECTORS );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void
|
||
|
ReadLogSector( HANDLE hFile, LogIndex ilog, ULONG cbSector, BYTE rgbSector[] )
|
||
|
{
|
||
|
ULONG iSector = LogIndex2SectorIndex( cbSector, ilog );
|
||
|
ULONG cb;
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer(hFile, iSector * cbSector, NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't seek file to %lu (in test)"), iSector*cbSector ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !ReadFile( hFile, rgbSector, cbSector, &cb, NULL )
|
||
|
||
|
||
|
cbSector != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't read from logfile (in test), cbRead = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
WriteLogSector( HANDLE hFile, LogIndex ilog, ULONG cbSector, BYTE rgbSector[] )
|
||
|
{
|
||
|
ULONG iSector = LogIndex2SectorIndex( cbSector, ilog );
|
||
|
ULONG cb;
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer(hFile, iSector * cbSector, NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't seek file to %lu (in test)"), iSector*cbSector ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !WriteFile( hFile, rgbSector, cbSector, &cb, NULL )
|
||
|
||
|
||
|
cbSector != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't write to logfile (in test), cbWritten = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
CreateNewLog( CTestLogCallback *pcTestLogCallback, ULONG *pcLogEntries, ULONG *piNotifications,
|
||
|
SequenceNumber *pseqFirst)
|
||
|
{
|
||
|
|
||
|
_tprintf( TEXT(" Creating a log") );
|
||
|
|
||
|
g_cTrkWksConfiguration._dwMinLogKB = 1;
|
||
|
g_cTrkWksConfiguration._dwMaxLogKB = 1;
|
||
|
|
||
|
g_cTestLog.CreateLog( pcTestLogCallback );
|
||
|
|
||
|
*pcLogEntries = g_cTestLog.NumEntriesInFile();
|
||
|
*piNotifications = 0;
|
||
|
*pseqFirst = 0;
|
||
|
|
||
|
if( 0 != g_cTestLog.GetNextSeqNumber( ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Next sequence number should be zero after a create") ));
|
||
|
TrkRaiseException( E_FAIL );
|
||
|
}
|
||
|
|
||
|
_tprintf( TEXT(" (%d entries)\n"), *pcLogEntries );
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
EXTERN_C void __cdecl _tmain( int argc, TCHAR *argv[] )
|
||
|
{
|
||
|
CTestLogCallback cTestLogCallback;
|
||
|
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsWritten[ 50 ];
|
||
|
TRKSVR_MOVE_NOTIFICATION rgNotificationsRead[ 50 ];
|
||
|
TRKSVR_MOVE_NOTIFICATION tempNotificationWrite, tempNotificationRead;
|
||
|
|
||
|
__try
|
||
|
{
|
||
|
|
||
|
ULONG cLogEntries = 0;
|
||
|
ULONG i;
|
||
|
DWORD dw;
|
||
|
ULONG cRead, cb, cbFile;
|
||
|
SequenceNumber seqFirst = 0;
|
||
|
BOOL fAppendFailed;
|
||
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
|
BYTE rgbSector[ 2048 ];
|
||
|
LogHeader *plogheader = NULL;
|
||
|
LogEntry *plogentry = NULL;
|
||
|
ULONG iNotifications = 0;
|
||
|
|
||
|
BYTE rgbExtendedHeaderWrite[ 16 ];
|
||
|
BYTE rgbExtendedHeaderRead[ 16 ];
|
||
|
|
||
|
LogIndex ilogStart, ilogEnd;
|
||
|
|
||
|
|
||
|
// --------------
|
||
|
// Initialization
|
||
|
// --------------
|
||
|
|
||
|
_tprintf( TEXT("\nCLog Unit Test\n") );
|
||
|
_tprintf( TEXT( "==============\n\n") );
|
||
|
|
||
|
if( argc > 1 )
|
||
|
{
|
||
|
if( !_tcscmp( TEXT("/D"), argv[1] )
|
||
|
||
|
||
|
!_tcscmp( TEXT("/d"), argv[1] )
|
||
|
||
|
||
|
!_tcscmp( TEXT("-D"), argv[1] )
|
||
|
||
|
||
|
!_tcscmp( TEXT("-d"), argv[1] )
|
||
|
)
|
||
|
{
|
||
|
g_fTLogDebug = TRUE;
|
||
|
}
|
||
|
} // if( argc > 1 )
|
||
|
|
||
|
|
||
|
TrkDebugCreate( TRK_DBG_FLAGS_WRITE_TO_DBG | (g_fTLogDebug ? TRK_DBG_FLAGS_WRITE_TO_STDOUT : 0), "TLog" );
|
||
|
|
||
|
g_cTrkWksConfiguration.Initialize();
|
||
|
|
||
|
g_cTrkWksConfiguration._dwMinLogKB = 1;
|
||
|
g_cTrkWksConfiguration._dwMaxLogKB = 1;
|
||
|
g_cTrkWksConfiguration._dwLogDeltaKB = 1;
|
||
|
g_cTrkWksConfiguration._dwLogOverwriteAge = 10;
|
||
|
g_cTrkWksConfiguration._dwLogFileOpenTime = 10;
|
||
|
g_cTrkWksConfiguration._dwDebugFlags = (0xFFFFFFFF & ~TRKDBG_WORKMAN);
|
||
|
|
||
|
g_Debug = g_cTrkWksConfiguration._dwDebugFlags;
|
||
|
|
||
|
for( i = 0; i < sizeof(rgNotificationsWritten); i++ )
|
||
|
((BYTE*) rgNotificationsWritten)[ i ] = (BYTE) i;
|
||
|
|
||
|
|
||
|
|
||
|
// -----------
|
||
|
// Basic Tests
|
||
|
// -----------
|
||
|
|
||
|
_tprintf( TEXT("Basic exercises\n") );
|
||
|
|
||
|
CreateNewLog( &cTestLogCallback, &cLogEntries, &iNotifications, &seqFirst );
|
||
|
|
||
|
// Test the initial log
|
||
|
|
||
|
FillAndExerciseLog( 0, cLogEntries, seqFirst, rgNotificationsWritten, rgNotificationsRead );
|
||
|
|
||
|
// Seek to a non-existent entry in the log
|
||
|
|
||
|
g_cTestLog.Seek( 1 );
|
||
|
g_cTestLog.Seek( -1 );
|
||
|
ExerciseLog( cLogEntries-1, 0,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
// Cause the log to expand. Note that in this case, the start/end indices are
|
||
|
// currently at the start/end of the file.
|
||
|
|
||
|
_tprintf( TEXT("Cause the log to expand") );
|
||
|
|
||
|
g_cTrkWksConfiguration._dwLogDeltaKB = 1;
|
||
|
g_cTrkWksConfiguration._dwMaxLogKB = g_cTrkWksConfiguration._dwMaxLogKB + 1;
|
||
|
|
||
|
g_cTestLog.Append( 1, &rgNotificationsWritten[ cLogEntries - 1] );
|
||
|
|
||
|
_tprintf( TEXT(" (%d entries)\n"), g_cTestLog.NumEntriesInFile() );
|
||
|
|
||
|
g_cTestLog.DelayUntilClose();
|
||
|
|
||
|
FillAndExerciseLog( cLogEntries,
|
||
|
g_cTestLog.NumEntriesInFile(),
|
||
|
seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
cLogEntries = g_cTestLog.NumEntriesInFile();
|
||
|
|
||
|
// Close and re-open the log
|
||
|
|
||
|
_tprintf( TEXT("Close and re-open the log\n") );
|
||
|
|
||
|
g_cTestLog.CloseLog();
|
||
|
g_cTestLog.OpenLog( &cTestLogCallback );
|
||
|
|
||
|
ExerciseLog( cLogEntries - 1, seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
// Ensure that we can't add to a full log (where the log can't be expanded,
|
||
|
// the start entry isn't old enough to throw away, and the start entry
|
||
|
// hasn't yet been read).
|
||
|
|
||
|
__try
|
||
|
{
|
||
|
fAppendFailed = FALSE;
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("TLog Unit Test: Causing an intentional Append exception") ));
|
||
|
g_cTestLog.Seek( SEEK_SET, 0 ); // Make the start entry un-read
|
||
|
g_cTestLog.Append( 1, rgNotificationsWritten );
|
||
|
}
|
||
|
__except( BreakOnAccessViolation() )
|
||
|
{
|
||
|
if( GetExceptionCode() != STATUS_LOG_FILE_FULL )
|
||
|
TestRaiseException( GetExceptionCode(), TEXT("Wrong exception raised when attempting to write to a full log") );
|
||
|
fAppendFailed = TRUE;
|
||
|
}
|
||
|
|
||
|
if( !fAppendFailed )
|
||
|
TestRaiseException( E_FAIL, TEXT("Append to a full log should have failed") );
|
||
|
|
||
|
// Overwrite an entry in the log that's overwritable since it's been read already.
|
||
|
|
||
|
_tprintf( TEXT("Try to add to a max log (the start entry's been read)\n") );
|
||
|
|
||
|
g_cTestLog.Seek( SEEK_SET, 1 );
|
||
|
g_cTestLog.Append( 1, &rgNotificationsWritten[ cLogEntries - 1 ] );
|
||
|
|
||
|
seqFirst++;
|
||
|
iNotifications++;
|
||
|
|
||
|
ExerciseLog( cLogEntries-1, seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
g_cTestLog.ValidateLog();
|
||
|
|
||
|
// Overwrite an old entry in the log
|
||
|
|
||
|
_tprintf( TEXT("Try to add to a max log (the start entry's old)\n") );
|
||
|
g_cTestLog.Seek( SEEK_SET, 0 );
|
||
|
g_cTestLog.MakeEntryOld();
|
||
|
g_cTestLog.Append( 1, &rgNotificationsWritten[cLogEntries] );
|
||
|
|
||
|
seqFirst++;
|
||
|
iNotifications++;
|
||
|
|
||
|
ExerciseLog( cLogEntries-1, seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
g_cTestLog.ValidateLog();
|
||
|
|
||
|
// Grow again (note that this time, the start/end indices are in the middle of
|
||
|
// the file). Also, this time, we show that the log can grow to up to the max
|
||
|
// size, even if it means we can't grow an entire delta.
|
||
|
|
||
|
_tprintf( TEXT("Cause the log to expand again") );
|
||
|
|
||
|
g_cTrkWksConfiguration._dwLogDeltaKB = 10;
|
||
|
g_cTrkWksConfiguration._dwMaxLogKB = g_cTrkWksConfiguration._dwMaxLogKB + 1;
|
||
|
|
||
|
g_cTestLog.Append( 1, &rgNotificationsWritten[ cLogEntries+1 ] );
|
||
|
|
||
|
if( g_cTestLog.NumEntriesInFile()
|
||
|
>
|
||
|
cLogEntries + g_cTestLog.NumEntriesPerKB()
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Log grew by more than the max allowable") ));
|
||
|
TrkRaiseWin32Error( E_FAIL );
|
||
|
}
|
||
|
|
||
|
_tprintf( TEXT(" (%d entries)\n"), g_cTestLog.NumEntriesInFile() );
|
||
|
|
||
|
FillAndExerciseLog( cLogEntries, g_cTestLog.NumEntriesInFile(),
|
||
|
seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
cLogEntries = g_cTestLog.NumEntriesInFile();
|
||
|
|
||
|
g_cTestLog.ValidateLog();
|
||
|
|
||
|
|
||
|
// --------------
|
||
|
// Extended Tests
|
||
|
// --------------
|
||
|
|
||
|
// Test the extended header
|
||
|
|
||
|
_tprintf( TEXT("Extended header area\n") );
|
||
|
|
||
|
for( i = 0; i < sizeof(rgbExtendedHeaderWrite); i++ )
|
||
|
rgbExtendedHeaderWrite[ i ] = (BYTE)i;
|
||
|
|
||
|
g_cTestLog.WriteExtendedHeader( 32, (void*) rgbExtendedHeaderWrite, sizeof(rgbExtendedHeaderWrite) );
|
||
|
g_cTestLog.ReadExtendedHeader( 32, (void*) rgbExtendedHeaderRead, sizeof(rgbExtendedHeaderRead) );
|
||
|
|
||
|
for( i = 0; i < sizeof(rgbExtendedHeaderWrite); i++ )
|
||
|
rgbExtendedHeaderWrite[ i ] = (BYTE)(i + 1);
|
||
|
|
||
|
g_cTestLog.WriteExtendedHeader( 32, (void*) rgbExtendedHeaderWrite, sizeof(rgbExtendedHeaderWrite) );
|
||
|
g_cTestLog.DelayUntilClose();
|
||
|
g_cTestLog.ReadExtendedHeader( 32, (void*) rgbExtendedHeaderRead, sizeof(rgbExtendedHeaderRead) );
|
||
|
|
||
|
|
||
|
if( memcmp( rgbExtendedHeaderWrite, rgbExtendedHeaderRead, sizeof(rgbExtendedHeaderWrite) ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Extended header information couldn't be written/read") ));
|
||
|
TrkRaiseWin32Error( E_FAIL );
|
||
|
}
|
||
|
|
||
|
// Make the log look abnormally shutdown, then open it.
|
||
|
|
||
|
_tprintf( TEXT("Make log look abnormally shut down\n") );
|
||
|
|
||
|
g_cTestLog.CloseLog();
|
||
|
|
||
|
hFile = CreateFile( g_cTestLog.LogFileName(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
|
||
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||
|
if( INVALID_HANDLE_VALUE == hFile )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open the logfile from the test") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !ReadFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't read from logfile (in test), cbRead = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
plogheader = (LogHeader*) rgbSector;
|
||
|
plogheader->fProperShutdown = FALSE;
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't seek logfile (in test)") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !WriteFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't write to logfile (in test), cbWritten = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
CloseHandle( hFile );
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
plogheader = NULL;
|
||
|
|
||
|
g_cTestLog.OpenLog( &cTestLogCallback );
|
||
|
ExerciseLog( cLogEntries - 1, seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
|
||
|
// Make the log look like it crashed during an expansion.
|
||
|
|
||
|
_tprintf( TEXT("Expansion crash recovery\n") );
|
||
|
|
||
|
ilogStart = g_cTestLog.GetStartIndex();
|
||
|
ilogEnd = g_cTestLog.GetEndIndex();
|
||
|
|
||
|
g_cTestLog.CloseLog();
|
||
|
|
||
|
hFile = CreateFile( g_cTestLog.LogFileName(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
|
||
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||
|
if( INVALID_HANDLE_VALUE == hFile )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open the logfile from the test") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
cbFile = GetFileSize( hFile, NULL );
|
||
|
if( 0xffffffff == cbFile )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't get log file size (in test)") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !ReadFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't read from logfile (in test), cbRead = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
plogheader = (LogHeader*) rgbSector;
|
||
|
|
||
|
plogheader->fProperShutdown = FALSE;
|
||
|
plogheader->expand.cbFile = cbFile;
|
||
|
plogheader->expand.ilogStart = ilogStart;
|
||
|
plogheader->expand.ilogEnd = ilogEnd;
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't seek logfile (in test)") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !WriteFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't write to logfile (in test), cbWritten = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
ReadLogSector( hFile, ilogStart, g_cTestLog.CBSector(), rgbSector );
|
||
|
plogentry = &( (LogEntry*)rgbSector )[ ilogStart % (g_cTestLog.CBSector()/sizeof(LogEntry)) ];
|
||
|
plogentry->ilogPrevious = -1;
|
||
|
WriteLogSector( hFile, ilogStart, g_cTestLog.CBSector(), rgbSector );
|
||
|
|
||
|
ReadLogSector( hFile, ilogEnd, g_cTestLog.CBSector(), rgbSector );
|
||
|
plogentry = &( (LogEntry*)rgbSector )[ ilogEnd % (g_cTestLog.CBSector()/sizeof(LogEntry)) ];
|
||
|
plogentry->ilogNext = -1;
|
||
|
WriteLogSector( hFile, ilogEnd, g_cTestLog.CBSector(), rgbSector );
|
||
|
|
||
|
|
||
|
cbFile += g_cTestLog.CBSector();
|
||
|
if ( 0xFFFFFFFF == SetFilePointer(hFile, cbFile, NULL, FILE_BEGIN)
|
||
|
||
|
||
|
!SetEndOfFile(hFile) )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't reset log file size to %lu (in test)"), cbFile ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
CloseHandle( hFile );
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
plogheader = NULL;
|
||
|
|
||
|
g_cTestLog.OpenLog( &cTestLogCallback );
|
||
|
ExerciseLog( cLogEntries - 1, seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
|
||
|
// Corrupt the log header, but in a way that is recoverable.
|
||
|
|
||
|
_tprintf( TEXT("Make log look corrupted (recoverable)\n") );
|
||
|
|
||
|
g_cTestLog.CloseLog();
|
||
|
|
||
|
hFile = CreateFile( g_cTestLog.LogFileName(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
|
||
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||
|
if( INVALID_HANDLE_VALUE == hFile )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open the logfile from the test") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !ReadFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't read from logfile (in test), cbRead = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
plogheader = (LogHeader*) rgbSector;
|
||
|
plogheader->fProperShutdown = FALSE;
|
||
|
|
||
|
memset( &reinterpret_cast<BYTE*>(plogheader)[CLOG_LOGINFO_START], 0, CLOG_LOGINFO_LENGTH );
|
||
|
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't seek logfile (in test)") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !WriteFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't write to logfile (in test), cbWritten = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
CloseHandle( hFile );
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
plogheader = NULL;
|
||
|
|
||
|
g_cTestLog.OpenLog( &cTestLogCallback );
|
||
|
if( g_cTestLog.IsEmpty() )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("We got a new log file after what should have been a recoverable corruption") ));
|
||
|
TrkRaiseWin32Error( E_FAIL );
|
||
|
}
|
||
|
|
||
|
ExerciseLog( cLogEntries - 1, seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
|
||
|
// Make the log look corrupted and un-recoverable in the header
|
||
|
|
||
|
_tprintf( TEXT("Make log header look corrupted (un-recoverable)\n") );
|
||
|
|
||
|
g_cTestLog.CloseLog();
|
||
|
|
||
|
hFile = CreateFile( g_cTestLog.LogFileName(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
|
||
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||
|
if( INVALID_HANDLE_VALUE == hFile )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open the logfile from the test") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !ReadFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't read from logfile (in test), cbRead = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
plogheader = (LogHeader*) rgbSector;
|
||
|
plogheader->fProperShutdown = FALSE;
|
||
|
plogheader->ulSignature = 0;
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer( hFile, 0, NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't seek logfile (in test)") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !WriteFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't write to logfile (in test), cbWritten = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
CloseHandle( hFile );
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
plogheader = NULL;
|
||
|
|
||
|
g_cTestLog.OpenLog( &cTestLogCallback );
|
||
|
if( !g_cTestLog.IsEmpty() )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR,TEXT("After opening a corrupt log, we should have a new log file") ));
|
||
|
TrkRaiseWin32Error( E_FAIL );
|
||
|
}
|
||
|
|
||
|
|
||
|
// Make the log look corrupted and un-recoverable in the sectors
|
||
|
|
||
|
_tprintf( TEXT("Make log sectors look corrupted (un-recoverable)\n") );
|
||
|
|
||
|
g_cTestLog.CloseLog();
|
||
|
|
||
|
CreateNewLog( &cTestLogCallback, &cLogEntries, &iNotifications, &seqFirst );
|
||
|
FillAndExerciseLog( 0, cLogEntries, seqFirst, rgNotificationsWritten, rgNotificationsRead );
|
||
|
g_cTestLog.CloseLog();
|
||
|
|
||
|
hFile = CreateFile( g_cTestLog.LogFileName(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ,
|
||
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
||
|
if( INVALID_HANDLE_VALUE == hFile )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't open the logfile from the test") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer( hFile,
|
||
|
g_cTestLog.DataSectorOffset(),
|
||
|
NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't seek logfile to start of first sector (in test)") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !ReadFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't read from logfile (in test), cbRead = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
memset( rgbSector, 0, sizeof(rgbSector) );
|
||
|
|
||
|
if( 0xFFFFFFFF == SetFilePointer( hFile,
|
||
|
g_cTestLog.DataSectorOffset(),
|
||
|
NULL, FILE_BEGIN ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't re-seek logfile to start of first sector (in test)") ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
if( !WriteFile( hFile, rgbSector, g_cTestLog.CBSector(), &cb, NULL )
|
||
|
||
|
||
|
g_cTestLog.CBSector() != cb
|
||
|
)
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't write to logfile (in test), cbWritten = %d"), cb ));
|
||
|
TrkRaiseLastError( );
|
||
|
}
|
||
|
|
||
|
CloseHandle( hFile );
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
BOOL fExceptionRaised = FALSE;
|
||
|
__try
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("About to open a corrupted log (this should raise)") ));
|
||
|
|
||
|
// The open should succeed
|
||
|
g_cTestLog.OpenLog( &cTestLogCallback,
|
||
|
FALSE // => Don't validate
|
||
|
);
|
||
|
|
||
|
// This should raise
|
||
|
ExerciseLog( cLogEntries - 1, seqFirst,
|
||
|
&rgNotificationsWritten[ iNotifications ],
|
||
|
&rgNotificationsRead[ iNotifications ] );
|
||
|
|
||
|
}
|
||
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
||
|
{
|
||
|
fExceptionRaised = TRUE;
|
||
|
if( GetExceptionCode() != TRK_E_CORRUPT_LOG )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("After corrupting a sector, Open should have raised TRK_E_CORRUPT_LOG") ));
|
||
|
TrkRaiseException( GetExceptionCode() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( !fExceptionRaised )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("We should have gotten an exception after corrupting log sectors") ));
|
||
|
TrkRaiseWin32Error( E_FAIL );
|
||
|
}
|
||
|
|
||
|
g_cTestLog.ReInitialize();
|
||
|
|
||
|
if( !g_cTestLog.IsEmpty() )
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("After opening a corrupt log, we should have a new log file") ));
|
||
|
TrkRaiseWin32Error( E_FAIL );
|
||
|
}
|
||
|
|
||
|
cLogEntries = g_cTestLog.NumEntriesInFile();
|
||
|
iNotifications = 0;
|
||
|
seqFirst = 0;
|
||
|
|
||
|
FillAndExerciseLog( 0, cLogEntries, seqFirst, rgNotificationsWritten, rgNotificationsRead );
|
||
|
|
||
|
// Test that tunneling works correctly (if there are duplicate entries, we should
|
||
|
// get the most recent).
|
||
|
|
||
|
_tprintf( TEXT("Test tunneling\n") );
|
||
|
|
||
|
tempNotificationWrite = rgNotificationsWritten[0];
|
||
|
|
||
|
g_cTestLog.Append( 1, &tempNotificationWrite );
|
||
|
|
||
|
strncpy( (LPSTR) &tempNotificationWrite.droidBirth, "abcdefghijklmnopqrstuvwxyznowiknowmyabcsnexttimewontyousingwithme",
|
||
|
sizeof(tempNotificationWrite.droidBirth) );
|
||
|
|
||
|
g_cTestLog.Append( 1, &tempNotificationWrite );
|
||
|
g_cTestLog.Search( tempNotificationWrite.droidCurrent, &tempNotificationRead );
|
||
|
|
||
|
|
||
|
if( memcmp( &tempNotificationWrite, &tempNotificationRead, sizeof(tempNotificationWrite) ))
|
||
|
{
|
||
|
TrkLog(( TRKDBG_ERROR, TEXT("Didn't get the tunneled move notification") ));
|
||
|
TrkRaiseWin32Error( E_FAIL );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
_tprintf( TEXT("\nTests Passed\n") );
|
||
|
|
||
|
g_cTestLog.CloseLog();
|
||
|
|
||
|
} // __try
|
||
|
|
||
|
__finally
|
||
|
{
|
||
|
}
|
||
|
|
||
|
} // _tmain()
|