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 /* Close the process handles */
121 CloseHandle(ProcessInfo
.hProcess
);
122 CloseHandle(ProcessInfo
.hThread
);
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
);
152 HeapFree(hProcessHeap
, 0, SubmitData
->General
.SuiteID
);
156 HeapFree(hProcessHeap
, 0, SubmitData
->Log
);
165 * Runs the desired tests for a specified module.
168 * The file name of the module's test suite.
171 * The full path to the file of the module's test suite.
174 * Handle to the Read Pipe set up in RunWineTests.
177 * Pointer to the StartupInfo structure set up in RunWineTests.
180 * Pointer to the SubmitData structure set up in RunWineTests.
183 * TRUE if everything went well, FALSE otherwise.
186 IntRunModuleTests(PWSTR File
, PWSTR FilePath
, HANDLE hReadPipe
, LPSTARTUPINFOW StartupInfo
, PWINE_SUBMIT_DATA SubmitData
)
188 DWORD BytesAvailable
;
194 PROCESS_INFORMATION ProcessInfo
;
196 WINE_GETSUITEID_DATA GetSuiteIDData
;
198 /* Build the full command line */
199 FilePosition
= wcslen(FilePath
);
200 FilePath
[FilePosition
++] = ' ';
201 FilePath
[FilePosition
] = 0;
202 wcscat(FilePath
, L
"--list");
204 /* Store the tested module name */
205 Length
= wcschr(File
, L
'_') - File
;
206 GetSuiteIDData
.Module
= HeapAlloc(hProcessHeap
, 0, Length
+ 1);
207 WideCharToMultiByte(CP_ACP
, 0, File
, Length
, GetSuiteIDData
.Module
, Length
, NULL
, NULL
);
208 GetSuiteIDData
.Module
[Length
] = 0;
210 /* Start the process for getting all available tests */
211 if(!CreateProcessW(NULL
, FilePath
, NULL
, NULL
, TRUE
, NORMAL_PRIORITY_CLASS
, NULL
, NULL
, StartupInfo
, &ProcessInfo
))
213 StringOut("CreateProcessW for getting the available tests failed\n");
217 /* Wait till this process ended */
218 if(WaitForSingleObject(ProcessInfo
.hProcess
, INFINITE
) == WAIT_FAILED
)
220 StringOut("WaitForSingleObject failed for the test list\n");
224 /* Close the process handles */
225 CloseHandle(ProcessInfo
.hProcess
);
226 CloseHandle(ProcessInfo
.hThread
);
228 /* Read the output data into a buffer */
229 if(!PeekNamedPipe(hReadPipe
, NULL
, 0, NULL
, &BytesAvailable
, NULL
))
231 StringOut("PeekNamedPipe failed for the test list\n");
235 Buffer
= HeapAlloc(hProcessHeap
, 0, BytesAvailable
);
237 if(!ReadFile(hReadPipe
, Buffer
, BytesAvailable
, &Temp
, NULL
))
239 StringOut("ReadFile failed\n");
243 /* Jump to the first available test */
244 pStart
= strchr(Buffer
, '\n');
247 while(pStart
< (Buffer
+ BytesAvailable
))
249 /* Get start and end of this test name */
255 /* Store the test name */
256 GetSuiteIDData
.Test
= HeapAlloc(hProcessHeap
, 0, pEnd
- pStart
+ 1);
257 memcpy(GetSuiteIDData
.Test
, pStart
, pEnd
- pStart
);
258 GetSuiteIDData
.Test
[pEnd
- pStart
] = 0;
260 /* If the user gave us a test to run, we check whether the module's test suite really provides this test. */
261 if(!AppOptions
.Test
|| (AppOptions
.Test
&& !strcmp(AppOptions
.Test
, GetSuiteIDData
.Test
)))
263 /* Build the command line for this test */
264 Length
= MultiByteToWideChar(CP_ACP
, 0, pStart
, pEnd
- pStart
, NULL
, 0);
265 MultiByteToWideChar(CP_ACP
, 0, pStart
, pEnd
- pStart
, &FilePath
[FilePosition
], Length
* sizeof(WCHAR
));
266 FilePath
[FilePosition
+ Length
] = 0;
268 if(!IntRunTest(FilePath
, hReadPipe
, StartupInfo
, &GetSuiteIDData
, SubmitData
))
273 HeapFree(hProcessHeap
, 0, GetSuiteIDData
.Test
);
275 /* Move to the next test */
280 HeapFree(hProcessHeap
, 0, GetSuiteIDData
.Module
);
281 HeapFree(hProcessHeap
, 0, Buffer
);
287 * Runs the Wine tests according to the options specified by the parameters.
290 * TRUE if everything went well, FALSE otherwise.
295 GENERAL_FINISH_DATA FinishData
;
299 SECURITY_ATTRIBUTES SecurityAttributes
;
300 STARTUPINFOW StartupInfo
= {0};
302 WCHAR FilePath
[MAX_PATH
];
304 WINE_SUBMIT_DATA SubmitData
= { {0} };
306 /* Create a pipe for getting the output of the tests */
307 SecurityAttributes
.nLength
= sizeof(SecurityAttributes
);
308 SecurityAttributes
.bInheritHandle
= TRUE
;
309 SecurityAttributes
.lpSecurityDescriptor
= NULL
;
311 if(!CreatePipe(&hReadPipe
, &hWritePipe
, &SecurityAttributes
, 0))
313 StringOut("CreatePipe failed\n");
317 StartupInfo
.cb
= sizeof(StartupInfo
);
318 StartupInfo
.dwFlags
= STARTF_USESTDHANDLES
;
319 StartupInfo
.hStdOutput
= hWritePipe
;
321 /* Build the path for finding the tests */
322 if(GetWindowsDirectoryW(FilePath
, MAX_PATH
) > MAX_PATH
- 60)
324 StringOut("Windows directory path is too long\n");
328 wcscat(FilePath
, L
"\\bin\\");
329 PathPosition
= wcslen(FilePath
);
331 if(AppOptions
.Module
)
333 /* Find a test belonging to this module */
334 wcscat(FilePath
, AppOptions
.Module
);
335 wcscat(FilePath
, L
"_*.exe");
340 wcscat(FilePath
, L
"*.exe");
343 hFind
= FindFirstFileW(FilePath
, &fd
);
345 if(hFind
== INVALID_HANDLE_VALUE
)
347 StringOut("FindFirstFileW failed\n");
354 /* Build the full path to the test suite */
355 wcscpy(&FilePath
[PathPosition
], fd
.cFileName
);
358 if(!IntRunModuleTests(fd
.cFileName
, FilePath
, hReadPipe
, &StartupInfo
, &SubmitData
))
361 while(FindNextFileW(hFind
, &fd
));
366 if(AppOptions
.Submit
&& SubmitData
.General
.TestID
)
368 /* We're done with the tests, so close this test run */
369 FinishData
.TestID
= SubmitData
.General
.TestID
;
371 if(!Finish(WineTest
, &FinishData
))
375 HeapFree(hProcessHeap
, 0, FinishData
.TestID
);
378 CloseHandle(hReadPipe
);
379 CloseHandle(hWritePipe
);