2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Class implementing a journaled test list for the Crash Recovery feature
5 * COPYRIGHT: Copyright 2009 Colin Finck (colin@reactos.org)
10 static const char szJournalHeader
[] = "RAT_J-V1";
11 static const WCHAR szJournalFileName
[] = L
"rosautotest.journal";
14 * Constructs a CJournaledTestList object for an associated CTest-derived object.
17 * Pointer to a CTest-derived object, for which this test list shall serve.
19 CJournaledTestList::CJournaledTestList(CTest
* Test
)
22 WCHAR JournalFile
[MAX_PATH
];
24 m_hJournal
= INVALID_HANDLE_VALUE
;
26 /* Build the path to the journal file */
27 if(SHGetFolderPathW(NULL
, CSIDL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, JournalFile
) != S_OK
)
28 FATAL("SHGetFolderPathW failed\n");
30 m_JournalFile
= JournalFile
;
31 m_JournalFile
+= L
"\\rosautotest\\";
33 /* Create the directory if necessary */
34 if(GetFileAttributesW(m_JournalFile
.c_str()) == INVALID_FILE_ATTRIBUTES
)
35 CreateDirectoryW(m_JournalFile
.c_str(), NULL
);
37 m_JournalFile
+= szJournalFileName
;
39 /* Check if the journal already exists */
40 if(GetFileAttributesW(m_JournalFile
.c_str()) == INVALID_FILE_ATTRIBUTES
)
41 WriteInitialJournalFile();
47 * Destructs a CJournaledTestList object.
49 CJournaledTestList::~CJournaledTestList()
51 if(m_hJournal
!= INVALID_HANDLE_VALUE
)
52 CloseHandle(m_hJournal
);
56 * Opens the journal file through the CreateFileW API using the m_hJournal handle.
58 * @param DesiredAccess
59 * dwDesiredAccess parameter passed to CreateFileW
62 * true if the journal file shall be created, false if an existing one shall be opened
65 CJournaledTestList::OpenJournal(DWORD DesiredAccess
, bool CreateNew
)
67 m_hJournal
= CreateFileW(
68 m_JournalFile
.c_str(),
72 (CreateNew
? CREATE_ALWAYS
: OPEN_EXISTING
),
73 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_WRITE_THROUGH
,
77 if(m_hJournal
== INVALID_HANDLE_VALUE
)
78 FATAL("CreateFileW failed\n");
82 * Serializes a std::string and writes it into the opened journal file.
85 * The std::string to serialize
87 * @see UnserializeFromBuffer
90 CJournaledTestList::SerializeIntoJournal(const string
& String
)
93 WriteFile(m_hJournal
, String
.c_str(), String
.size() + 1, &BytesWritten
, NULL
);
94 FlushFileBuffers(m_hJournal
);
98 * Serializes a std::wstring and writes it into the opened journal file.
101 * The std::wstring to serialize
103 * @see UnserializeFromBuffer
106 CJournaledTestList::SerializeIntoJournal(const wstring
& String
)
109 WriteFile(m_hJournal
, String
.c_str(), (String
.size() + 1) * sizeof(WCHAR
), &BytesWritten
, NULL
);
110 FlushFileBuffers(m_hJournal
);
114 * Unserializes the next std::string from the journal buffer.
115 * The passed buffer pointer will point at the next element afterwards.
118 * Pointer to a pointer to a char array containing the journal buffer
121 * The std::string to unserialize the value into.
124 CJournaledTestList::UnserializeFromBuffer(char** Buffer
, string
& Output
)
126 Output
= string(*Buffer
);
127 *Buffer
+= Output
.size() + 1;
131 * Unserializes the next std::wstring from the journal buffer.
132 * The passed buffer pointer will point at the next element afterwards.
135 * Pointer to a pointer to a char array containing the journal buffer
138 * The std::wstring to unserialize the value into.
141 CJournaledTestList::UnserializeFromBuffer(char** Buffer
, wstring
& Output
)
143 Output
= wstring((PWSTR
)*Buffer
);
144 *Buffer
+= (Output
.size() + 1) * sizeof(WCHAR
);
148 * Gets all tests to be run and writes an initial journal file with this information.
151 CJournaledTestList::WriteInitialJournalFile()
153 char TerminatingNull
= 0;
157 StringOut("Writing initial journal file...\n\n");
161 /* Store all command lines in the m_List vector */
162 while((TestInfo
= m_Test
->GetNextTestInfo()) != 0)
164 m_List
.push_back(*TestInfo
);
168 /* Serialize the vector and the iterator into a file */
169 OpenJournal(GENERIC_WRITE
, true);
171 WriteFile(m_hJournal
, szJournalHeader
, sizeof(szJournalHeader
), &BytesWritten
, NULL
);
172 WriteFile(m_hJournal
, &m_ListIterator
, sizeof(m_ListIterator
), &BytesWritten
, NULL
);
174 for(size_t i
= 0; i
< m_List
.size(); i
++)
176 SerializeIntoJournal(m_List
[i
].CommandLine
);
177 SerializeIntoJournal(m_List
[i
].Module
);
178 SerializeIntoJournal(m_List
[i
].Test
);
181 WriteFile(m_hJournal
, &TerminatingNull
, sizeof(TerminatingNull
), &BytesWritten
, NULL
);
182 FlushFileBuffers(m_hJournal
);
184 CloseHandle(m_hJournal
);
185 m_hJournal
= INVALID_HANDLE_VALUE
;
187 /* m_ListIterator will be incremented before its first use */
188 m_ListIterator
= (size_t)-1;
192 * Loads the existing journal file and sets all members to the values saved in that file.
195 CJournaledTestList::LoadJournalFile()
199 char FileHeader
[sizeof(szJournalHeader
)];
203 StringOut("Loading journal file...\n\n");
205 OpenJournal(GENERIC_READ
);
206 RemainingSize
= GetFileSize(m_hJournal
, NULL
);
208 /* Verify the header of the journal file */
209 ReadFile(m_hJournal
, FileHeader
, sizeof(szJournalHeader
), &BytesRead
, NULL
);
210 RemainingSize
-= BytesRead
;
212 if(BytesRead
!= sizeof(szJournalHeader
))
213 EXCEPTION("Journal file contains no header!\n");
215 if(strcmp(FileHeader
, szJournalHeader
))
216 EXCEPTION("Journal file has an unsupported header!\n");
218 /* Read the iterator */
219 ReadFile(m_hJournal
, &m_ListIterator
, sizeof(m_ListIterator
), &BytesRead
, NULL
);
220 RemainingSize
-= BytesRead
;
222 if(BytesRead
!= sizeof(m_ListIterator
))
223 EXCEPTION("Journal file contains no m_ListIterator member!\n");
225 /* Read the rest of the file into a buffer */
226 Buffer
= new char[RemainingSize
];
228 ReadFile(m_hJournal
, Buffer
, RemainingSize
, &BytesRead
, NULL
);
230 CloseHandle(m_hJournal
);
233 /* Now recreate the m_List vector out of that information */
238 UnserializeFromBuffer(&pBuffer
, TestInfo
.CommandLine
);
239 UnserializeFromBuffer(&pBuffer
, TestInfo
.Module
);
240 UnserializeFromBuffer(&pBuffer
, TestInfo
.Test
);
242 m_List
.push_back(TestInfo
);
249 * Writes the current m_ListIterator value into the journal.
252 CJournaledTestList::UpdateJournal()
256 OpenJournal(GENERIC_WRITE
);
258 /* Skip the header */
259 SetFilePointer(m_hJournal
, sizeof(szJournalHeader
), NULL
, FILE_CURRENT
);
261 WriteFile(m_hJournal
, &m_ListIterator
, sizeof(m_ListIterator
), &BytesWritten
, NULL
);
262 FlushFileBuffers(m_hJournal
);
264 CloseHandle(m_hJournal
);
269 * Interface to other classes for receiving information about the next test to be run.
272 * A pointer to a CTestInfo object, which contains all available information about the next test.
273 * The caller needs to free that object.
276 CJournaledTestList::GetNextTestInfo()
280 /* Always jump to the next test here.
281 - If we're at the beginning of a new test list, the iterator will be set to 0.
282 - If we started with a loaded one, we assume that the test m_ListIterator is currently set
283 to crashed, so we move to the next test. */
286 /* Check whether the iterator would already exceed the number of stored elements */
287 if(m_ListIterator
== m_List
.size())
289 /* Delete the journal and return no pointer */
290 DeleteFileW(m_JournalFile
.c_str());
296 /* Update the journal with the current iterator and return the test information */
299 TestInfo
= new CTestInfo(m_List
[m_ListIterator
]);