ReactOS  0.4.15-dev-439-g292f67a
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 
10 static 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.
14 static 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 
60 bool
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) */
70  if(FindNextFileW(m_hFind, &fd))
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 
110 DWORD
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 
160 bool
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;
185  m_ListBuffer = NULL;
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 
211 CTestInfo*
213 {
214  while(!m_CurrentFile.empty() || GetNextFile())
215  {
216  /* The user asked for a list of all modules */
218  {
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 */
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 += ' ';
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;
252  SSEXCEPTION;
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 
287 void
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 
303  try
304  {
305  /* Execute the test */
306  CPipedProcess Process(TestInfo->CommandLine, Pipe);
307 
308  /* Receive all the data from the pipe */
309  for (;;)
310  {
311  DWORD dwReadResult = Pipe.Read(Buffer, sizeof(Buffer) - 1, &BytesAvailable, ProcessActivityTimeout);
312  if (dwReadResult == ERROR_SUCCESS)
313  {
314  /* Output text through StringOut, even while the test is still running */
315  Buffer[BytesAvailable] = 0;
316  tailString = StringOut(tailString.append(string(Buffer)), false);
317 
318  if (Configuration.DoSubmit())
319  TestInfo->Log += Buffer;
320  }
321  else if (dwReadResult == ERROR_BROKEN_PIPE)
322  {
323  // The process finished and has been terminated.
324  break;
325  }
326  else if (dwReadResult == WAIT_TIMEOUT)
327  {
328  // The process activity timeout above has elapsed without any new data.
329  TESTEXCEPTION("Timeout while waiting for the test process\n");
330  }
331  else
332  {
333  // An unexpected error.
334  TESTEXCEPTION("CPipe::Read failed for the test run\n");
335  }
336  }
337  }
338  catch(CTestException& e)
339  {
340  if(!tailString.empty())
341  StringOut(tailString);
342  tailString.clear();
343  StringOut(e.GetMessage());
344  TestInfo->Log += e.GetMessage();
345  }
346 
347  /* Print what's left */
348  if(!tailString.empty())
349  StringOut(tailString);
350 
351  TotalTime = ((float)GetTickCount() - StartTime)/1000;
352  ssFinish << "Test " << TestInfo->Test << " completed in ";
353  ssFinish << setprecision(2) << fixed << TotalTime << " seconds." << endl;
354  StringOut(ssFinish.str());
355  TestInfo->Log += ssFinish.str();
356 }
357 
361 void
363 {
365  auto_ptr<CWebService> WebService;
366  CTestInfo* TestInfo;
367  DWORD ErrorMode;
368 
369  /* The virtual test list is of course faster, so it should be preferred over
370  the journaled one.
371  Enable the journaled one only in case ...
372  - we're running under ReactOS (as the journal is only useful in conjunction with sysreg2)
373  - we shall keep information for Crash Recovery
374  - and the user didn't specify a module (then doing Crash Recovery doesn't really make sense) */
376  {
377  /* Use a test list with a permanent journal */
378  TestList.reset(new CJournaledTestList(this));
379  }
380  else
381  {
382  /* Use the fast virtual test list with no additional overhead */
383  TestList.reset(new CVirtualTestList(this));
384  }
385 
386  /* Initialize the Web Service interface if required */
387  if(Configuration.DoSubmit())
388  WebService.reset(new CWebService());
389 
390  /* Disable error dialogs if we're running in non-interactive mode */
393 
394  /* Get information for each test to run */
395  while((TestInfo = TestList->GetNextTestInfo()) != 0)
396  {
397  auto_ptr<CTestInfo> TestInfoPtr(TestInfo);
398 
399  RunTest(TestInfo);
400 
401  if(Configuration.DoSubmit() && !TestInfo->Log.empty())
402  WebService->Submit("wine", TestInfo);
403 
404  StringOut("\n\n");
405  }
406 
407  /* We're done with all tests. Finish this run */
408  if(Configuration.DoSubmit())
409  WebService->Finish("wine");
410 
411  /* Restore the original error mode */
413  SetErrorMode(ErrorMode);
414 }
signed char * PCHAR
Definition: retypes.h:7
#define TESTEXCEPTION(Message)
Definition: precomp.h:59
BOOL WINAPI FindNextFileW(IN HANDLE hFindFile, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:382
#define FATAL(Message)
Definition: precomp.h:57
UINT WINAPI SetErrorMode(IN UINT uMode)
Definition: except.c:753
void Finish(const char *TestType)
#define ERROR_SUCCESS
Definition: deptool.c:10
#define SEM_FAILCRITICALERRORS
Definition: rtltypes.h:69
HANDLE m_hFind
Definition: CWineTest.h:11
static const DWORD ProcessActivityTimeout
Definition: CWineTest.cpp:14
void Submit(const char *TestType, CTestInfo *TestInfo)
_String str() const
Definition: _sstream.h:230
Definition: CPipe.h:9
DWORD WINAPI GetTickCount(VOID)
Definition: time.c:455
static const DWORD ListTimeout
Definition: CWineTest.cpp:10
bool DoSubmit() const
#define INVALID_HANDLE_VALUE
Definition: compat.h:400
static int fd
Definition: io.c:51
_STLP_PRIV _Ios_Manip_1< streamsize > _STLP_CALL setprecision(int __n)
Definition: _iomanip.h:119
static LARGE_INTEGER StartTime
Definition: sys_arch.c:18
void RunTest(CTestInfo *TestInfo)
Definition: CWineTest.cpp:288
UINT WINAPI GetWindowsDirectoryW(OUT LPWSTR lpBuffer, IN UINT uSize)
Definition: path.c:2351
bool DoCrashRecovery() const
void Run()
Definition: CWineTest.cpp:362
string Log
Definition: CTestInfo.h:14
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
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
void reset(_Tp *__px=0) _STLP_NOTHROW
Definition: _auto_ptr.h:59
bool IsReactOS() const
#define e
Definition: ke_i.h:82
_Tp * release() _STLP_NOTHROW
Definition: _auto_ptr.h:53
wstring m_CurrentFile
Definition: CWineTest.h:14
wstring m_TestPath
Definition: CWineTest.h:16
smooth NULL
Definition: ftsmooth.c:416
Definition: bufpool.h:45
DWORD Read(PVOID Buffer, DWORD NumberOfBytesToRead, PDWORD NumberOfBytesRead, DWORD TimeoutMilliseconds)
Definition: CPipe.cpp:160
EH_STD::__list__< TestClass, eh_allocator(TestClass) > TestList
Definition: test_list.cpp:31
wstring AsciiToUnicode(const char *AsciiString)
Definition: tools.cpp:220
string UnicodeToAscii(PCWSTR UnicodeString)
Definition: tools.cpp:261
__wchar_t WCHAR
Definition: xmlstorage.h:180
const string & GetTest() const
bool ListModulesOnly() const
#define MAX_PATH
Definition: compat.h:26
const wstring & GetModule() const
#define BufferSize
Definition: classpnp.h:419
unsigned long DWORD
Definition: ntddk_ex.h:95
#define WAIT_FAILED
Definition: winbase.h:394
#define ERROR_BROKEN_PIPE
Definition: winerror.h:183
string StringOut(const string &String, bool forcePrint=true)
Definition: tools.cpp:96
wstring CommandLine
Definition: CTestInfo.h:11
string m_CurrentTest
Definition: CWineTest.h:13
#define WAIT_TIMEOUT
Definition: dderror.h:14
string Module
Definition: CTestInfo.h:12
static const WCHAR L[]
Definition: oid.c:1250
bool Peek(PVOID Buffer, DWORD BufferSize, PDWORD BytesRead, PDWORD TotalBytesAvailable)
Definition: CPipe.cpp:129
CConfiguration Configuration
Definition: main.cpp:11
#define SEM_NOGPFAULTERRORBOX
Definition: rtltypes.h:70
char string[160]
Definition: util.h:11
#define SSEXCEPTION
Definition: precomp.h:58
bool GetNextTest()
Definition: CWineTest.cpp:161
static float(__cdecl *square_half_float)(float x
friend class CVirtualTestList
Definition: CTest.h:18
friend class CJournaledTestList
Definition: CTest.h:17
char * strchr(const char *String, int ch)
Definition: utclib.c:501
_Must_inspect_result_ _In_ PLARGE_INTEGER _In_ PLARGE_INTEGER _In_ ULONG _In_ PFILE_OBJECT _In_ PVOID Process
Definition: fsrtlfuncs.h:219
PCHAR m_ListBuffer
Definition: CWineTest.h:12
basic_stringstream< char, char_traits< char >, allocator< char > > stringstream
Definition: _iosfwd.h:128
CTestInfo * GetNextTestInfo()
Definition: CWineTest.cpp:212
DWORD DoListCommand()
Definition: CWineTest.cpp:111
string Test
Definition: CTestInfo.h:13
bool GetNextFile()
Definition: CWineTest.cpp:61
#define ss
Definition: i386-dis.c:432
#define INFINITE
Definition: serial.h:102
#define GetEnvironmentVariableW(x, y, z)
Definition: compat.h:421
bool IsInteractive() const
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502