ab4eb38193b4556b61eb20e33c7b82ee4e042751
[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 HINTERNET hHTTP;
37 HINTERNET hHTTPRequest;
38 HINTERNET hInet;
39
40 /* Establish an internet connection to the "testman" server */
41 hInet = InternetOpenW(L"rosautotest", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
42
43 if(!hInet)
44 {
45 StringOut("InternetOpenW failed\n");
46 return FALSE;
47 }
48
49 hHTTP = InternetConnectW(hInet, SERVER_HOSTNAME, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
50
51 if(!hHTTP)
52 {
53 StringOut("InternetConnectW failed\n");
54 return FALSE;
55 }
56
57 /* Post our test results to the web service */
58 hHTTPRequest = HttpOpenRequestW(hHTTP, L"POST", SERVER_FILE, NULL, NULL, NULL, INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
59
60 if(!hHTTPRequest)
61 {
62 StringOut("HttpOpenRequestW failed\n");
63 return FALSE;
64 }
65
66 if(!HttpSendRequestW(hHTTPRequest, Headers, wcslen(Headers), *Data, *DataLength))
67 {
68 StringOut("HttpSendRequestW failed\n");
69 return FALSE;
70 }
71
72 HeapFree(hProcessHeap, 0, *Data);
73
74 /* Get the response */
75 if(!InternetQueryDataAvailable(hHTTPRequest, DataLength, 0, 0))
76 {
77 StringOut("InternetQueryDataAvailable failed\n");
78 return FALSE;
79 }
80
81 *Data = HeapAlloc(hProcessHeap, 0, *DataLength + 1);
82
83 if(!InternetReadFile(hHTTPRequest, *Data, *DataLength, DataLength))
84 {
85 StringOut("InternetReadFile failed\n");
86 return FALSE;
87 }
88
89 (*Data)[*DataLength] = 0;
90
91 InternetCloseHandle(hHTTPRequest);
92 InternetCloseHandle(hHTTP);
93 InternetCloseHandle(hInet);
94
95 return TRUE;
96 }
97
98 /**
99 * Determines whether a string contains entirely numeric values.
100 *
101 * @param Input
102 * The string to check.
103 *
104 * @return
105 * TRUE if the string is entirely numeric, FALSE otherwise.
106 */
107 static BOOL
108 IsNumber(PCHAR Input)
109 {
110 do
111 {
112 if(!isdigit(*Input))
113 return FALSE;
114
115 ++Input;
116 }
117 while(*Input);
118
119 return TRUE;
120 }
121
122 /**
123 * Requests a Test ID from the web service for our test run.
124 *
125 * @param TestType
126 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
127 *
128 * @return
129 * Returns the Test ID as a CHAR array if successful or NULL otherwise.
130 */
131 PCHAR
132 GetTestID(TESTTYPES TestType)
133 {
134 const CHAR GetTestIDAction[] = "gettestid";
135
136 DWORD DataLength;
137 PCHAR Data;
138
139 /* Build the full request string */
140 DataLength = sizeof(ActionProp) - 1 + sizeof(GetTestIDAction) - 1;
141 DataLength += strlen(AuthenticationRequestString) + strlen(SystemInfoRequestString);
142 DataLength += sizeof(TestTypeProp) - 1;
143
144 switch(TestType)
145 {
146 case WineTest:
147 DataLength += sizeof(WineTestType) - 1;
148 break;
149 }
150
151 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
152 strcpy(Data, ActionProp);
153 strcat(Data, GetTestIDAction);
154 strcat(Data, AuthenticationRequestString);
155 strcat(Data, SystemInfoRequestString);
156 strcat(Data, TestTypeProp);
157
158 switch(TestType)
159 {
160 case WineTest:
161 strcat(Data, WineTestType);
162 break;
163 }
164
165 if(!IntDoRequest(&Data, &DataLength))
166 return NULL;
167
168 /* Verify that this is really a number */
169 if(!IsNumber(Data))
170 {
171 StringOut("Expected Test ID, but received:\n");
172 StringOut(Data);
173 StringOut("\n");
174 HeapFree(hProcessHeap, 0, Data);
175 return NULL;
176 }
177
178 return Data;
179 }
180
181 /**
182 * Requests a Suite ID from the web service for our module/test combination.
183 *
184 * @param TestType
185 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
186 *
187 * @param TestData
188 * Pointer to a *_GETSUITEID_DATA structure appropriate for our selected test type.
189 * Contains other input information for this request.
190 *
191 * @return
192 * Returns the Suite ID as a CHAR array if successful or NULL otherwise.
193 */
194 PCHAR
195 GetSuiteID(TESTTYPES TestType, const PVOID TestData)
196 {
197 const CHAR GetSuiteIDAction[] = "getsuiteid";
198 const CHAR ModuleProp[] = "&module=";
199 const CHAR TestProp[] = "&test=";
200
201 DWORD DataLength;
202 PCHAR Data;
203 PWINE_GETSUITEID_DATA WineData;
204
205 DataLength = sizeof(ActionProp) - 1 + sizeof(GetSuiteIDAction) - 1;
206 DataLength += strlen(AuthenticationRequestString);
207 DataLength += sizeof(TestTypeProp) - 1;
208
209 switch(TestType)
210 {
211 case WineTest:
212 DataLength += sizeof(WineTestType) - 1;
213
214 WineData = (PWINE_GETSUITEID_DATA)TestData;
215 DataLength += sizeof(ModuleProp) - 1;
216 DataLength += strlen(WineData->Module);
217 DataLength += sizeof(TestProp) - 1;
218 DataLength += strlen(WineData->Test);
219
220 break;
221 }
222
223 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
224 strcpy(Data, ActionProp);
225 strcat(Data, GetSuiteIDAction);
226 strcat(Data, AuthenticationRequestString);
227 strcat(Data, TestTypeProp);
228
229 switch(TestType)
230 {
231 case WineTest:
232 strcat(Data, WineTestType);
233
234 /* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
235 WineData = (PWINE_GETSUITEID_DATA)TestData;
236 strcat(Data, ModuleProp);
237 strcat(Data, WineData->Module);
238 strcat(Data, TestProp);
239 strcat(Data, WineData->Test);
240
241 break;
242 }
243
244 if(!IntDoRequest(&Data, &DataLength))
245 return NULL;
246
247 /* Verify that this is really a number */
248 if(!IsNumber(Data))
249 {
250 StringOut("Expected Suite ID, but received:\n");
251 StringOut(Data);
252 StringOut("\n");
253 HeapFree(hProcessHeap, 0, Data);
254 return NULL;
255 }
256
257 return Data;
258 }
259
260 /**
261 * Submits the result of one test call to the web service.
262 *
263 * @param TestType
264 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
265 *
266 * @param TestData
267 * Pointer to a *_SUBMIT_DATA structure appropriate for our selected test type.
268 * Contains other input information for this request.
269 *
270 * @return
271 * TRUE if everything went well, FALSE otherwise.
272 */
273 BOOL
274 Submit(TESTTYPES TestType, const PVOID TestData)
275 {
276 const CHAR SubmitAction[] = "submit";
277 const CHAR SuiteIDProp[] = "&suiteid=";
278 const CHAR LogProp[] = "&log=";
279
280 DWORD DataLength;
281 PCHAR Data;
282 PCHAR pData;
283 PGENERAL_SUBMIT_DATA GeneralData;
284 PWINE_SUBMIT_DATA WineData;
285
286 /* Compute the full length of the POST data */
287 DataLength = sizeof(ActionProp) - 1 + sizeof(SubmitAction) - 1;
288 DataLength += strlen(AuthenticationRequestString);
289
290 GeneralData = (PGENERAL_SUBMIT_DATA)TestData;
291 DataLength += sizeof(TestIDProp) - 1;
292 DataLength += strlen(GeneralData->TestID);
293 DataLength += sizeof(SuiteIDProp) - 1;
294 DataLength += strlen(GeneralData->SuiteID);
295
296 /* The rest of the POST data depends on the test type */
297 DataLength += sizeof(TestTypeProp) - 1;
298
299 switch(TestType)
300 {
301 case WineTest:
302 DataLength += sizeof(WineTestType) - 1;
303
304 WineData = (PWINE_SUBMIT_DATA)TestData;
305 DataLength += sizeof(LogProp) - 1;
306 DataLength += 3 * strlen(WineData->Log);
307
308 break;
309 }
310
311 /* Now collect all the POST data */
312 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
313 strcpy(Data, ActionProp);
314 strcat(Data, SubmitAction);
315 strcat(Data, AuthenticationRequestString);
316
317 strcat(Data, TestIDProp);
318 strcat(Data, GeneralData->TestID);
319 strcat(Data, SuiteIDProp);
320 strcat(Data, GeneralData->SuiteID);
321
322 strcat(Data, TestTypeProp);
323
324 switch(TestType)
325 {
326 case WineTest:
327 strcat(Data, WineTestType);
328
329 /* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
330 WineData = (PWINE_SUBMIT_DATA)TestData;
331
332 strcat(Data, LogProp);
333 pData = Data + strlen(Data);
334 EscapeString(pData, WineData->Log);
335
336 break;
337 }
338
339 /* DataLength still contains the maximum length of the buffer, but not the actual data length we need for the request.
340 Determine that one now. */
341 DataLength = strlen(Data);
342
343 /* Send all the stuff */
344 if(!IntDoRequest(&Data, &DataLength))
345 return FALSE;
346
347 /* Output the response */
348 StringOut("The server responded:\n");
349 StringOut(Data);
350 StringOut("\n");
351
352 if(!strcmp(Data, "OK"))
353 return TRUE;
354
355 return FALSE;
356 }
357
358 /**
359 * Finishes a test run for the web service.
360 *
361 * @param TestType
362 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
363 *
364 * @param TestData
365 * Pointer to a *_FINISH_DATA structure appropriate for our selected test type.
366 * Contains other input information for this request.
367 *
368 * @return
369 * TRUE if everything went well, FALSE otherwise.
370 */
371 BOOL
372 Finish(TESTTYPES TestType, const PVOID TestData)
373 {
374 const CHAR FinishAction[] = "finish";
375
376 DWORD DataLength;
377 PCHAR Data;
378 PGENERAL_FINISH_DATA GeneralData;
379
380 /* Build the full request string */
381 DataLength = sizeof(ActionProp) - 1 + sizeof(FinishAction) - 1;
382 DataLength += strlen(AuthenticationRequestString);
383
384 GeneralData = (PGENERAL_FINISH_DATA)TestData;
385 DataLength += sizeof(TestIDProp) - 1;
386 DataLength += strlen(GeneralData->TestID);
387
388 DataLength += sizeof(TestTypeProp) - 1;
389
390 switch(TestType)
391 {
392 case WineTest:
393 DataLength += sizeof(WineTestType) - 1;
394 break;
395 }
396
397 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
398 strcpy(Data, ActionProp);
399 strcat(Data, FinishAction);
400 strcat(Data, AuthenticationRequestString);
401 strcat(Data, TestIDProp);
402 strcat(Data, GeneralData->TestID);
403 strcat(Data, TestTypeProp);
404
405 switch(TestType)
406 {
407 case WineTest:
408 strcat(Data, WineTestType);
409 break;
410 }
411
412 if(!IntDoRequest(&Data, &DataLength))
413 return FALSE;
414
415 if(!strcmp(Data, "OK"))
416 return TRUE;
417
418 return FALSE;
419 }