ReactOS 0.4.17-dev-470-gf9e3448
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
57bool
59{
60 bool FoundFile = false;
62
63 /* Reset the test list */
65 m_ListString.clear();
66
67 /* Did we already begin searching for files? */
68 if (m_hFind)
69 {
70 /* Then get the next file (if any) */
72 {
73 // printf("cFileName is '%S'.\n", fd.cFileName);
74 /* If it was NOT rosautotest.exe then proceed as normal */
75 if (_wcsicmp(fd.cFileName, TestName) != 0)
76 {
77 FoundFile = true;
78 }
79 else
80 {
81 /* It was rosautotest.exe so get the next file (if any) */
83 {
84 FoundFile = true;
85 }
86 // printf("cFileName is '%S'.\n", fd.cFileName);
87 }
88 }
89 }
90 else
91 {
92 /* Start searching for test files */
93 wstring FindPath = m_TestPath;
94 wstring Module = Configuration.GetModule();
95
96 /* Did the user specify a module? */
97 if(Module.empty())
98 {
99 /* No module, so search for all "*test.exe" files in that directory */
100 FindPath += L"*test.exe";
101 }
102 else
103 {
104 /* Check for 'special' tests (e.g. "kmtest") or full test name ("ntdll_winetest") */
105 if (Module.substr(Module.length() - 4, 4) == L"test")
106 {
107 /* Search for files with the pattern "modulename.exe" */
108 FindPath += Configuration.GetModule();
109 FindPath += L".exe";
110 }
111 else
112 {
113 /* Search for files with the pattern "modulename_*test.exe" */
114 FindPath += Configuration.GetModule();
115 FindPath += L"_*test.exe";
116 }
117 }
118
119 /* Search for the first file and check whether we got one */
120 m_hFind = FindFirstFileW(FindPath.c_str(), &fd);
121
122 /* If we returned a good handle */
124 {
125 // printf("cFileName is '%S'.\n", fd.cFileName);
126 /* If it was NOT rosautotest.exe then proceed as normal */
127 if (_wcsicmp(fd.cFileName, TestName) != 0)
128 {
129 FoundFile = true;
130 }
131 else
132 {
133 /* It was rosautotest.exe so get the next file (if any) */
134 if (FindNextFileW(m_hFind, &fd))
135 {
136 FoundFile = true;
137 }
138 // printf("cFileName is '%S'.\n", fd.cFileName);
139 }
140 }
141 }
142
143 if(FoundFile)
144 m_CurrentFile = fd.cFileName;
145
146 return FoundFile;
147}
148
155DWORD
157{
159 wstring CommandLine;
160 CPipe Pipe;
161 CHAR TempBuffer[1024];
162 DWORD ret;
163
164 m_ListString.clear();
165
166 /* Build the command line */
167 CommandLine = m_TestPath;
168 CommandLine += m_CurrentFile;
169 CommandLine += L" --list";
170
171 /* Start the process for getting all available tests */
172 CPipedProcess Process(CommandLine, Pipe);
173
174 for (;;)
175 {
176 /* Try to read from the pipe */
177 ret = Pipe.Read(TempBuffer, ARRAYSIZE(TempBuffer), &BytesRead, ListTimeout);
178
179 /* If the pipe is broken, the process terminated */
180 if (ret == ERROR_BROKEN_PIPE)
181 break;
182
183 /* Timeout, the process might not be responding */
184 if (ret == WAIT_TIMEOUT)
185 break;
186
187 if (ret != ERROR_SUCCESS)
188 TESTEXCEPTION("Unexpected error\n");
189
190 m_ListString.append(TempBuffer, BytesRead);
191 }
192
193 if (WaitForSingleObject(Process.GetProcessHandle(), ListTimeout) != ERROR_SUCCESS)
194 TESTEXCEPTION("WaitForSingleObject failed for the test list\n");
195
197 return (DWORD)m_ListString.size();
198}
199
206bool
208{
209 PCHAR pEnd;
210 static DWORD BufferSize;
211 static PCHAR pStart;
212
213 if(!m_ListBuffer)
214 {
215 /* Perform the --list command */
217
218 if ((BufferSize == 0) || (m_ListBuffer == NULL))
219 {
221 ss << "The --list command did not return any data for " << UnicodeToAscii(m_CurrentFile) << endl;
222 TESTEXCEPTION(ss.str());
223 }
224
225 /* Move the pointer to the first test */
226 pStart = strchr(m_ListBuffer, '\n');
227 pStart += 5;
228 }
229
230 /* If we reach the buffer size, we finished analyzing the output of this test */
231 if(pStart >= (m_ListBuffer + BufferSize))
232 {
233 /* Clear m_CurrentFile to indicate that */
234 m_CurrentFile.clear();
235
236 /* Also free the memory for the list buffer */
238 m_ListString.clear();
239
240 return false;
241 }
242
243 /* Get start and end of this test name */
244 pEnd = pStart;
245
246 while (*pEnd != '\r')
247 {
248 if (*pEnd == '\0')
249 TESTEXCEPTION("Unexpected test list format\n");
250 ++pEnd;
251 }
252
253 /* Store the test name */
254 m_CurrentTest = string(pStart, pEnd);
255
256 /* Move the pointer to the next test */
257 pStart = pEnd + 6;
258
259 return true;
260}
261
270{
271 while(!m_CurrentFile.empty() || GetNextFile())
272 {
273 /* The user asked for a list of all modules */
274 if (Configuration.ListModulesOnly())
275 {
276 std::stringstream ss;
277 ss << "Module: " << UnicodeToAscii(m_CurrentFile) << endl;
278 m_CurrentFile.clear();
279 StringOut(ss.str());
280 continue;
281 }
282
283 try
284 {
285 while(GetNextTest())
286 {
287 /* If the user specified a test through the command line, check this here */
288 if(!Configuration.GetTest().empty() && Configuration.GetTest() != m_CurrentTest)
289 continue;
290
291 {
292 auto_ptr<CTestInfo> TestInfo(new CTestInfo());
293 size_t UnderscorePosition;
294
295 /* Build the command line */
296 TestInfo->CommandLine = m_TestPath;
297 TestInfo->CommandLine += m_CurrentFile;
298 TestInfo->CommandLine += ' ';
299 TestInfo->CommandLine += AsciiToUnicode(m_CurrentTest);
300
301 /* Store the Module name */
302 UnderscorePosition = m_CurrentFile.find_last_of('_');
303
304 if(UnderscorePosition == m_CurrentFile.npos)
305 {
306 /* Use the entire name (without .exe extendion) */
307 UnderscorePosition = m_CurrentFile.find_first_of('.');
308 if(UnderscorePosition == m_CurrentFile.npos)
309 UnderscorePosition = m_CurrentFile.length();
310 }
311
312 TestInfo->Module = UnicodeToAscii(m_CurrentFile.substr(0, UnderscorePosition));
313
314 /* Store the test */
315 TestInfo->Test = m_CurrentTest;
316
317 return TestInfo.release();
318 }
319 }
320 }
321 catch(CTestException& e)
322 {
324
325 ss << "An exception occurred trying to list tests for: " << UnicodeToAscii(m_CurrentFile) << endl;
326 StringOut(ss.str());
327 StringOut(e.GetMessage());
328 StringOut("\n");
329 m_CurrentFile.clear();
330 m_ListString.clear();
331 }
332 }
333
334 return NULL;
335}
336
344void
346{
347 DWORD BytesAvailable;
348 stringstream ss, ssFinish;
350 float TotalTime;
351 string tailString;
352 CPipe Pipe;
353 char Buffer[1024];
354
355 ss << "Running Wine Test, Module: " << TestInfo->Module << ", Test: " << TestInfo->Test << endl;
356 StringOut(ss.str());
357
359
361
362 try
363 {
364 /* Execute the test */
366
367 /* Receive all the data from the pipe */
368 for (;;)
369 {
370 DWORD dwReadResult = Pipe.Read(Buffer, sizeof(Buffer) - 1, &BytesAvailable, ProcessActivityTimeout);
371 if (dwReadResult == ERROR_SUCCESS)
372 {
373 /* Output text through StringOut, even while the test is still running */
374 Buffer[BytesAvailable] = 0;
375 tailString = StringOut(tailString.append(string(Buffer)), false);
376
377 if (Configuration.DoSubmit())
378 TestInfo->Log += Buffer;
379 }
380 else if (dwReadResult == ERROR_BROKEN_PIPE)
381 {
382 // The process finished and has been terminated.
383 break;
384 }
385 else if (dwReadResult == WAIT_TIMEOUT)
386 {
387 // The process activity timeout above has elapsed without any new data.
388 TESTEXCEPTION("Timeout while waiting for the test process\n");
389 }
390 else
391 {
392 // An unexpected error.
393 TESTEXCEPTION("CPipe::Read failed for the test run\n");
394 }
395 }
396 }
397 catch(CTestException& e)
398 {
399 if(!tailString.empty())
400 StringOut(tailString);
401 tailString.clear();
402 StringOut(e.GetMessage());
403 TestInfo->Log += e.GetMessage();
404 }
405
406 /* Print what's left */
407 if(!tailString.empty())
408 StringOut(tailString);
409
410 TotalTime = ((float)GetTickCount() - StartTime)/1000;
411 ssFinish << "Test " << TestInfo->Test << " completed in ";
412 ssFinish << setprecision(2) << fixed << TotalTime << " seconds." << endl;
413 StringOut(ssFinish.str());
414 TestInfo->Log += ssFinish.str();
415}
416
420void
422{
424 auto_ptr<CWebService> WebService;
425 CTestInfo* TestInfo;
426 DWORD ErrorMode = 0;
427
428 /* The virtual test list is of course faster, so it should be preferred over
429 the journaled one.
430 Enable the journaled one only in case ...
431 - we're running under ReactOS (as the journal is only useful in conjunction with sysreg2)
432 - we shall keep information for Crash Recovery
433 - and the user didn't specify a module (then doing Crash Recovery doesn't really make sense) */
434 if(Configuration.IsReactOS() && Configuration.DoCrashRecovery() && Configuration.GetModule().empty())
435 {
436 /* Use a test list with a permanent journal */
437 TestList.reset(new CJournaledTestList(this));
438 }
439 else
440 {
441 /* Use the fast virtual test list with no additional overhead */
442 TestList.reset(new CVirtualTestList(this));
443 }
444
445 /* Initialize the Web Service interface if required */
446 if (Configuration.DoSubmit())
447 {
449 {
450 StringOut("[ROSAUTOTEST] Using libcurl\n");
451 WebService.reset(new CWebServiceLibCurl());
452 }
453 else
454 {
455 StringOut("[ROSAUTOTEST] Using wininet\n");
456 WebService.reset(new CWebServiceWinInet());
457 }
458 }
459
460 /* Disable error dialogs if we're running in non-interactive mode */
461 if(!Configuration.IsInteractive())
463
464 /* Get information for each test to run */
465 while((TestInfo = TestList->GetNextTestInfo()) != 0)
466 {
467 auto_ptr<CTestInfo> TestInfoPtr(TestInfo);
468
469 RunTest(TestInfo);
470
471 if(Configuration.DoSubmit() && !TestInfo->Log.empty())
472 WebService->Submit("wine", TestInfo);
473
474 StringOut("\n\n");
475 }
476
477 /* We're done with all tests. Finish this run */
478 if(Configuration.DoSubmit())
479 WebService->Finish("wine");
480
481 /* Restore the original error mode */
482 if(!Configuration.IsInteractive())
483 SetErrorMode(ErrorMode);
484}
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
basic_ostream< _CharT, _Traits > &_STLP_CALL endl(basic_ostream< _CharT, _Traits > &__os)
Definition: _ostream.h:357
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
static bool CanUseLibCurl()
bool GetNextFile()
Definition: CWineTest.cpp:58
PCHAR m_ListBuffer
Definition: CWineTest.h:13
bool GetNextTest()
Definition: CWineTest.cpp:207
wstring m_TestPath
Definition: CWineTest.h:17
void Run()
Definition: CWineTest.cpp:421
string m_CurrentTest
Definition: CWineTest.h:14
CTestInfo * GetNextTestInfo()
Definition: CWineTest.cpp:269
DWORD DoListCommand()
Definition: CWineTest.cpp:156
HANDLE m_hFind
Definition: CWineTest.h:11
wstring m_CurrentFile
Definition: CWineTest.h:15
void RunTest(CTestInfo *TestInfo)
Definition: CWineTest.cpp:345
std::string m_ListString
Definition: CWineTest.h:12
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 ARRAYSIZE(array)
Definition: filtermapper.c:47
#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:2168
UINT WINAPI GetWindowsDirectoryW(OUT LPWSTR lpBuffer, IN UINT uSize)
Definition: path.c:2271
ULONG WINAPI DECLSPEC_HOTPATCH GetTickCount(void)
Definition: sync.c:182
_ACRTIMP int __cdecl _wcsicmp(const wchar_t *, const wchar_t *)
Definition: wcs.c:164
_ACRTIMP char *__cdecl strchr(const char *, int)
Definition: string.c:3291
return ret
Definition: mutex.c:146
#define L(x)
Definition: resources.c:13
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
static LARGE_INTEGER StartTime
Definition: sys_arch.c:15
#define e
Definition: ke_i.h:82
#define PCHAR
Definition: match.c:90
char string[160]
Definition: util.h:11
WCHAR TestName[MAX_PATH]
Definition: main.cpp:13
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:59
#define TESTEXCEPTION(Message)
Definition: precomp.h:61
static float(__cdecl *square_half_float)(float x
#define SEM_FAILCRITICALERRORS
Definition: rtltypes.h:69
#define SEM_NOGPFAULTERRORBOX
Definition: rtltypes.h:70
short WCHAR
Definition: pedump.c:58
char CHAR
Definition: pedump.c:57
static int fd
Definition: io.c:51
__crt_unique_heap_ptr< wchar_t > const wstring(_malloc_crt_t(wchar_t, maxsize))
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
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
_Must_inspect_result_ _In_ WDFIOTARGET _In_opt_ WDFREQUEST _In_opt_ PWDF_MEMORY_DESCRIPTOR _In_opt_ PLONGLONG _In_opt_ PWDF_REQUEST_SEND_OPTIONS _Out_opt_ PULONG_PTR BytesRead
Definition: wdfiotarget.h:870
_In_ WDFMEMORY _Out_opt_ size_t * BufferSize
Definition: wdfmemory.h:254
_In_ WDFUSBPIPE Pipe
Definition: wdfusb.h:1741
#define ERROR_BROKEN_PIPE
Definition: winerror.h:305