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
;
37 DWORD LogAvailable
= 0;
39 DWORD LogPosition
= 0;
42 PROCESS_INFORMATION ProcessInfo
;
46 /* Allocate one block for the log */
47 SubmitData
->Log
= HeapAlloc(hProcessHeap
, 0, BUFFER_BLOCKSIZE
);
48 LogAvailable
= BUFFER_BLOCKSIZE
;
49 LogLength
= BUFFER_BLOCKSIZE
;
52 /* Execute the test */
53 StringOut("Running Wine Test, Module: ");
54 StringOut(GetSuiteIDData
->Module
);
55 StringOut(", Test: ");
56 StringOut(GetSuiteIDData
->Test
);
59 if(!CreateProcessW(NULL
, CommandLine
, NULL
, NULL
, TRUE
, NORMAL_PRIORITY_CLASS
, NULL
, NULL
, StartupInfo
, &ProcessInfo
))
61 StringOut("CreateProcessW for running the test failed\n");
65 /* Receive all the data from the pipe */
68 /* When the application finished, make sure that we peek the pipe one more time, so that we get all data.
69 If the following condition would be the while() condition, we might hit a race condition:
70 - We check for data with PeekNamedPipe -> no data available
71 - The application outputs its data and finishes
72 - WaitForSingleObject reports that the application has finished and we break the loop without receiving any data
74 if(WaitForSingleObject(ProcessInfo
.hProcess
, 0) == WAIT_OBJECT_0
)
77 if(!PeekNamedPipe(hReadPipe
, NULL
, 0, NULL
, &BytesAvailable
, NULL
))
79 StringOut("PeekNamedPipe failed for the test run\n");
85 /* There is data, so get it and output it */
86 Buffer
= HeapAlloc(hProcessHeap
, 0, BytesAvailable
+ 1);
88 if(!ReadFile(hReadPipe
, Buffer
, BytesAvailable
, &Temp
, NULL
))
90 StringOut("ReadFile failed for the test run\n");
94 /* Output all test output through StringOut, even while the test is still running */
95 Buffer
[BytesAvailable
] = 0;
100 /* Also store it in the buffer */
101 if(BytesAvailable
> LogAvailable
)
103 /* Allocate enough new blocks to hold all available data */
104 Temp
= ((BytesAvailable
- LogAvailable
) / BUFFER_BLOCKSIZE
+ 1) * BUFFER_BLOCKSIZE
;
105 LogAvailable
+= Temp
;
107 SubmitData
->Log
= HeapReAlloc(hProcessHeap
, 0, SubmitData
->Log
, LogLength
);
110 memcpy(&SubmitData
->Log
[LogPosition
], Buffer
, BytesAvailable
);
111 LogPosition
+= BytesAvailable
;
112 LogAvailable
-= BytesAvailable
;
115 HeapFree(hProcessHeap
, 0, Buffer
);
120 if(AppOptions
.Submit
)
122 SubmitData
->Log
[LogLength
- LogAvailable
] = 0;
124 /* If we got any output, submit it to the web service */
127 /* We don't want to waste any ID's, so only request them if we can be sure that we have results to submit. */
129 /* Get a Test ID if we don't have one yet */
130 if(!SubmitData
->General
.TestID
)
132 SubmitData
->General
.TestID
= GetTestID(WineTest
);
134 if(!SubmitData
->General
.TestID
)
138 /* Get a Suite ID for this combination */
139 SubmitData
->General
.SuiteID
= GetSuiteID(WineTest
, GetSuiteIDData
);
141 if(!SubmitData
->General
.SuiteID
)
144 /* Submit the stuff */
145 Submit(WineTest
, SubmitData
);
148 HeapFree(hProcessHeap
, 0, SubmitData
->General
.SuiteID
);
152 HeapFree(hProcessHeap
, 0, SubmitData
->Log
);
161 * Runs the desired tests for a specified module.
164 * The file name of the module's test suite.
167 * The full path to the file of the module's test suite.
170 * Handle to the Read Pipe set up in RunWineTests.
173 * Pointer to the StartupInfo structure set up in RunWineTests.
176 * Pointer to the SubmitData structure set up in RunWineTests.
179 * TRUE if everything went well, FALSE otherwise.
182 IntRunModuleTests(PWSTR File
, PWSTR FilePath
, HANDLE hReadPipe
, LPSTARTUPINFOW StartupInfo
, PWINE_SUBMIT_DATA SubmitData
)
184 DWORD BytesAvailable
;
190 PROCESS_INFORMATION ProcessInfo
;
192 WINE_GETSUITEID_DATA GetSuiteIDData
;
194 /* Build the full command line */
195 FilePosition
= wcslen(FilePath
);
196 FilePath
[FilePosition
++] = ' ';
197 FilePath
[FilePosition
] = 0;
198 wcscat(FilePath
, L
"--list");
200 /* Store the tested module name */
201 Length
= wcschr(File
, L
'_') - File
;
202 GetSuiteIDData
.Module
= HeapAlloc(hProcessHeap
, 0, Length
+ 1);
203 WideCharToMultiByte(CP_ACP
, 0, File
, Length
, GetSuiteIDData
.Module
, Length
, NULL
, NULL
);
204 GetSuiteIDData
.Module
[Length
] = 0;
206 /* Start the process for getting all available tests */
207 if(!CreateProcessW(NULL
, FilePath
, NULL
, NULL
, TRUE
, NORMAL_PRIORITY_CLASS
, NULL
, NULL
, StartupInfo
, &ProcessInfo
))
209 StringOut("CreateProcessW for getting the available tests failed\n");
213 /* Wait till this process ended */
214 if(WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
) == WAIT_FAILED
)
216 StringOut("WaitForSingleObject failed for the test list\n");
220 /* Read the output data into a buffer */
221 if(!PeekNamedPipe(hReadPipe
, NULL
, 0, NULL
, &BytesAvailable
, NULL
))
223 StringOut("PeekNamedPipe failed for the test list\n");
227 Buffer
= HeapAlloc(hProcessHeap
, 0, BytesAvailable
);
229 if(!ReadFile(hReadPipe
, Buffer
, BytesAvailable
, &Temp
, NULL
))
231 StringOut("ReadFile failed\n");
235 /* Jump to the first available test */
236 pStart
= strchr(Buffer
, '\n');
239 while(pStart
< (Buffer
+ BytesAvailable
))
241 /* Get start and end of this test name */
247 /* Store the test name */
248 GetSuiteIDData
.Test
= HeapAlloc(hProcessHeap
, 0, pEnd
- pStart
+ 1);
249 memcpy(GetSuiteIDData
.Test
, pStart
, pEnd
- pStart
);
250 GetSuiteIDData
.Test
[pEnd
- pStart
] = 0;
252 /* If the user gave us a test to run, we check whether the module's test suite really provides this test. */
253 if(!AppOptions
.Test
|| (AppOptions
.Test
&& !strcmp(AppOptions
.Test
, GetSuiteIDData
.Test
)))
255 /* Build the command line for this test */
256 Length
= MultiByteToWideChar(CP_ACP
, 0, pStart
, pEnd
- pStart
, NULL
, 0);
257 MultiByteToWideChar(CP_ACP
, 0, pStart
, pEnd
- pStart
, &FilePath
[FilePosition
], Length
* sizeof(WCHAR
));
258 FilePath
[FilePosition
+ Length
] = 0;
260 if(!IntRunTest(FilePath
, hReadPipe
, StartupInfo
, &GetSuiteIDData
, SubmitData
))
265 HeapFree(hProcessHeap
, 0, GetSuiteIDData
.Test
);
267 /* Move to the next test */
272 HeapFree(hProcessHeap
, 0, GetSuiteIDData
.Module
);
273 HeapFree(hProcessHeap
, 0, Buffer
);
279 * Runs the Wine tests according to the options specified by the parameters.
282 * TRUE if everything went well, FALSE otherwise.
287 GENERAL_FINISH_DATA FinishData
;
291 SECURITY_ATTRIBUTES SecurityAttributes
;
292 STARTUPINFOW StartupInfo
= {0};
294 WCHAR FilePath
[MAX_PATH
];
296 WINE_SUBMIT_DATA SubmitData
= { {0} };
298 /* Create a pipe for getting the output of the tests */
299 SecurityAttributes
.nLength
= sizeof(SecurityAttributes
);
300 SecurityAttributes
.bInheritHandle
= TRUE
;
301 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
303 if(!CreatePipe(&hReadPipe
, &hWritePipe
, &SecurityAttributes
, 0))
305 StringOut("CreatePipe failed\n");
309 StartupInfo
.cb
= sizeof(StartupInfo
);
310 StartupInfo
.dwFlags
= STARTF_USESTDHANDLES
;
311 StartupInfo
.hStdOutput
= hWritePipe
;
313 /* Build the path for finding the tests */
314 if(GetWindowsDirectoryW(FilePath
, MAX_PATH
) > MAX_PATH
- 60)
316 StringOut("Windows directory path is too long\n");
320 wcscat(FilePath
, L
"\\bin\\");
321 PathPosition
= wcslen(FilePath
);
323 if(AppOptions
.Module
)
325 /* Find a test belonging to this module */
326 wcscat(FilePath
, AppOptions
.Module
);
327 wcscat(FilePath
, L
"_*.exe");
332 wcscat(FilePath
, L
"*.exe");
335 hFind
= FindFirstFileW(FilePath
, &fd
);
337 if(hFind
== INVALID_HANDLE_VALUE
)
339 StringOut("FindFirstFileW failed\n");
346 /* Build the full path to the test suite */
347 wcscpy(&FilePath
[PathPosition
], fd
.cFileName
);
350 if(!IntRunModuleTests(fd
.cFileName
, FilePath
, hReadPipe
, &StartupInfo
, &SubmitData
))
353 while(FindNextFileW(hFind
, &fd
));
358 if(AppOptions
.Submit
&& SubmitData
.General
.TestID
)
360 /* We're done with the tests, so close this test run */
361 FinishData
.TestID
= SubmitData
.General
.TestID
;
363 if(!Finish(WineTest
, &FinishData
))
367 HeapFree(hProcessHeap
, 0, FinishData
.TestID
);
370 CloseHandle(hReadPipe
);
371 CloseHandle(hWritePipe
);