852ee3d598a5d90b38b854e58897142e39bc5fc5
[reactos.git] / rostests / rosautotest / webservice.c
1 /*
2 * PROJECT: ReactOS Automatic Testing Utility
3 * LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
4 * PURPOSE: Submitting test results to the Web Service
5 * COPYRIGHT: Copyright 2008-2009 Colin Finck <colin@reactos.org>
6 */
7
8 #include "precomp.h"
9
10 static const CHAR ActionProp[] = "action=";
11 static const CHAR TestIDProp[] = "&testid=";
12 static const CHAR TestTypeProp[] = "&testtype=";
13 static const CHAR WineTestType[] = "wine";
14
15 /**
16 * Sends data to the ReactOS Web Test Manager web service.
17 *
18 * @param Data
19 * Pointer to a CHAR pointer, which contains the data to submit as HTTP POST data.
20 * The buffer behind this pointer had to be allocated with HeapAlloc.
21 * Returns the data received by the web service after the call.
22 *
23 * @param DataLength
24 * Pointer to a DWORD, which contains the length of the data to submit (in bytes).
25 * Returns the length of the data received by the web service after the call (in bytes).
26 *
27 * @return
28 * TRUE if everything went well, FALSE if an error occured while submitting the request.
29 * In case of an error, the function will output an appropriate error message through StringOut.
30 */
31 static BOOL
32 IntDoRequest(char** Data, PDWORD DataLength)
33 {
34 const WCHAR Headers[] = L"Content-Type: application/x-www-form-urlencoded";
35
36 BOOL ReturnValue = FALSE;
37 HINTERNET hHTTP = NULL;
38 HINTERNET hHTTPRequest = NULL;
39 HINTERNET hInet = NULL;
40
41 /* Establish an internet connection to the "testman" server */
42 hInet = InternetOpenW(L"rosautotest", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
43
44 if(!hInet)
45 {
46 StringOut("InternetOpenW failed\n");
47 goto Cleanup;
48 }
49
50 hHTTP = InternetConnectW(hInet, SERVER_HOSTNAME, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
51
52 if(!hHTTP)
53 {
54 StringOut("InternetConnectW failed\n");
55 goto Cleanup;
56 }
57
58 /* Post our test results to the web service */
59 hHTTPRequest = HttpOpenRequestW(hHTTP, L"POST", SERVER_FILE, NULL, NULL, NULL, INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
60
61 if(!hHTTPRequest)
62 {
63 StringOut("HttpOpenRequestW failed\n");
64 goto Cleanup;
65 }
66
67 if(!HttpSendRequestW(hHTTPRequest, Headers, wcslen(Headers), *Data, *DataLength))
68 {
69 StringOut("HttpSendRequestW failed\n");
70 goto Cleanup;
71 }
72
73 HeapFree(hProcessHeap, 0, *Data);
74 *Data = NULL;
75
76 /* Get the response */
77 if(!InternetQueryDataAvailable(hHTTPRequest, DataLength, 0, 0))
78 {
79 StringOut("InternetQueryDataAvailable failed\n");
80 goto Cleanup;
81 }
82
83 *Data = HeapAlloc(hProcessHeap, 0, *DataLength + 1);
84
85 if(!InternetReadFile(hHTTPRequest, *Data, *DataLength, DataLength))
86 {
87 StringOut("InternetReadFile failed\n");
88 goto Cleanup;
89 }
90
91 (*Data)[*DataLength] = 0;
92 ReturnValue = TRUE;
93
94 Cleanup:
95 if(hHTTPRequest)
96 InternetCloseHandle(hHTTPRequest);
97
98 if(hHTTP)
99 InternetCloseHandle(hHTTP);
100
101 if(hInet)
102 InternetCloseHandle(hInet);
103
104 return ReturnValue;
105 }
106
107 /**
108 * Determines whether a string contains entirely numeric values.
109 *
110 * @param Input
111 * The string to check.
112 *
113 * @return
114 * TRUE if the string is entirely numeric, FALSE otherwise.
115 */
116 static BOOL
117 IsNumber(PCHAR Input)
118 {
119 do
120 {
121 if(!isdigit(*Input))
122 return FALSE;
123
124 ++Input;
125 }
126 while(*Input);
127
128 return TRUE;
129 }
130
131 /**
132 * Requests a Test ID from the web service for our test run.
133 *
134 * @param TestType
135 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
136 *
137 * @return
138 * Returns the Test ID as a CHAR array if successful or NULL otherwise.
139 * The caller needs to HeapFree the returned pointer in case of success.
140 */
141 PCHAR
142 GetTestID(TESTTYPES TestType)
143 {
144 const CHAR GetTestIDAction[] = "gettestid";
145 const CHAR CommentProp[] = "&comment=";
146
147 DWORD DataLength;
148 PCHAR Data;
149 PCHAR ReturnValue = NULL;
150
151 /* Build the full request string */
152 DataLength = sizeof(ActionProp) - 1 + sizeof(GetTestIDAction) - 1;
153 DataLength += strlen(AuthenticationRequestString) + strlen(SystemInfoRequestString);
154
155 if(AppOptions.Comment)
156 DataLength += sizeof(CommentProp) - 1 + strlen(AppOptions.Comment);
157
158 DataLength += sizeof(TestTypeProp) - 1;
159
160 switch(TestType)
161 {
162 case WineTest:
163 DataLength += sizeof(WineTestType) - 1;
164 break;
165 }
166
167 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
168 strcpy(Data, ActionProp);
169 strcat(Data, GetTestIDAction);
170 strcat(Data, AuthenticationRequestString);
171 strcat(Data, SystemInfoRequestString);
172
173 if(AppOptions.Comment)
174 {
175 strcat(Data, CommentProp);
176 strcat(Data, AppOptions.Comment);
177 }
178
179 strcat(Data, TestTypeProp);
180
181 switch(TestType)
182 {
183 case WineTest:
184 strcat(Data, WineTestType);
185 break;
186 }
187
188 if(!IntDoRequest(&Data, &DataLength))
189 goto Cleanup;
190
191 /* Verify that this is really a number */
192 if(!IsNumber(Data))
193 {
194 StringOut("Expected Test ID, but received:\n");
195 StringOut(Data);
196 StringOut("\n");
197 goto Cleanup;
198 }
199
200 ReturnValue = Data;
201
202 Cleanup:
203 if(Data && ReturnValue != Data)
204 HeapFree(hProcessHeap, 0, Data);
205
206 return ReturnValue;
207 }
208
209 /**
210 * Requests a Suite ID from the web service for our module/test combination.
211 *
212 * @param TestType
213 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
214 *
215 * @param TestData
216 * Pointer to a *_GETSUITEID_DATA structure appropriate for our selected test type.
217 * Contains other input information for this request.
218 *
219 * @return
220 * Returns the Suite ID as a CHAR array if successful or NULL otherwise.
221 * The caller needs to HeapFree the returned pointer in case of success.
222 */
223 PCHAR
224 GetSuiteID(TESTTYPES TestType, const PVOID TestData)
225 {
226 const CHAR GetSuiteIDAction[] = "getsuiteid";
227 const CHAR ModuleProp[] = "&module=";
228 const CHAR TestProp[] = "&test=";
229
230 DWORD DataLength;
231 PCHAR Data;
232 PCHAR ReturnValue = NULL;
233 PWINE_GETSUITEID_DATA WineData;
234
235 DataLength = sizeof(ActionProp) - 1 + sizeof(GetSuiteIDAction) - 1;
236 DataLength += strlen(AuthenticationRequestString);
237 DataLength += sizeof(TestTypeProp) - 1;
238
239 switch(TestType)
240 {
241 case WineTest:
242 DataLength += sizeof(WineTestType) - 1;
243
244 WineData = (PWINE_GETSUITEID_DATA)TestData;
245 DataLength += sizeof(ModuleProp) - 1;
246 DataLength += strlen(WineData->Module);
247 DataLength += sizeof(TestProp) - 1;
248 DataLength += strlen(WineData->Test);
249
250 break;
251 }
252
253 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
254 strcpy(Data, ActionProp);
255 strcat(Data, GetSuiteIDAction);
256 strcat(Data, AuthenticationRequestString);
257 strcat(Data, TestTypeProp);
258
259 switch(TestType)
260 {
261 case WineTest:
262 strcat(Data, WineTestType);
263
264 /* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
265 WineData = (PWINE_GETSUITEID_DATA)TestData;
266 strcat(Data, ModuleProp);
267 strcat(Data, WineData->Module);
268 strcat(Data, TestProp);
269 strcat(Data, WineData->Test);
270
271 break;
272 }
273
274 if(!IntDoRequest(&Data, &DataLength))
275 goto Cleanup;
276
277 /* Verify that this is really a number */
278 if(!IsNumber(Data))
279 {
280 StringOut("Expected Suite ID, but received:\n");
281 StringOut(Data);
282 StringOut("\n");
283 goto Cleanup;
284 }
285
286 ReturnValue = Data;
287
288 Cleanup:
289 if(Data && ReturnValue != Data)
290 HeapFree(hProcessHeap, 0, Data);
291
292 return ReturnValue;
293 }
294
295 /**
296 * Submits the result of one test call to the web service.
297 *
298 * @param TestType
299 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
300 *
301 * @param TestData
302 * Pointer to a *_SUBMIT_DATA structure appropriate for our selected test type.
303 * Contains other input information for this request.
304 *
305 * @return
306 * TRUE if everything went well, FALSE otherwise.
307 */
308 BOOL
309 Submit(TESTTYPES TestType, const PVOID TestData)
310 {
311 const CHAR SubmitAction[] = "submit";
312 const CHAR SuiteIDProp[] = "&suiteid=";
313 const CHAR LogProp[] = "&log=";
314
315 BOOL ReturnValue = FALSE;
316 DWORD DataLength;
317 PCHAR Data;
318 PCHAR pData;
319 PGENERAL_SUBMIT_DATA GeneralData;
320 PWINE_SUBMIT_DATA WineData;
321
322 /* Compute the full length of the POST data */
323 DataLength = sizeof(ActionProp) - 1 + sizeof(SubmitAction) - 1;
324 DataLength += strlen(AuthenticationRequestString);
325
326 GeneralData = (PGENERAL_SUBMIT_DATA)TestData;
327 DataLength += sizeof(TestIDProp) - 1;
328 DataLength += strlen(GeneralData->TestID);
329 DataLength += sizeof(SuiteIDProp) - 1;
330 DataLength += strlen(GeneralData->SuiteID);
331
332 /* The rest of the POST data depends on the test type */
333 DataLength += sizeof(TestTypeProp) - 1;
334
335 switch(TestType)
336 {
337 case WineTest:
338 DataLength += sizeof(WineTestType) - 1;
339
340 WineData = (PWINE_SUBMIT_DATA)TestData;
341 DataLength += sizeof(LogProp) - 1;
342 DataLength += 3 * strlen(WineData->Log);
343
344 break;
345 }
346
347 /* Now collect all the POST data */
348 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
349 strcpy(Data, ActionProp);
350 strcat(Data, SubmitAction);
351 strcat(Data, AuthenticationRequestString);
352
353 strcat(Data, TestIDProp);
354 strcat(Data, GeneralData->TestID);
355 strcat(Data, SuiteIDProp);
356 strcat(Data, GeneralData->SuiteID);
357
358 strcat(Data, TestTypeProp);
359
360 switch(TestType)
361 {
362 case WineTest:
363 strcat(Data, WineTestType);
364
365 /* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
366 WineData = (PWINE_SUBMIT_DATA)TestData;
367
368 strcat(Data, LogProp);
369 pData = Data + strlen(Data);
370 EscapeString(pData, WineData->Log);
371
372 break;
373 }
374
375 /* DataLength still contains the maximum length of the buffer, but not the actual data length we need for the request.
376 Determine that one now. */
377 DataLength = strlen(Data);
378
379 /* Send all the stuff */
380 if(!IntDoRequest(&Data, &DataLength))
381 goto Cleanup;
382
383 /* Output the response */
384 StringOut("The server responded:\n");
385 StringOut(Data);
386 StringOut("\n");
387
388 if(!strcmp(Data, "OK"))
389 ReturnValue = TRUE;
390
391 Cleanup:
392 if(Data)
393 HeapFree(hProcessHeap, 0, Data);
394
395 return ReturnValue;
396 }
397
398 /**
399 * Finishes a test run for the web service.
400 *
401 * @param TestType
402 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
403 *
404 * @param TestData
405 * Pointer to a *_FINISH_DATA structure appropriate for our selected test type.
406 * Contains other input information for this request.
407 *
408 * @return
409 * TRUE if everything went well, FALSE otherwise.
410 */
411 BOOL
412 Finish(TESTTYPES TestType, const PVOID TestData)
413 {
414 const CHAR FinishAction[] = "finish";
415
416 BOOL ReturnValue = FALSE;
417 DWORD DataLength;
418 PCHAR Data;
419 PGENERAL_FINISH_DATA GeneralData;
420
421 /* Build the full request string */
422 DataLength = sizeof(ActionProp) - 1 + sizeof(FinishAction) - 1;
423 DataLength += strlen(AuthenticationRequestString);
424
425 GeneralData = (PGENERAL_FINISH_DATA)TestData;
426 DataLength += sizeof(TestIDProp) - 1;
427 DataLength += strlen(GeneralData->TestID);
428
429 DataLength += sizeof(TestTypeProp) - 1;
430
431 switch(TestType)
432 {
433 case WineTest:
434 DataLength += sizeof(WineTestType) - 1;
435 break;
436 }
437
438 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
439 strcpy(Data, ActionProp);
440 strcat(Data, FinishAction);
441 strcat(Data, AuthenticationRequestString);
442 strcat(Data, TestIDProp);
443 strcat(Data, GeneralData->TestID);
444 strcat(Data, TestTypeProp);
445
446 switch(TestType)
447 {
448 case WineTest:
449 strcat(Data, WineTestType);
450 break;
451 }
452
453 if(!IntDoRequest(&Data, &DataLength))
454 goto Cleanup;
455
456 if(!strcmp(Data, "OK"))
457 ReturnValue = TRUE;
458
459 Cleanup:
460 if(Data)
461 HeapFree(hProcessHeap, 0, Data);
462
463 return ReturnValue;
464 }