reactos/rostests/rosautotest/CJournaledTestList.cpp
Colin Finck 10f13abcb5 Big testing system commit
rosautotest
- Rewrite rosautotest in C++
  Should increase maintainability and expandability, since most of the functionality is encapsulated in classes and there exist some abstract classes for further enhancements (i.e. new test types).
  Furthermore, due to the usage of STL strings, we don't need x lines anymore just for building a string out of several small parts.
- The new codebase made it fairly easy to implement a Crash Recovery feature based on a journal.
  If you start rosautotest with the /r option under ReactOS, it will keep a journal about the tests to run and the tests already ran. In case of a crash, it can just continue with the next test in the list then.
- Add some reasonable timeouts to avoid certain hangs in case a test crashes

sysreg2
- Make the necessary changes to sysreg2 to restart the VM in case of such a crash in 3rd stage, but set a maximum number of allowed crashes as well.
  Christoph, please test and review that on the Buildslave :-)
- Prepend all sysreg messages with [SYSREG] through a new function SysregPrintf, so the BuildBot aggregator script of testman can distinguish between debug output and sysreg messages.
- Put all header includes into the central header file "sysreg.h"
- Remove unnecessary libs from the Makefile

testman
- Change the testman Web Interface to show such crashes as CRASH in the Compare and Detail views.

svn path=/trunk/; revision=40147
2009-03-21 01:39:04 +00:00

292 lines
8.1 KiB
C++

/*
* PROJECT: ReactOS Automatic Testing Utility
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
* PURPOSE: Class implementing a journaled test list for the Crash Recovery feature
* COPYRIGHT: Copyright 2009 Colin Finck <colin@reactos.org>
*/
#include "precomp.h"
static const char szJournalHeader[] = "RAT_J-V1";
static const WCHAR szJournalFileName[] = L"rosautotest.journal";
/**
* Constructs a CJournaledTestList object for an associated CTest-derived object.
*
* @param Test
* Pointer to a CTest-derived object, for which this test list shall serve.
*/
CJournaledTestList::CJournaledTestList(CTest* Test)
: CTestList(Test)
{
WCHAR JournalFile[MAX_PATH];
m_hJournal = INVALID_HANDLE_VALUE;
/* Build the path to the journal file */
if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, JournalFile) != S_OK)
FATAL("SHGetFolderPathW failed\n");
m_JournalFile = JournalFile;
m_JournalFile += L"\\rosautotest\\";
/* Create the directory if necessary */
if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
CreateDirectoryW(m_JournalFile.c_str(), NULL);
m_JournalFile += szJournalFileName;
/* Check if the journal already exists */
if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
WriteInitialJournalFile();
else
LoadJournalFile();
}
/**
* Destructs a CJournaledTestList object.
*/
CJournaledTestList::~CJournaledTestList()
{
if(m_hJournal != INVALID_HANDLE_VALUE)
CloseHandle(m_hJournal);
}
/**
* Opens the journal file through the CreateFileW API using the m_hJournal handle.
*
* @param DesiredAccess
* dwDesiredAccess parameter passed to CreateFileW
*
* @param CreateNew
* true if the journal file shall be created, false if an existing one shall be opened
*/
void
CJournaledTestList::OpenJournal(DWORD DesiredAccess, bool CreateNew)
{
m_hJournal = CreateFileW(m_JournalFile.c_str(), DesiredAccess, 0, NULL, (CreateNew ? CREATE_ALWAYS : OPEN_EXISTING), FILE_ATTRIBUTE_NORMAL, NULL);
if(m_hJournal == INVALID_HANDLE_VALUE)
FATAL("CreateFileW failed\n");
}
/**
* Serializes a std::string and writes it into the opened journal file.
*
* @param String
* The std::string to serialize
*
* @see UnserializeFromBuffer
*/
void
CJournaledTestList::SerializeIntoJournal(const string& String)
{
DWORD BytesWritten;
WriteFile(m_hJournal, String.c_str(), String.size() + 1, &BytesWritten, NULL);
}
/**
* Serializes a std::wstring and writes it into the opened journal file.
*
* @param String
* The std::wstring to serialize
*
* @see UnserializeFromBuffer
*/
void
CJournaledTestList::SerializeIntoJournal(const wstring& String)
{
DWORD BytesWritten;
WriteFile(m_hJournal, String.c_str(), (String.size() + 1) * sizeof(WCHAR), &BytesWritten, NULL);
}
/**
* Unserializes the next std::string from the journal buffer.
* The passed buffer pointer will point at the next element afterwards.
*
* @param Buffer
* Pointer to a pointer to a char array containing the journal buffer
*
* @param Output
* The std::string to unserialize the value into.
*/
void
CJournaledTestList::UnserializeFromBuffer(char** Buffer, string& Output)
{
Output = string(*Buffer);
*Buffer += Output.size() + 1;
}
/**
* Unserializes the next std::wstring from the journal buffer.
* The passed buffer pointer will point at the next element afterwards.
*
* @param Buffer
* Pointer to a pointer to a char array containing the journal buffer
*
* @param Output
* The std::wstring to unserialize the value into.
*/
void
CJournaledTestList::UnserializeFromBuffer(char** Buffer, wstring& Output)
{
Output = wstring((PWSTR)*Buffer);
*Buffer += (Output.size() + 1) * sizeof(WCHAR);
}
/**
* Gets all tests to be run and writes an initial journal file with this information.
*/
void
CJournaledTestList::WriteInitialJournalFile()
{
char TerminatingNull = 0;
CTestInfo* TestInfo;
DWORD BytesWritten;
StringOut("Writing initial journal file...\n\n");
m_ListIterator = 0;
/* Store all command lines in the m_List vector */
while((TestInfo = m_Test->GetNextTestInfo()) != 0)
{
m_List.push_back(*TestInfo);
delete TestInfo;
}
/* Serialize the vector and the iterator into a file */
OpenJournal(GENERIC_WRITE, true);
WriteFile(m_hJournal, szJournalHeader, sizeof(szJournalHeader), &BytesWritten, NULL);
WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
for(size_t i = 0; i < m_List.size(); i++)
{
SerializeIntoJournal(m_List[i].CommandLine);
SerializeIntoJournal(m_List[i].Module);
SerializeIntoJournal(m_List[i].Test);
}
WriteFile(m_hJournal, &TerminatingNull, sizeof(TerminatingNull), &BytesWritten, NULL);
CloseHandle(m_hJournal);
m_hJournal = INVALID_HANDLE_VALUE;
/* m_ListIterator will be incremented before its first use */
m_ListIterator = (size_t)-1;
}
/**
* Loads the existing journal file and sets all members to the values saved in that file.
*/
void
CJournaledTestList::LoadJournalFile()
{
char* Buffer;
char* pBuffer;
char FileHeader[sizeof(szJournalHeader)];
DWORD BytesRead;
DWORD RemainingSize;
StringOut("Loading journal file...\n\n");
OpenJournal(GENERIC_READ);
RemainingSize = GetFileSize(m_hJournal, NULL);
/* Verify the header of the journal file */
ReadFile(m_hJournal, FileHeader, sizeof(szJournalHeader), &BytesRead, NULL);
RemainingSize -= BytesRead;
if(BytesRead != sizeof(szJournalHeader))
EXCEPTION("Journal file contains no header!\n");
if(strcmp(FileHeader, szJournalHeader))
EXCEPTION("Journal file has an unsupported header!\n");
/* Read the iterator */
ReadFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesRead, NULL);
RemainingSize -= BytesRead;
if(BytesRead != sizeof(m_ListIterator))
EXCEPTION("Journal file contains no m_ListIterator member!\n");
/* Read the rest of the file into a buffer */
Buffer = new char[RemainingSize];
pBuffer = Buffer;
ReadFile(m_hJournal, Buffer, RemainingSize, &BytesRead, NULL);
CloseHandle(m_hJournal);
m_hJournal = NULL;
/* Now recreate the m_List vector out of that information */
while(*pBuffer)
{
CTestInfo TestInfo;
UnserializeFromBuffer(&pBuffer, TestInfo.CommandLine);
UnserializeFromBuffer(&pBuffer, TestInfo.Module);
UnserializeFromBuffer(&pBuffer, TestInfo.Test);
m_List.push_back(TestInfo);
}
delete[] Buffer;
}
/**
* Writes the current m_ListIterator value into the journal.
*/
void
CJournaledTestList::UpdateJournal()
{
DWORD BytesWritten;
OpenJournal(GENERIC_WRITE);
/* Skip the header */
SetFilePointer(m_hJournal, sizeof(szJournalHeader), NULL, FILE_CURRENT);
WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
CloseHandle(m_hJournal);
m_hJournal = NULL;
}
/**
* Interface to other classes for receiving information about the next test to be run.
*
* @return
* A pointer to a CTestInfo object, which contains all available information about the next test.
* The caller needs to free that object.
*/
CTestInfo*
CJournaledTestList::GetNextTestInfo()
{
CTestInfo* TestInfo;
/* Always jump to the next test here.
- If we're at the beginning of a new test list, the iterator will be set to 0.
- If we started with a loaded one, we assume that the test m_ListIterator is currently set
to crashed, so we move to the next test. */
++m_ListIterator;
/* Check whether the iterator would already exceed the number of stored elements */
if(m_ListIterator == m_List.size())
{
/* Delete the journal and return no pointer */
DeleteFileW(m_JournalFile.c_str());
TestInfo = NULL;
}
else
{
/* Update the journal with the current iterator and return the test information */
UpdateJournal();
TestInfo = new CTestInfo(m_List[m_ListIterator]);
}
return TestInfo;
}