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 Colin Finck <colin@reactos.org>
10 static const DWORD ListTimeout
= 10000;
13 * Constructs a CWineTest object.
15 CWineTest::CWineTest()
17 WCHAR WindowsDirectory
[MAX_PATH
];
19 /* Zero-initialize variables */
23 /* Set up m_TestPath */
24 if(!GetWindowsDirectoryW(WindowsDirectory
, MAX_PATH
))
25 FATAL("GetWindowsDirectoryW failed");
27 m_TestPath
= WindowsDirectory
;
28 m_TestPath
+= L
"\\bin\\";
32 * Destructs a CWineTest object.
34 CWineTest::~CWineTest()
44 * Gets the next module test file using the FindFirstFileW/FindNextFileW API.
47 * true if we found a next file, otherwise false.
50 CWineTest::GetNextFile()
52 bool FoundFile
= false;
55 /* Did we already begin searching for files? */
58 /* Then get the next file (if any) */
59 if(FindNextFileW(m_hFind
, &fd
))
64 /* Start searching for test files */
65 wstring FindPath
= m_TestPath
;
67 /* Did the user specify a module? */
68 if(Configuration
.GetModule().empty())
70 /* No module, so search for all files in that directory */
75 /* Search for files with the pattern "modulename_*" */
76 FindPath
+= Configuration
.GetModule();
77 FindPath
+= L
"_*.exe";
80 /* Search for the first file and check whether we got one */
81 m_hFind
= FindFirstFileW(FindPath
.c_str(), &fd
);
83 if(m_hFind
!= INVALID_HANDLE_VALUE
)
88 m_CurrentFile
= fd
.cFileName
;
94 * Executes the --list command of a module test file to get information about the available tests.
97 * The number of bytes we read into the m_ListBuffer member variable by capturing the output of the --list command.
100 CWineTest::DoListCommand()
102 DWORD BytesAvailable
;
107 /* Build the command line */
108 CommandLine
= m_TestPath
;
109 CommandLine
+= m_CurrentFile
;
110 CommandLine
+= L
" --list";
113 /* Start the process for getting all available tests */
114 CPipedProcess
Process(CommandLine
, Pipe
);
116 /* Wait till this process ended */
117 if(WaitForSingleObject(Process
.GetProcessHandle(), ListTimeout
) == WAIT_FAILED
)
118 FATAL("WaitForSingleObject failed for the test list\n");
121 /* Read the output data into a buffer */
122 if(!Pipe
.Peek(NULL
, 0, NULL
, &BytesAvailable
))
123 FATAL("CPipe::Peek failed for the test list\n");
125 /* Check if we got any */
130 ss
<< "The --list command did not return any data for " << UnicodeToAscii(m_CurrentFile
) << endl
;
135 m_ListBuffer
= new char[BytesAvailable
];
137 if(!Pipe
.Read(m_ListBuffer
, BytesAvailable
, &Temp
))
138 FATAL("CPipe::Read failed\n");
140 return BytesAvailable
;
144 * Gets the next test from m_ListBuffer, which was filled with information from the --list command.
147 * true if a next test was found, otherwise false.
150 CWineTest::GetNextTest()
153 static DWORD BufferSize
;
158 /* Perform the --list command */
159 BufferSize
= DoListCommand();
161 /* Move the pointer to the first test */
162 pStart
= strchr(m_ListBuffer
, '\n');
166 /* If we reach the buffer size, we finished analyzing the output of this test */
167 if(pStart
>= (m_ListBuffer
+ BufferSize
))
169 /* Clear m_CurrentFile to indicate that */
170 m_CurrentFile
.clear();
172 /* Also free the memory for the list buffer */
173 delete[] m_ListBuffer
;
179 /* Get start and end of this test name */
185 /* Store the test name */
186 m_CurrentTest
= string(pStart
, pEnd
);
188 /* Move the pointer to the next test */
195 * Interface to CTestList-derived classes for getting all information about the next test to be run.
198 * Returns a pointer to a CTestInfo object containing all available information about the next test.
201 CWineTest::GetNextTestInfo()
203 while(!m_CurrentFile
.empty() || GetNextFile())
207 /* If the user specified a test through the command line, check this here */
208 if(!Configuration
.GetTest().empty() && Configuration
.GetTest() != m_CurrentTest
)
212 auto_ptr
<CTestInfo
> TestInfo(new CTestInfo());
213 size_t UnderscorePosition
;
215 /* Build the command line */
216 TestInfo
->CommandLine
= m_TestPath
;
217 TestInfo
->CommandLine
+= m_CurrentFile
;
218 TestInfo
->CommandLine
+= ' ';
219 TestInfo
->CommandLine
+= AsciiToUnicode(m_CurrentTest
);
221 /* Store the Module name */
222 UnderscorePosition
= m_CurrentFile
.find_last_of('_');
224 if(UnderscorePosition
== m_CurrentFile
.npos
)
228 ss
<< "Invalid test file name: " << UnicodeToAscii(m_CurrentFile
) << endl
;
232 TestInfo
->Module
= UnicodeToAscii(m_CurrentFile
.substr(0, UnderscorePosition
));
235 TestInfo
->Test
= m_CurrentTest
;
237 return TestInfo
.release();
246 * Runs a Wine test and captures the output
249 * Pointer to a CTestInfo object containing information about the test.
250 * Will contain the test log afterwards if the user wants to submit data.
253 CWineTest::RunTest(CTestInfo
* TestInfo
)
255 DWORD BytesAvailable
;
256 stringstream ss
, ssFinish
;
263 ss
<< "Running Wine Test, Module: " << TestInfo
->Module
<< ", Test: " << TestInfo
->Test
<< endl
;
266 StartTime
= GetTickCount();
269 /* Execute the test */
270 CPipedProcess
Process(TestInfo
->CommandLine
, Pipe
);
272 /* Receive all the data from the pipe */
273 while(Pipe
.Read(Buffer
, sizeof(Buffer
) - 1, &BytesAvailable
) && BytesAvailable
)
275 /* Output text through StringOut, even while the test is still running */
276 Buffer
[BytesAvailable
] = 0;
277 tailString
= StringOut(tailString
.append(string(Buffer
)), false);
279 if(Configuration
.DoSubmit())
280 TestInfo
->Log
+= Buffer
;
282 if(GetLastError() != ERROR_BROKEN_PIPE
)
283 FATAL("CPipe::Read failed for the test run\n");
286 /* Print what's left */
287 if(!tailString
.empty())
288 StringOut(tailString
);
290 TotalTime
= ((float)GetTickCount() - StartTime
)/1000;
291 ssFinish
<< "Test " << TestInfo
->Test
<< " completed in ";
292 ssFinish
<< setprecision(2) << fixed
<< TotalTime
<< " seconds." << endl
;
293 StringOut(ssFinish
.str());
297 * Interface to other classes for running all desired Wine tests.
302 auto_ptr
<CTestList
> TestList
;
303 auto_ptr
<CWebService
> WebService
;
306 /* The virtual test list is of course faster, so it should be preferred over
308 Enable the journaled one only in case ...
309 - we're running under ReactOS (as the journal is only useful in conjunction with sysreg2)
310 - we shall keep information for Crash Recovery
311 - and the user didn't specify a module (then doing Crash Recovery doesn't really make sense) */
312 if(Configuration
.IsReactOS() && Configuration
.DoCrashRecovery() && Configuration
.GetModule().empty())
314 /* Use a test list with a permanent journal */
315 TestList
.reset(new CJournaledTestList(this));
319 /* Use the fast virtual test list with no additional overhead */
320 TestList
.reset(new CVirtualTestList(this));
323 /* Initialize the Web Service interface if required */
324 if(Configuration
.DoSubmit())
325 WebService
.reset(new CWebService());
327 /* Get information for each test to run */
328 while((TestInfo
= TestList
->GetNextTestInfo()) != 0)
330 auto_ptr
<CTestInfo
> TestInfoPtr(TestInfo
);
334 if(Configuration
.DoSubmit() && !TestInfo
->Log
.empty())
335 WebService
->Submit("wine", TestInfo
);