ReactOS 0.4.16-dev-92-g0c2cdca
CWineTest.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Class implementing functions for handling Wine tests
5 * COPYRIGHT: Copyright 2009-2019 Colin Finck (colin@reactos.org)
6 */
7
8#include "precomp.h"
9
10static const DWORD ListTimeout = 10000;
11
12// This value needs to be lower than the <timeout> configured in sysreg.xml! (usually 180000)
13// Otherwise, sysreg2 kills the VM before we can kill the process.
14static const DWORD ProcessActivityTimeout = 170000;
15
16
21 : m_hFind(NULL), m_ListBuffer(NULL)
22{
23 WCHAR wszDirectory[MAX_PATH];
24
25 /* Set up m_TestPath */
26 if (GetEnvironmentVariableW(L"ROSAUTOTEST_DIR", wszDirectory, MAX_PATH))
27 {
28 m_TestPath = wszDirectory;
29 if (*m_TestPath.rbegin() != L'\\')
30 m_TestPath += L'\\';
31 }
32 else
33 {
34 if (!GetWindowsDirectoryW(wszDirectory, MAX_PATH))
35 FATAL("GetWindowsDirectoryW failed\n");
36
37 m_TestPath = wszDirectory;
38 m_TestPath += L"\\bin\\";
39 }
40}
41
46{
47 if(m_hFind)
49
50 if(m_ListBuffer)
51 delete m_ListBuffer;
52}
53
60bool
62{
63 bool FoundFile = false;
65
66 /* Did we already begin searching for files? */
67 if(m_hFind)
68 {
69 /* Then get the next file (if any) */
71 FoundFile = true;
72 }
73 else
74 {
75 /* Start searching for test files */
76 wstring FindPath = m_TestPath;
77
78 /* Did the user specify a module? */
79 if(Configuration.GetModule().empty())
80 {
81 /* No module, so search for all files in that directory */
82 FindPath += L"*.exe";
83 }
84 else
85 {
86 /* Search for files with the pattern "modulename_*" */
87 FindPath += Configuration.GetModule();
88 FindPath += L"_*.exe";
89 }
90
91 /* Search for the first file and check whether we got one */
92 m_hFind = FindFirstFileW(FindPath.c_str(), &fd);
93
95 FoundFile = true;
96 }
97
98 if(FoundFile)
99 m_CurrentFile = fd.cFileName;
100
101 return FoundFile;
102}
103
110DWORD
112{
113 DWORD BytesAvailable;
114 DWORD Temp;
115 wstring CommandLine;
116 CPipe Pipe;
117
118 /* Build the command line */
119 CommandLine = m_TestPath;
120 CommandLine += m_CurrentFile;
121 CommandLine += L" --list";
122
123 {
124 /* Start the process for getting all available tests */
125 CPipedProcess Process(CommandLine, Pipe);
126
127 /* Wait till this process ended */
128 if(WaitForSingleObject(Process.GetProcessHandle(), ListTimeout) == WAIT_FAILED)
129 TESTEXCEPTION("WaitForSingleObject failed for the test list\n");
130 }
131
132 /* Read the output data into a buffer */
133 if(!Pipe.Peek(NULL, 0, NULL, &BytesAvailable))
134 TESTEXCEPTION("CPipe::Peek failed for the test list\n");
135
136 /* Check if we got any */
137 if(!BytesAvailable)
138 {
140
141 ss << "The --list command did not return any data for " << UnicodeToAscii(m_CurrentFile) << endl;
142 TESTEXCEPTION(ss.str());
143 }
144
145 /* Read the data */
146 m_ListBuffer = new char[BytesAvailable];
147
148 if(Pipe.Read(m_ListBuffer, BytesAvailable, &Temp, INFINITE) != ERROR_SUCCESS)
149 TESTEXCEPTION("CPipe::Read failed\n");
150
151 return BytesAvailable;
152}
153
160bool
162{
163 PCHAR pEnd;
164 static DWORD BufferSize;
165 static PCHAR pStart;
166
167 if(!m_ListBuffer)
168 {
169 /* Perform the --list command */
171
172 /* Move the pointer to the first test */
173 pStart = strchr(m_ListBuffer, '\n');
174 pStart += 5;
175 }
176
177 /* If we reach the buffer size, we finished analyzing the output of this test */
178 if(pStart >= (m_ListBuffer + BufferSize))
179 {
180 /* Clear m_CurrentFile to indicate that */
181 m_CurrentFile.clear();
182
183 /* Also free the memory for the list buffer */
184 delete[] m_ListBuffer;
186
187 return false;
188 }
189
190 /* Get start and end of this test name */
191 pEnd = pStart;
192
193 while(*pEnd != '\r')
194 ++pEnd;
195
196 /* Store the test name */
197 m_CurrentTest = string(pStart, pEnd);
198
199 /* Move the pointer to the next test */
200 pStart = pEnd + 6;
201
202 return true;
203}
204
213{
214 while(!m_CurrentFile.empty() || GetNextFile())
215 {
216 /* The user asked for a list of all modules */
217 if (Configuration.ListModulesOnly())
218 {
219 std::stringstream ss;
220 ss << "Module: " << UnicodeToAscii(m_CurrentFile) << endl;
221 m_CurrentFile.clear();
222 StringOut(ss.str());
223 continue;
224 }
225
226 try
227 {
228 while(GetNextTest())
229 {
230 /* If the user specified a test through the command line, check this here */
231 if(!Configuration.GetTest().empty() && Configuration.GetTest() != m_CurrentTest)
232 continue;
233
234 {
235 auto_ptr<CTestInfo> TestInfo(new CTestInfo());
236 size_t UnderscorePosition;
237
238 /* Build the command line */
239 TestInfo->CommandLine = m_TestPath;
240 TestInfo->CommandLine += m_CurrentFile;
241 TestInfo->CommandLine += ' ';
242 TestInfo->CommandLine += AsciiToUnicode(m_CurrentTest);
243
244 /* Store the Module name */
245 UnderscorePosition = m_CurrentFile.find_last_of('_');
246
247 if(UnderscorePosition == m_CurrentFile.npos)
248 {
250
251 ss << "Invalid test file name: " << UnicodeToAscii(m_CurrentFile) << endl;
253 }
254
255 TestInfo->Module = UnicodeToAscii(m_CurrentFile.substr(0, UnderscorePosition));
256
257 /* Store the test */
258 TestInfo->Test = m_CurrentTest;
259
260 return TestInfo.release();
261 }
262 }
263 }
264 catch(CTestException& e)
265 {
267
268 ss << "An exception occurred trying to list tests for: " << UnicodeToAscii(m_CurrentFile) << endl;
269 StringOut(ss.str());
270 StringOut(e.GetMessage());
271 StringOut("\n");
272 m_CurrentFile.clear();
273 delete[] m_ListBuffer;
274 }
275 }
276
277 return NULL;
278}
279
287void
289{
290 DWORD BytesAvailable;
291 stringstream ss, ssFinish;
293 float TotalTime;
294 string tailString;
295 CPipe Pipe;
296 char Buffer[1024];
297
298 ss << "Running Wine Test, Module: " << TestInfo->Module << ", Test: " << TestInfo->Test << endl;
299 StringOut(ss.str());
300
302
304
305 try
306 {
307 /* Execute the test */
309
310 /* Receive all the data from the pipe */
311 for (;;)
312 {
313 DWORD dwReadResult = Pipe.Read(Buffer, sizeof(Buffer) - 1, &BytesAvailable, ProcessActivityTimeout);
314 if (dwReadResult == ERROR_SUCCESS)
315 {
316 /* Output text through StringOut, even while the test is still running */
317 Buffer[BytesAvailable] = 0;
318 tailString = StringOut(tailString.append(string(Buffer)), false);
319
320 if (Configuration.DoSubmit())
321 TestInfo->Log += Buffer;
322 }
323 else if (dwReadResult == ERROR_BROKEN_PIPE)
324 {
325 // The process finished and has been terminated.
326 break;
327 }
328 else if (dwReadResult == WAIT_TIMEOUT)
329 {
330 // The process activity timeout above has elapsed without any new data.
331 TESTEXCEPTION("Timeout while waiting for the test process\n");
332 }
333 else
334 {
335 // An unexpected error.
336 TESTEXCEPTION("CPipe::Read failed for the test run\n");
337 }
338 }
339 }
340 catch(CTestException& e)
341 {
342 if(!tailString.empty())
343 StringOut(tailString);
344 tailString.clear();
345 StringOut(e.GetMessage());
346 TestInfo->Log += e.GetMessage();
347 }
348
349 /* Print what's left */
350 if(!tailString.empty())
351 StringOut(tailString);
352
353 TotalTime = ((float)GetTickCount() - StartTime)/1000;
354 ssFinish << "Test " << TestInfo->Test << " completed in ";
355 ssFinish << setprecision(2) << fixed << TotalTime << " seconds." << endl;
356 StringOut(ssFinish.str());
357 TestInfo->Log += ssFinish.str();
358}
359
363void
365{
367 auto_ptr<CWebService> WebService;
368 CTestInfo* TestInfo;
369 DWORD ErrorMode;
370
371 /* The virtual test list is of course faster, so it should be preferred over
372 the journaled one.
373 Enable the journaled one only in case ...
374 - we're running under ReactOS (as the journal is only useful in conjunction with sysreg2)
375 - we shall keep information for Crash Recovery
376 - and the user didn't specify a module (then doing Crash Recovery doesn't really make sense) */
377 if(Configuration.IsReactOS() && Configuration.DoCrashRecovery() && Configuration.GetModule().empty())
378 {
379 /* Use a test list with a permanent journal */
380 TestList.reset(new CJournaledTestList(this));
381 }
382 else
383 {
384 /* Use the fast virtual test list with no additional overhead */
385 TestList.reset(new CVirtualTestList(this));
386 }
387
388 /* Initialize the Web Service interface if required */
389 if(Configuration.DoSubmit())
390 WebService.reset(new CWebService());
391
392 /* Disable error dialogs if we're running in non-interactive mode */
393 if(!Configuration.IsInteractive())
395
396 /* Get information for each test to run */
397 while((TestInfo = TestList->GetNextTestInfo()) != 0)
398 {
399 auto_ptr<CTestInfo> TestInfoPtr(TestInfo);
400
401 RunTest(TestInfo);
402
403 if(Configuration.DoSubmit() && !TestInfo->Log.empty())
404 WebService->Submit("wine", TestInfo);
405
406 StringOut("\n\n");
407 }
408
409 /* We're done with all tests. Finish this run */
410 if(Configuration.DoSubmit())
411 WebService->Finish("wine");
412
413 /* Restore the original error mode */
414 if(!Configuration.IsInteractive())
415 SetErrorMode(ErrorMode);
416}
static const DWORD ListTimeout
Definition: CWineTest.cpp:10
static const DWORD ProcessActivityTimeout
Definition: CWineTest.cpp:14
static KSTART_ROUTINE RunTest
Definition: NpfsConnect.c:238
_STLP_PRIV _Ios_Manip_1< streamsize > _STLP_CALL setprecision(int __n)
Definition: _iomanip.h:119
ios_base &_STLP_CALL fixed(ios_base &__s)
Definition: _ios_base.h:332
basic_ostream< _CharT, _Traits > &_STLP_CALL endl(basic_ostream< _CharT, _Traits > &__os)
Definition: _ostream.h:357
char * strchr(const char *String, int ch)
Definition: utclib.c:501
Definition: bufpool.h:45
Definition: CPipe.h:10
wstring CommandLine
Definition: CTestInfo.h:11
string Log
Definition: CTestInfo.h:14
string Module
Definition: CTestInfo.h:12
string Test
Definition: CTestInfo.h:13
friend class CJournaledTestList
Definition: CTest.h:17
friend class CVirtualTestList
Definition: CTest.h:18
bool GetNextFile()
Definition: CWineTest.cpp:61
PCHAR m_ListBuffer
Definition: CWineTest.h:12
bool GetNextTest()
Definition: CWineTest.cpp:161
wstring m_TestPath
Definition: CWineTest.h:16
void Run()
Definition: CWineTest.cpp:364
string m_CurrentTest
Definition: CWineTest.h:13
CTestInfo * GetNextTestInfo()
Definition: CWineTest.cpp:212
DWORD DoListCommand()
Definition: CWineTest.cpp:111
HANDLE m_hFind
Definition: CWineTest.h:11
wstring m_CurrentFile
Definition: CWineTest.h:14
void RunTest(CTestInfo *TestInfo)
Definition: CWineTest.cpp:288
void reset(_Tp *__px=0) _STLP_NOTHROW
Definition: _auto_ptr.h:59
_Tp * release() _STLP_NOTHROW
Definition: _auto_ptr.h:53
_String str() const
Definition: _sstream.h:230
#define WAIT_TIMEOUT
Definition: dderror.h:14
#define BufferSize
Definition: mmc.h:75
#define ERROR_SUCCESS
Definition: deptool.c:10
#define NULL
Definition: types.h:112
#define GetEnvironmentVariableW(x, y, z)
Definition: compat.h:755
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
#define MAX_PATH
Definition: compat.h:34
UINT WINAPI SetErrorMode(IN UINT uMode)
Definition: except.c:751
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
BOOL WINAPI FindNextFileW(IN HANDLE hFindFile, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:382
BOOL WINAPI SetCurrentDirectoryW(IN LPCWSTR lpPathName)
Definition: path.c:2249
UINT WINAPI GetWindowsDirectoryW(OUT LPWSTR lpBuffer, IN UINT uSize)
Definition: path.c:2352
DWORD WINAPI GetTickCount(VOID)
Definition: time.c:455
#define INFINITE
Definition: serial.h:102
unsigned long DWORD
Definition: ntddk_ex.h:95
_Must_inspect_result_ _In_ PLARGE_INTEGER _In_ PLARGE_INTEGER _In_ ULONG _In_ PFILE_OBJECT _In_ PVOID Process
Definition: fsrtlfuncs.h:223
#define ss
Definition: i386-dis.c:441
#define e
Definition: ke_i.h:82
char string[160]
Definition: util.h:11
#define SSEXCEPTION
Definition: precomp.h:58
string StringOut(const string &String, bool forcePrint=true)
Definition: tools.cpp:96
string UnicodeToAscii(PCWSTR UnicodeString)
Definition: tools.cpp:261
wstring AsciiToUnicode(const char *AsciiString)
Definition: tools.cpp:220
#define FATAL(Message)
Definition: precomp.h:57
#define TESTEXCEPTION(Message)
Definition: precomp.h:59
static float(__cdecl *square_half_float)(float x
#define SEM_FAILCRITICALERRORS
Definition: rtltypes.h:69
#define SEM_NOGPFAULTERRORBOX
Definition: rtltypes.h:70
#define L(x)
Definition: ntvdm.h:50
static int fd
Definition: io.c:51
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
static LARGE_INTEGER StartTime
Definition: sys_arch.c:13
EH_STD::__list__< TestClass, eh_allocator(TestClass) > TestList
Definition: test_list.cpp:31
char * PCHAR
Definition: typedefs.h:51
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_INTERRUPT_CONFIG Configuration
Definition: wdfinterrupt.h:374
_In_ WDFMEMORY _Out_opt_ size_t * BufferSize
Definition: wdfmemory.h:254
_In_ WDFUSBPIPE Pipe
Definition: wdfusb.h:1741
#define WAIT_FAILED
Definition: winbase.h:413
#define ERROR_BROKEN_PIPE
Definition: winerror.h:183
__wchar_t WCHAR
Definition: xmlstorage.h:180