ReactOS  0.4.14-dev-323-g6fe6a88
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  try
217  {
218  while(GetNextTest())
219  {
220  /* If the user specified a test through the command line, check this here */
222  continue;
223 
224  {
225  auto_ptr<CTestInfo> TestInfo(new CTestInfo());
226  size_t UnderscorePosition;
227 
228  /* Build the command line */
229  TestInfo->CommandLine = m_TestPath;
230  TestInfo->CommandLine += m_CurrentFile;
231  TestInfo->CommandLine += ' ';
233 
234  /* Store the Module name */
235  UnderscorePosition = m_CurrentFile.find_last_of('_');
236 
237  if(UnderscorePosition == m_CurrentFile.npos)
238  {
240 
241  ss << "Invalid test file name: " << UnicodeToAscii(m_CurrentFile) << endl;
242  SSEXCEPTION;
243  }
244 
245  TestInfo->Module = UnicodeToAscii(m_CurrentFile.substr(0, UnderscorePosition));
246 
247  /* Store the test */
248  TestInfo->Test = m_CurrentTest;
249 
250  return TestInfo.release();
251  }
252  }
253  }
254  catch(CTestException& e)
255  {
257 
258  ss << "An exception occurred trying to list tests for: " << UnicodeToAscii(m_CurrentFile) << endl;
259  StringOut(ss.str());
260  StringOut(e.GetMessage());
261  StringOut("\n");
262  m_CurrentFile.clear();
263  delete[] m_ListBuffer;
264  }
265  }
266 
267  return NULL;
268 }
269 
277 void
279 {
280  DWORD BytesAvailable;
281  stringstream ss, ssFinish;
283  float TotalTime;
284  string tailString;
285  CPipe Pipe;
286  char Buffer[1024];
287 
288  ss << "Running Wine Test, Module: " << TestInfo->Module << ", Test: " << TestInfo->Test << endl;
289  StringOut(ss.str());
290 
292 
293  try
294  {
295  /* Execute the test */
296  CPipedProcess Process(TestInfo->CommandLine, Pipe);
297 
298  /* Receive all the data from the pipe */
299  for (;;)
300  {
301  DWORD dwReadResult = Pipe.Read(Buffer, sizeof(Buffer) - 1, &BytesAvailable, ProcessActivityTimeout);
302  if (dwReadResult == ERROR_SUCCESS)
303  {
304  /* Output text through StringOut, even while the test is still running */
305  Buffer[BytesAvailable] = 0;
306  tailString = StringOut(tailString.append(string(Buffer)), false);
307 
308  if (Configuration.DoSubmit())
309  TestInfo->Log += Buffer;
310  }
311  else if (dwReadResult == ERROR_BROKEN_PIPE)
312  {
313  // The process finished and has been terminated.
314  break;
315  }
316  else if (dwReadResult == WAIT_TIMEOUT)
317  {
318  // The process activity timeout above has elapsed without any new data.
319  TESTEXCEPTION("Timeout while waiting for the test process\n");
320  }
321  else
322  {
323  // An unexpected error.
324  TESTEXCEPTION("CPipe::Read failed for the test run\n");
325  }
326  }
327  }
328  catch(CTestException& e)
329  {
330  if(!tailString.empty())
331  StringOut(tailString);
332  tailString.clear();
333  StringOut(e.GetMessage());
334  TestInfo->Log += e.GetMessage();
335  }
336 
337  /* Print what's left */
338  if(!tailString.empty())
339  StringOut(tailString);
340 
341  TotalTime = ((float)GetTickCount() - StartTime)/1000;
342  ssFinish << "Test " << TestInfo->Test << " completed in ";
343  ssFinish << setprecision(2) << fixed << TotalTime << " seconds." << endl;
344  StringOut(ssFinish.str());
345  TestInfo->Log += ssFinish.str();
346 }
347 
351 void
353 {
355  auto_ptr<CWebService> WebService;
356  CTestInfo* TestInfo;
357  DWORD ErrorMode;
358 
359  /* The virtual test list is of course faster, so it should be preferred over
360  the journaled one.
361  Enable the journaled one only in case ...
362  - we're running under ReactOS (as the journal is only useful in conjunction with sysreg2)
363  - we shall keep information for Crash Recovery
364  - and the user didn't specify a module (then doing Crash Recovery doesn't really make sense) */
366  {
367  /* Use a test list with a permanent journal */
368  TestList.reset(new CJournaledTestList(this));
369  }
370  else
371  {
372  /* Use the fast virtual test list with no additional overhead */
373  TestList.reset(new CVirtualTestList(this));
374  }
375 
376  /* Initialize the Web Service interface if required */
377  if(Configuration.DoSubmit())
378  WebService.reset(new CWebService());
379 
380  /* Disable error dialogs if we're running in non-interactive mode */
383 
384  /* Get information for each test to run */
385  while((TestInfo = TestList->GetNextTestInfo()) != 0)
386  {
387  auto_ptr<CTestInfo> TestInfoPtr(TestInfo);
388 
389  RunTest(TestInfo);
390 
391  if(Configuration.DoSubmit() && !TestInfo->Log.empty())
392  WebService->Submit("wine", TestInfo);
393 
394  StringOut("\n\n");
395  }
396 
397  /* We're done with all tests. Finish this run */
398  if(Configuration.DoSubmit())
399  WebService->Finish("wine");
400 
401  /* Restore the original error mode */
403  SetErrorMode(ErrorMode);
404 }
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:445
static const DWORD ListTimeout
Definition: CWineTest.cpp:10
bool DoSubmit() const
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
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:278
UINT WINAPI GetWindowsDirectoryW(OUT LPWSTR lpBuffer, IN UINT uSize)
Definition: path.c:2351
bool DoCrashRecovery() const
void Run()
Definition: CWineTest.cpp:352
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
#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
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
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:412
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