dcfd79fa51f82fad2a1f62dffd3f63be492cba74
[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 HeapFree(hProcessHeap, 0, Data);
174 return NULL;
175 }
176
177 return Data;
178 }
179
180 /**
181 * Requests a Suite ID from the web service for our module/test combination.
182 *
183 * @param TestType
184 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
185 *
186 * @param TestData
187 * Pointer to a *_GETSUITEID_DATA structure appropriate for our selected test type.
188 * Contains other input information for this request.
189 *
190 * @return
191 * Returns the Suite ID as a CHAR array if successful or NULL otherwise.
192 */
193 PCHAR
194 GetSuiteID(TESTTYPES TestType, const PVOID TestData)
195 {
196 const CHAR GetSuiteIDAction[] = "getsuiteid";
197 const CHAR ModuleProp[] = "&module=";
198 const CHAR TestProp[] = "&test=";
199
200 DWORD DataLength;
201 PCHAR Data;
202 PWINE_GETSUITEID_DATA WineData;
203
204 DataLength = sizeof(ActionProp) - 1 + sizeof(GetSuiteIDAction) - 1;
205 DataLength += strlen(AuthenticationRequestString);
206 DataLength += sizeof(TestTypeProp) - 1;
207
208 switch(TestType)
209 {
210 case WineTest:
211 DataLength += sizeof(WineTestType) - 1;
212
213 WineData = (PWINE_GETSUITEID_DATA)TestData;
214 DataLength += sizeof(ModuleProp) - 1;
215 DataLength += strlen(WineData->Module);
216 DataLength += sizeof(TestProp) - 1;
217 DataLength += strlen(WineData->Test);
218
219 break;
220 }
221
222 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
223 strcpy(Data, ActionProp);
224 strcat(Data, GetSuiteIDAction);
225 strcat(Data, AuthenticationRequestString);
226 strcat(Data, TestTypeProp);
227
228 switch(TestType)
229 {
230 case WineTest:
231 strcat(Data, WineTestType);
232
233 /* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
234 WineData = (PWINE_GETSUITEID_DATA)TestData;
235 strcat(Data, ModuleProp);
236 strcat(Data, WineData->Module);
237 strcat(Data, TestProp);
238 strcat(Data, WineData->Test);
239
240 break;
241 }
242
243 if(!IntDoRequest(&Data, &DataLength))
244 return NULL;
245
246 /* Verify that this is really a number */
247 if(!IsNumber(Data))
248 {
249 StringOut("Expected Suite ID, but received:\n");
250 StringOut(Data);
251 HeapFree(hProcessHeap, 0, Data);
252 return NULL;
253 }
254
255 return Data;
256 }
257
258 /**
259 * Submits the result of one test call to the web service.
260 *
261 * @param TestType
262 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
263 *
264 * @param TestData
265 * Pointer to a *_SUBMIT_DATA structure appropriate for our selected test type.
266 * Contains other input information for this request.
267 *
268 * @return
269 * TRUE if everything went well, FALSE otherwise.
270 */
271 BOOL
272 Submit(TESTTYPES TestType, const PVOID TestData)
273 {
274 const CHAR SubmitAction[] = "submit";
275 const CHAR SuiteIDProp[] = "&suiteid=";
276 const CHAR LogProp[] = "&log=";
277
278 DWORD DataLength;
279 PCHAR Data;
280 PCHAR pData;
281 PGENERAL_SUBMIT_DATA GeneralData;
282 PWINE_SUBMIT_DATA WineData;
283
284 /* Compute the full length of the POST data */
285 DataLength = sizeof(ActionProp) - 1 + sizeof(SubmitAction) - 1;
286 DataLength += strlen(AuthenticationRequestString);
287
288 GeneralData = (PGENERAL_SUBMIT_DATA)TestData;
289 DataLength += sizeof(TestIDProp) - 1;
290 DataLength += strlen(GeneralData->TestID);
291 DataLength += sizeof(SuiteIDProp) - 1;
292 DataLength += strlen(GeneralData->SuiteID);
293
294 /* The rest of the POST data depends on the test type */
295 DataLength += sizeof(TestTypeProp) - 1;
296
297 switch(TestType)
298 {
299 case WineTest:
300 DataLength += sizeof(WineTestType) - 1;
301
302 WineData = (PWINE_SUBMIT_DATA)TestData;
303 DataLength += sizeof(LogProp) - 1;
304 DataLength += 3 * strlen(WineData->Log);
305
306 break;
307 }
308
309 /* Now collect all the POST data */
310 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
311 strcpy(Data, ActionProp);
312 strcat(Data, SubmitAction);
313 strcat(Data, AuthenticationRequestString);
314
315 strcat(Data, TestIDProp);
316 strcat(Data, GeneralData->TestID);
317 strcat(Data, SuiteIDProp);
318 strcat(Data, GeneralData->SuiteID);
319
320 strcat(Data, TestTypeProp);
321
322 switch(TestType)
323 {
324 case WineTest:
325 strcat(Data, WineTestType);
326
327 /* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
328 WineData = (PWINE_SUBMIT_DATA)TestData;
329
330 strcat(Data, LogProp);
331 pData = Data + strlen(Data);
332 EscapeString(pData, WineData->Log);
333
334 break;
335 }
336
337 /* DataLength still contains the maximum length of the buffer, but not the actual data length we need for the request.
338 Determine that one now. */
339 DataLength = strlen(Data);
340
341 /* Send all the stuff */
342 if(!IntDoRequest(&Data, &DataLength))
343 return FALSE;
344
345 /* Output the response */
346 StringOut("The server responded:\n");
347 StringOut(Data);
348 StringOut("\n");
349
350 if(!strcmp(Data, "OK"))
351 return TRUE;
352
353 return FALSE;
354 }
355
356 /**
357 * Finishes a test run for the web service.
358 *
359 * @param TestType
360 * Value from the TESTTYPES enum indicating the type of test we are about to submit.
361 *
362 * @param TestData
363 * Pointer to a *_FINISH_DATA structure appropriate for our selected test type.
364 * Contains other input information for this request.
365 *
366 * @return
367 * TRUE if everything went well, FALSE otherwise.
368 */
369 BOOL
370 Finish(TESTTYPES TestType, const PVOID TestData)
371 {
372 const CHAR FinishAction[] = "finish";
373
374 DWORD DataLength;
375 PCHAR Data;
376 PGENERAL_FINISH_DATA GeneralData;
377
378 /* Build the full request string */
379 DataLength = sizeof(ActionProp) - 1 + sizeof(FinishAction) - 1;
380 DataLength += strlen(AuthenticationRequestString);
381
382 GeneralData = (PGENERAL_FINISH_DATA)TestData;
383 DataLength += sizeof(TestIDProp) - 1;
384 DataLength += strlen(GeneralData->TestID);
385
386 DataLength += sizeof(TestTypeProp) - 1;
387
388 switch(TestType)
389 {
390 case WineTest:
391 DataLength += sizeof(WineTestType) - 1;
392 break;
393 }
394
395 Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
396 strcpy(Data, ActionProp);
397 strcat(Data, FinishAction);
398 strcat(Data, AuthenticationRequestString);
399 strcat(Data, TestIDProp);
400 strcat(Data, GeneralData->TestID);
401 strcat(Data, TestTypeProp);
402
403 switch(TestType)
404 {
405 case WineTest:
406 strcat(Data, WineTestType);
407 break;
408 }
409
410 if(!IntDoRequest(&Data, &DataLength))
411 return FALSE;
412
413 if(!strcmp(Data, "OK"))
414 return TRUE;
415
416 return FALSE;
417 }