2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
4 * PURPOSE: Running Wine Tests automatically
5 * COPYRIGHT: Copyright 2008-2009 Colin Finck <colin@reactos.org>
11 * Runs a specific test for a specific module.
12 * If we get results for a test, they are submitted to the Web Service.
15 * Command line to run (should be the path to a module's test suite together with a parameter for the specified test)
18 * Handle to the Read Pipe set up in RunWineTests.
21 * Pointer to the StartupInfo structure set up in RunWineTests.
23 * @param GetSuiteIDData
24 * Pointer to the GetSuiteIDData structure set up in IntRunModuleTests.
27 * Pointer to the SubmitData structure set up in RunWineTests.
30 * TRUE if everything went well, FALSE otherwise.
33 IntRunTest(PWSTR CommandLine
, HANDLE hReadPipe
, LPSTARTUPINFOW StartupInfo
, PWINE_GETSUITEID_DATA GetSuiteIDData
, PWINE_SUBMIT_DATA SubmitData
)
35 BOOL BreakLoop
= FALSE
;
36 BOOL ReturnValue
= FALSE
;
38 DWORD LogAvailable
= 0;
40 DWORD LogPosition
= 0;
43 PROCESS_INFORMATION ProcessInfo
= {0};
47 /* Allocate one block for the log */
48 SubmitData
->Log
= HeapAlloc(hProcessHeap
, 0, BUFFER_BLOCKSIZE
);
49 LogAvailable
= BUFFER_BLOCKSIZE
;
50 LogLength
= BUFFER_BLOCKSIZE
;
53 /* Allocate a buffer with the exact size of the output string.
54 We have to output this string in one call to prevent a race condition, when another application also outputs a string over the debug line. */
55 Buffer
= HeapAlloc(hProcessHeap
, 0, 27 + strlen(GetSuiteIDData
->Module
) + 8 + strlen(GetSuiteIDData
->Test
) + 2);
56 sprintf(Buffer
, "Running Wine Test, Module: %s, Test: %s\n", GetSuiteIDData
->Module
, GetSuiteIDData
->Test
);
58 HeapFree(hProcessHeap
, 0, Buffer
);
61 /* Execute the test */
62 if(!CreateProcessW(NULL
, CommandLine
, NULL
, NULL
, TRUE
, NORMAL_PRIORITY_CLASS
, NULL
, NULL
, StartupInfo
, &ProcessInfo
))
64 StringOut("CreateProcessW for running the test failed\n");
68 /* Receive all the data from the pipe */
71 /* When the application finished, make sure that we peek the pipe one more time, so that we get all data.
72 If the following condition would be the while() condition, we might hit a race condition:
73 - We check for data with PeekNamedPipe -> no data available
74 - The application outputs its data and finishes
75 - WaitForSingleObject reports that the application has finished and we break the loop without receiving any data
77 if(WaitForSingleObject(ProcessInfo
.hProcess
, 0) == WAIT_OBJECT_0
)
80 if(!PeekNamedPipe(hReadPipe
, NULL
, 0, NULL
, &BytesAvailable
, NULL
))
82 StringOut("PeekNamedPipe failed for the test run\n");
88 /* There is data, so get it and output it */
89 Buffer
= HeapAlloc(hProcessHeap
, 0, BytesAvailable
+ 1);
91 if(!ReadFile(hReadPipe
, Buffer
, BytesAvailable
, &Temp
, NULL
))
93 StringOut("ReadFile failed for the test run\n");
97 /* Output all test output through StringOut, even while the test is still running */
98 Buffer
[BytesAvailable
] = 0;
101 if(AppOptions
.Submit
)
103 /* Also store it in the buffer */
104 if(BytesAvailable
> LogAvailable
)
106 /* Allocate enough new blocks to hold all available data */
107 Temp
= ((BytesAvailable
- LogAvailable
) / BUFFER_BLOCKSIZE
+ 1) * BUFFER_BLOCKSIZE
;
108 LogAvailable
+= Temp
;
110 SubmitData
->Log
= HeapReAlloc(hProcessHeap
, 0, SubmitData
->Log
, LogLength
);
113 memcpy(&SubmitData
->Log
[LogPosition
], Buffer
, BytesAvailable
);
114 LogPosition
+= BytesAvailable
;
115 LogAvailable
-= BytesAvailable
;
118 HeapFree(hProcessHeap
, 0, Buffer
);
124 if(AppOptions
.Submit
)
126 SubmitData
->Log
[LogLength
- LogAvailable
] = 0;
128 /* If we got any output, submit it to the web service */
131 /* We don't want to waste any ID's, so only request them if we can be sure that we have results to submit. */
133 /* Get a Test ID if we don't have one yet */
134 if(!SubmitData
->General
.TestID
)
136 SubmitData
->General
.TestID
= GetTestID(WineTest
);
138 if(!SubmitData
->General
.TestID
)
142 /* Get a Suite ID for this combination */
143 SubmitData
->General
.SuiteID
= GetSuiteID(WineTest
, GetSuiteIDData
);
145 if(!SubmitData
->General
.SuiteID
)
148 /* Submit the stuff */
149 Submit(WineTest
, SubmitData
);
159 HeapFree(hProcessHeap
, 0, Buffer
);
161 if(ProcessInfo
.hProcess
)
162 HeapFree(hProcessHeap
, 0, ProcessInfo
.hProcess
);
164 if(ProcessInfo
.hThread
)
165 HeapFree(hProcessHeap
, 0, ProcessInfo
.hThread
);
167 if(SubmitData
->General
.SuiteID
)
169 HeapFree(hProcessHeap
, 0, SubmitData
->General
.SuiteID
);
170 SubmitData
->General
.SuiteID
= NULL
;
175 HeapFree(hProcessHeap
, 0, SubmitData
->Log
);
176 SubmitData
->Log
= NULL
;
183 * Runs the desired tests for a specified module.
186 * The file name of the module's test suite.
189 * The full path to the file of the module's test suite.
192 * Handle to the Read Pipe set up in RunWineTests.
195 * Pointer to the StartupInfo structure set up in RunWineTests.
198 * Pointer to the SubmitData structure set up in RunWineTests.
201 * TRUE if everything went well, FALSE otherwise.
204 IntRunModuleTests(PWSTR File
, PWSTR FilePath
, HANDLE hReadPipe
, LPSTARTUPINFOW StartupInfo
, PWINE_SUBMIT_DATA SubmitData
)
206 BOOL ReturnValue
= FALSE
;
207 DWORD BytesAvailable
;
213 PROCESS_INFORMATION ProcessInfo
= {0};
216 WINE_GETSUITEID_DATA GetSuiteIDData
= {0};
218 /* Build the full command line */
219 FilePosition
= wcslen(FilePath
);
220 FilePath
[FilePosition
++] = ' ';
221 FilePath
[FilePosition
] = 0;
222 wcscat(FilePath
, L
"--list");
224 /* Find the underscore in the file name */
225 pUnderscore
= wcschr(File
, L
'_');
229 StringOut("Invalid test file name: ");
231 Length
= wcslen(File
);
232 Buffer
= HeapAlloc(hProcessHeap
, 0, Length
+ 1);
233 WideCharToMultiByte(CP_ACP
, 0, File
, Length
+ 1, Buffer
, Length
+ 1, NULL
, NULL
);
241 /* Store the tested module name */
242 Length
= pUnderscore
- File
;
243 GetSuiteIDData
.Module
= HeapAlloc(hProcessHeap
, 0, Length
+ 1);
244 WideCharToMultiByte(CP_ACP
, 0, File
, Length
, GetSuiteIDData
.Module
, Length
, NULL
, NULL
);
245 GetSuiteIDData
.Module
[Length
] = 0;
247 /* Start the process for getting all available tests */
248 if(!CreateProcessW(NULL
, FilePath
, NULL
, NULL
, TRUE
, NORMAL_PRIORITY_CLASS
, NULL
, NULL
, StartupInfo
, &ProcessInfo
))
250 StringOut("CreateProcessW for getting the available tests failed\n");
254 /* Wait till this process ended */
255 if(WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
) == WAIT_FAILED
)
257 StringOut("WaitForSingleObject failed for the test list\n");
261 /* Read the output data into a buffer */
262 if(!PeekNamedPipe(hReadPipe
, NULL
, 0, NULL
, &BytesAvailable
, NULL
))
264 StringOut("PeekNamedPipe failed for the test list\n");
268 /* Check if we got any */
271 StringOut("The --list command did not return any data for ");
273 Length
= wcslen(File
);
274 Buffer
= HeapAlloc(hProcessHeap
, 0, Length
+ 1);
275 WideCharToMultiByte(CP_ACP
, 0, File
, Length
+ 1, Buffer
, Length
+ 1, NULL
, NULL
);
284 Buffer
= HeapAlloc(hProcessHeap
, 0, BytesAvailable
);
286 if(!ReadFile(hReadPipe
, Buffer
, BytesAvailable
, &Temp
, NULL
))
288 StringOut("ReadFile failed\n");
292 /* Jump to the first available test */
293 pStart
= strchr(Buffer
, '\n');
296 while(pStart
< (Buffer
+ BytesAvailable
))
298 /* Get start and end of this test name */
304 /* Store the test name */
305 GetSuiteIDData
.Test
= HeapAlloc(hProcessHeap
, 0, pEnd
- pStart
+ 1);
306 memcpy(GetSuiteIDData
.Test
, pStart
, pEnd
- pStart
);
307 GetSuiteIDData
.Test
[pEnd
- pStart
] = 0;
309 /* If the user gave us a test to run, we check whether the module's test suite really provides this test. */
310 if(!AppOptions
.Test
|| (AppOptions
.Test
&& !strcmp(AppOptions
.Test
, GetSuiteIDData
.Test
)))
312 /* Build the command line for this test */
313 Length
= MultiByteToWideChar(CP_ACP
, 0, pStart
, pEnd
- pStart
, NULL
, 0);
314 MultiByteToWideChar(CP_ACP
, 0, pStart
, pEnd
- pStart
, &FilePath
[FilePosition
], Length
* sizeof(WCHAR
));
315 FilePath
[FilePosition
+ Length
] = 0;
317 if(!IntRunTest(FilePath
, hReadPipe
, StartupInfo
, &GetSuiteIDData
, SubmitData
))
322 HeapFree(hProcessHeap
, 0, GetSuiteIDData
.Test
);
323 GetSuiteIDData
.Test
= NULL
;
325 /* Move to the next test */
332 if(GetSuiteIDData
.Module
)
333 HeapFree(hProcessHeap
, 0, GetSuiteIDData
.Module
);
335 if(GetSuiteIDData
.Test
)
336 HeapFree(hProcessHeap
, 0, GetSuiteIDData
.Test
);
339 HeapFree(hProcessHeap
, 0, Buffer
);
341 if(ProcessInfo
.hProcess
)
342 CloseHandle(ProcessInfo
.hProcess
);
344 if(ProcessInfo
.hThread
)
345 CloseHandle(ProcessInfo
.hThread
);
351 * Runs the Wine tests according to the options specified by the parameters.
354 * TRUE if everything went well, FALSE otherwise.
359 BOOL ReturnValue
= FALSE
;
360 GENERAL_FINISH_DATA FinishData
;
362 HANDLE hReadPipe
= NULL
;
363 HANDLE hWritePipe
= NULL
;
364 SECURITY_ATTRIBUTES SecurityAttributes
;
365 STARTUPINFOW StartupInfo
= {0};
367 WCHAR FilePath
[MAX_PATH
];
369 WINE_SUBMIT_DATA SubmitData
= { {0} };
371 /* Create a pipe for getting the output of the tests */
372 SecurityAttributes
.nLength
= sizeof(SecurityAttributes
);
373 SecurityAttributes
.bInheritHandle
= TRUE
;
374 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
376 if(!CreatePipe(&hReadPipe
, &hWritePipe
, &SecurityAttributes
, 0))
378 StringOut("CreatePipe failed\n");
382 StartupInfo
.cb
= sizeof(StartupInfo
);
383 StartupInfo
.dwFlags
= STARTF_USESTDHANDLES
;
384 StartupInfo
.hStdOutput
= hWritePipe
;
386 /* Build the path for finding the tests */
387 if(GetWindowsDirectoryW(FilePath
, MAX_PATH
) > MAX_PATH
- 60)
389 StringOut("Windows directory path is too long\n");
393 wcscat(FilePath
, L
"\\bin\\");
394 PathPosition
= wcslen(FilePath
);
396 if(AppOptions
.Module
)
398 /* Find a test belonging to this module */
399 wcscat(FilePath
, AppOptions
.Module
);
400 wcscat(FilePath
, L
"_*.exe");
405 wcscat(FilePath
, L
"*.exe");
408 hFind
= FindFirstFileW(FilePath
, &fd
);
410 if(hFind
== INVALID_HANDLE_VALUE
)
412 StringOut("FindFirstFileW failed\n");
419 /* Build the full path to the test suite */
420 wcscpy(&FilePath
[PathPosition
], fd
.cFileName
);
423 if(!IntRunModuleTests(fd
.cFileName
, FilePath
, hReadPipe
, &StartupInfo
, &SubmitData
))
426 while(FindNextFileW(hFind
, &fd
));
428 /* Close this test run if necessary */
429 if(SubmitData
.General
.TestID
)
431 FinishData
.TestID
= SubmitData
.General
.TestID
;
433 if(!Finish(WineTest
, &FinishData
))
440 if(SubmitData
.General
.TestID
)
441 HeapFree(hProcessHeap
, 0, SubmitData
.General
.TestID
);
443 if(hFind
&& hFind
!= INVALID_HANDLE_VALUE
)
447 CloseHandle(hReadPipe
);
450 CloseHandle(hWritePipe
);