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 functions for handling Wine tests
5 * COPYRIGHT: Copyright 2009-2015 Colin Finck <colin@reactos.org>
10 static const DWORD ListTimeout
= 10000;
13 * Constructs a CWineTest object.
15 CWineTest::CWineTest()
16 : m_hFind(NULL
), m_ListBuffer(NULL
)
18 WCHAR wszDirectory
[MAX_PATH
];
20 /* Set up m_TestPath */
21 if (GetEnvironmentVariableW(L
"ROSAUTOTEST_DIR", wszDirectory
, MAX_PATH
))
23 m_TestPath
= wszDirectory
;
24 if (*m_TestPath
.rbegin() != L
'\\')
29 if (!GetWindowsDirectoryW(wszDirectory
, MAX_PATH
))
30 FATAL("GetWindowsDirectoryW failed");
32 m_TestPath
= wszDirectory
;
33 m_TestPath
+= L
"\\bin\\";
38 * Destructs a CWineTest object.
40 CWineTest::~CWineTest()
50 * Gets the next module test file using the FindFirstFileW/FindNextFileW API.
53 * true if we found a next file, otherwise false.
56 CWineTest::GetNextFile()
58 bool FoundFile
= false;
61 /* Did we already begin searching for files? */
64 /* Then get the next file (if any) */
65 if(FindNextFileW(m_hFind
, &fd
))
70 /* Start searching for test files */
71 wstring FindPath
= m_TestPath
;
73 /* Did the user specify a module? */
74 if(Configuration
.GetModule().empty())
76 /* No module, so search for all files in that directory */
81 /* Search for files with the pattern "modulename_*" */
82 FindPath
+= Configuration
.GetModule();
83 FindPath
+= L
"_*.exe";
86 /* Search for the first file and check whether we got one */
87 m_hFind
= FindFirstFileW(FindPath
.c_str(), &fd
);
89 if(m_hFind
!= INVALID_HANDLE_VALUE
)
94 m_CurrentFile
= fd
.cFileName
;
100 * Executes the --list command of a module test file to get information about the available tests.
103 * The number of bytes we read into the m_ListBuffer member variable by capturing the output of the --list command.
106 CWineTest::DoListCommand()
108 DWORD BytesAvailable
;
113 /* Build the command line */
114 CommandLine
= m_TestPath
;
115 CommandLine
+= m_CurrentFile
;
116 CommandLine
+= L
" --list";
119 /* Start the process for getting all available tests */
120 CPipedProcess
Process(CommandLine
, Pipe
);
122 /* Wait till this process ended */
123 if(WaitForSingleObject(Process
.GetProcessHandle(), ListTimeout
) == WAIT_FAILED
)
124 FATAL("WaitForSingleObject failed for the test list\n");
127 /* Read the output data into a buffer */
128 if(!Pipe
.Peek(NULL
, 0, NULL
, &BytesAvailable
))
129 FATAL("CPipe::Peek failed for the test list\n");
131 /* Check if we got any */
136 ss
<< "The --list command did not return any data for " << UnicodeToAscii(m_CurrentFile
) << endl
;
141 m_ListBuffer
= new char[BytesAvailable
];
143 if(!Pipe
.Read(m_ListBuffer
, BytesAvailable
, &Temp
))
144 FATAL("CPipe::Read failed\n");
146 return BytesAvailable
;
150 * Gets the next test from m_ListBuffer, which was filled with information from the --list command.
153 * true if a next test was found, otherwise false.
156 CWineTest::GetNextTest()
159 static DWORD BufferSize
;
164 /* Perform the --list command */
165 BufferSize
= DoListCommand();
167 /* Move the pointer to the first test */
168 pStart
= strchr(m_ListBuffer
, '\n');
172 /* If we reach the buffer size, we finished analyzing the output of this test */
173 if(pStart
>= (m_ListBuffer
+ BufferSize
))
175 /* Clear m_CurrentFile to indicate that */
176 m_CurrentFile
.clear();
178 /* Also free the memory for the list buffer */
179 delete[] m_ListBuffer
;
185 /* Get start and end of this test name */
191 /* Store the test name */
192 m_CurrentTest
= string(pStart
, pEnd
);
194 /* Move the pointer to the next test */
201 * Interface to CTestList-derived classes for getting all information about the next test to be run.
204 * Returns a pointer to a CTestInfo object containing all available information about the next test.
207 CWineTest::GetNextTestInfo()
209 while(!m_CurrentFile
.empty() || GetNextFile())
213 /* If the user specified a test through the command line, check this here */
214 if(!Configuration
.GetTest().empty() && Configuration
.GetTest() != m_CurrentTest
)
218 auto_ptr
<CTestInfo
> TestInfo(new CTestInfo());
219 size_t UnderscorePosition
;
221 /* Build the command line */
222 TestInfo
->CommandLine
= m_TestPath
;
223 TestInfo
->CommandLine
+= m_CurrentFile
;
224 TestInfo
->CommandLine
+= ' ';
225 TestInfo
->CommandLine
+= AsciiToUnicode(m_CurrentTest
);
227 /* Store the Module name */
228 UnderscorePosition
= m_CurrentFile
.find_last_of('_');
230 if(UnderscorePosition
== m_CurrentFile
.npos
)
234 ss
<< "Invalid test file name: " << UnicodeToAscii(m_CurrentFile
) << endl
;
238 TestInfo
->Module
= UnicodeToAscii(m_CurrentFile
.substr(0, UnderscorePosition
));
241 TestInfo
->Test
= m_CurrentTest
;
243 return TestInfo
.release();
252 * Runs a Wine test and captures the output
255 * Pointer to a CTestInfo object containing information about the test.
256 * Will contain the test log afterwards if the user wants to submit data.
259 CWineTest::RunTest(CTestInfo
* TestInfo
)
261 DWORD BytesAvailable
;
262 stringstream ss
, ssFinish
;
269 ss
<< "Running Wine Test, Module: " << TestInfo
->Module
<< ", Test: " << TestInfo
->Test
<< endl
;
272 StartTime
= GetTickCount();
275 /* Execute the test */
276 CPipedProcess
Process(TestInfo
->CommandLine
, Pipe
);
278 /* Receive all the data from the pipe */
279 while(Pipe
.Read(Buffer
, sizeof(Buffer
) - 1, &BytesAvailable
) && BytesAvailable
)
281 /* Output text through StringOut, even while the test is still running */
282 Buffer
[BytesAvailable
] = 0;
283 tailString
= StringOut(tailString
.append(string(Buffer
)), false);
285 if(Configuration
.DoSubmit())
286 TestInfo
->Log
+= Buffer
;
288 if(GetLastError() != ERROR_BROKEN_PIPE
)
289 FATAL("CPipe::Read failed for the test run\n");
292 /* Print what's left */
293 if(!tailString
.empty())
294 StringOut(tailString
);
296 TotalTime
= ((float)GetTickCount() - StartTime
)/1000;
297 ssFinish
<< "Test " << TestInfo
->Test
<< " completed in ";
298 ssFinish
<< setprecision(2) << fixed
<< TotalTime
<< " seconds." << endl
;
299 StringOut(ssFinish
.str());
303 * Interface to other classes for running all desired Wine tests.
308 auto_ptr
<CTestList
> TestList
;
309 auto_ptr
<CWebService
> WebService
;
312 /* The virtual test list is of course faster, so it should be preferred over
314 Enable the journaled one only in case ...
315 - we're running under ReactOS (as the journal is only useful in conjunction with sysreg2)
316 - we shall keep information for Crash Recovery
317 - and the user didn't specify a module (then doing Crash Recovery doesn't really make sense) */
318 if(Configuration
.IsReactOS() && Configuration
.DoCrashRecovery() && Configuration
.GetModule().empty())
320 /* Use a test list with a permanent journal */
321 TestList
.reset(new CJournaledTestList(this));
325 /* Use the fast virtual test list with no additional overhead */
326 TestList
.reset(new CVirtualTestList(this));
329 /* Initialize the Web Service interface if required */
330 if(Configuration
.DoSubmit())
331 WebService
.reset(new CWebService());
333 /* Get information for each test to run */
334 while((TestInfo
= TestList
->GetNextTestInfo()) != 0)
336 auto_ptr
<CTestInfo
> TestInfoPtr(TestInfo
);
340 if(Configuration
.DoSubmit() && !TestInfo
->Log
.empty())
341 WebService
->Submit("wine", TestInfo
);