[ROSAUTOTEST]
[reactos.git] / rostests / rosautotest / CJournaledTestList.cpp
1 /*
2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
4 * PURPOSE: Class implementing a journaled test list for the Crash Recovery feature
5 * COPYRIGHT: Copyright 2009 Colin Finck <colin@reactos.org>
6 */
7
8 #include "precomp.h"
9
10 static const char szJournalHeader[] = "RAT_J-V1";
11 static const WCHAR szJournalFileName[] = L"rosautotest.journal";
12
13 /**
14 * Constructs a CJournaledTestList object for an associated CTest-derived object.
15 *
16 * @param Test
17 * Pointer to a CTest-derived object, for which this test list shall serve.
18 */
19 CJournaledTestList::CJournaledTestList(CTest* Test)
20 : CTestList(Test)
21 {
22 WCHAR JournalFile[MAX_PATH];
23
24 m_hJournal = INVALID_HANDLE_VALUE;
25
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");
29
30 m_JournalFile = JournalFile;
31 m_JournalFile += L"\\rosautotest\\";
32
33 /* Create the directory if necessary */
34 if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
35 CreateDirectoryW(m_JournalFile.c_str(), NULL);
36
37 m_JournalFile += szJournalFileName;
38
39 /* Check if the journal already exists */
40 if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
41 WriteInitialJournalFile();
42 else
43 LoadJournalFile();
44 }
45
46 /**
47 * Destructs a CJournaledTestList object.
48 */
49 CJournaledTestList::~CJournaledTestList()
50 {
51 if(m_hJournal != INVALID_HANDLE_VALUE)
52 CloseHandle(m_hJournal);
53 }
54
55 /**
56 * Opens the journal file through the CreateFileW API using the m_hJournal handle.
57 *
58 * @param DesiredAccess
59 * dwDesiredAccess parameter passed to CreateFileW
60 *
61 * @param CreateNew
62 * true if the journal file shall be created, false if an existing one shall be opened
63 */
64 void
65 CJournaledTestList::OpenJournal(DWORD DesiredAccess, bool CreateNew)
66 {
67 m_hJournal = CreateFileW(m_JournalFile.c_str(), DesiredAccess, 0, NULL, (CreateNew ? CREATE_ALWAYS : OPEN_EXISTING), FILE_ATTRIBUTE_NORMAL, NULL);
68
69 if(m_hJournal == INVALID_HANDLE_VALUE)
70 FATAL("CreateFileW failed\n");
71 }
72
73 /**
74 * Serializes a std::string and writes it into the opened journal file.
75 *
76 * @param String
77 * The std::string to serialize
78 *
79 * @see UnserializeFromBuffer
80 */
81 void
82 CJournaledTestList::SerializeIntoJournal(const string& String)
83 {
84 DWORD BytesWritten;
85 WriteFile(m_hJournal, String.c_str(), String.size() + 1, &BytesWritten, NULL);
86 FlushFileBuffers(m_hJournal);
87 }
88
89 /**
90 * Serializes a std::wstring and writes it into the opened journal file.
91 *
92 * @param String
93 * The std::wstring to serialize
94 *
95 * @see UnserializeFromBuffer
96 */
97 void
98 CJournaledTestList::SerializeIntoJournal(const wstring& String)
99 {
100 DWORD BytesWritten;
101 WriteFile(m_hJournal, String.c_str(), (String.size() + 1) * sizeof(WCHAR), &BytesWritten, NULL);
102 FlushFileBuffers(m_hJournal);
103 }
104
105 /**
106 * Unserializes the next std::string from the journal buffer.
107 * The passed buffer pointer will point at the next element afterwards.
108 *
109 * @param Buffer
110 * Pointer to a pointer to a char array containing the journal buffer
111 *
112 * @param Output
113 * The std::string to unserialize the value into.
114 */
115 void
116 CJournaledTestList::UnserializeFromBuffer(char** Buffer, string& Output)
117 {
118 Output = string(*Buffer);
119 *Buffer += Output.size() + 1;
120 }
121
122 /**
123 * Unserializes the next std::wstring from the journal buffer.
124 * The passed buffer pointer will point at the next element afterwards.
125 *
126 * @param Buffer
127 * Pointer to a pointer to a char array containing the journal buffer
128 *
129 * @param Output
130 * The std::wstring to unserialize the value into.
131 */
132 void
133 CJournaledTestList::UnserializeFromBuffer(char** Buffer, wstring& Output)
134 {
135 Output = wstring((PWSTR)*Buffer);
136 *Buffer += (Output.size() + 1) * sizeof(WCHAR);
137 }
138
139 /**
140 * Gets all tests to be run and writes an initial journal file with this information.
141 */
142 void
143 CJournaledTestList::WriteInitialJournalFile()
144 {
145 char TerminatingNull = 0;
146 CTestInfo* TestInfo;
147 DWORD BytesWritten;
148
149 StringOut("Writing initial journal file...\n\n", TRUE);
150
151 m_ListIterator = 0;
152
153 /* Store all command lines in the m_List vector */
154 while((TestInfo = m_Test->GetNextTestInfo()) != 0)
155 {
156 m_List.push_back(*TestInfo);
157 delete TestInfo;
158 }
159
160 /* Serialize the vector and the iterator into a file */
161 OpenJournal(GENERIC_WRITE, true);
162
163 WriteFile(m_hJournal, szJournalHeader, sizeof(szJournalHeader), &BytesWritten, NULL);
164 WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
165
166 for(size_t i = 0; i < m_List.size(); i++)
167 {
168 SerializeIntoJournal(m_List[i].CommandLine);
169 SerializeIntoJournal(m_List[i].Module);
170 SerializeIntoJournal(m_List[i].Test);
171 }
172
173 WriteFile(m_hJournal, &TerminatingNull, sizeof(TerminatingNull), &BytesWritten, NULL);
174 FlushFileBuffers(m_hJournal);
175
176 CloseHandle(m_hJournal);
177 m_hJournal = INVALID_HANDLE_VALUE;
178
179 /* m_ListIterator will be incremented before its first use */
180 m_ListIterator = (size_t)-1;
181 }
182
183 /**
184 * Loads the existing journal file and sets all members to the values saved in that file.
185 */
186 void
187 CJournaledTestList::LoadJournalFile()
188 {
189 char* Buffer;
190 char* pBuffer;
191 char FileHeader[sizeof(szJournalHeader)];
192 DWORD BytesRead;
193 DWORD RemainingSize;
194
195 StringOut("Loading journal file...\n\n", TRUE);
196
197 OpenJournal(GENERIC_READ);
198 RemainingSize = GetFileSize(m_hJournal, NULL);
199
200 /* Verify the header of the journal file */
201 ReadFile(m_hJournal, FileHeader, sizeof(szJournalHeader), &BytesRead, NULL);
202 RemainingSize -= BytesRead;
203
204 if(BytesRead != sizeof(szJournalHeader))
205 EXCEPTION("Journal file contains no header!\n");
206
207 if(strcmp(FileHeader, szJournalHeader))
208 EXCEPTION("Journal file has an unsupported header!\n");
209
210 /* Read the iterator */
211 ReadFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesRead, NULL);
212 RemainingSize -= BytesRead;
213
214 if(BytesRead != sizeof(m_ListIterator))
215 EXCEPTION("Journal file contains no m_ListIterator member!\n");
216
217 /* Read the rest of the file into a buffer */
218 Buffer = new char[RemainingSize];
219 pBuffer = Buffer;
220 ReadFile(m_hJournal, Buffer, RemainingSize, &BytesRead, NULL);
221
222 CloseHandle(m_hJournal);
223 m_hJournal = NULL;
224
225 /* Now recreate the m_List vector out of that information */
226 while(*pBuffer)
227 {
228 CTestInfo TestInfo;
229
230 UnserializeFromBuffer(&pBuffer, TestInfo.CommandLine);
231 UnserializeFromBuffer(&pBuffer, TestInfo.Module);
232 UnserializeFromBuffer(&pBuffer, TestInfo.Test);
233
234 m_List.push_back(TestInfo);
235 }
236
237 delete[] Buffer;
238 }
239
240 /**
241 * Writes the current m_ListIterator value into the journal.
242 */
243 void
244 CJournaledTestList::UpdateJournal()
245 {
246 DWORD BytesWritten;
247
248 OpenJournal(GENERIC_WRITE);
249
250 /* Skip the header */
251 SetFilePointer(m_hJournal, sizeof(szJournalHeader), NULL, FILE_CURRENT);
252
253 WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
254 FlushFileBuffers(m_hJournal);
255
256 CloseHandle(m_hJournal);
257 m_hJournal = NULL;
258 }
259
260 /**
261 * Interface to other classes for receiving information about the next test to be run.
262 *
263 * @return
264 * A pointer to a CTestInfo object, which contains all available information about the next test.
265 * The caller needs to free that object.
266 */
267 CTestInfo*
268 CJournaledTestList::GetNextTestInfo()
269 {
270 CTestInfo* TestInfo;
271
272 /* Always jump to the next test here.
273 - If we're at the beginning of a new test list, the iterator will be set to 0.
274 - If we started with a loaded one, we assume that the test m_ListIterator is currently set
275 to crashed, so we move to the next test. */
276 ++m_ListIterator;
277
278 /* Check whether the iterator would already exceed the number of stored elements */
279 if(m_ListIterator == m_List.size())
280 {
281 /* Delete the journal and return no pointer */
282 DeleteFileW(m_JournalFile.c_str());
283
284 TestInfo = NULL;
285 }
286 else
287 {
288 /* Update the journal with the current iterator and return the test information */
289 UpdateJournal();
290
291 TestInfo = new CTestInfo(m_List[m_ListIterator]);
292 }
293
294 return TestInfo;
295 }