reactos/rostests/rosautotest/webservice.c

420 lines
11 KiB
C
Raw Normal View History

/*
* PROJECT: ReactOS Automatic Testing Utility
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
* PURPOSE: Submitting test results to the Web Service
* COPYRIGHT: Copyright 2008-2009 Colin Finck <colin@reactos.org>
*/
#include "precomp.h"
static const CHAR ActionProp[] = "action=";
static const CHAR TestIDProp[] = "&testid=";
static const CHAR TestTypeProp[] = "&testtype=";
static const CHAR WineTestType[] = "wine";
/**
* Sends data to the ReactOS Web Test Manager web service.
*
* @param Data
* Pointer to a CHAR pointer, which contains the data to submit as HTTP POST data.
* The buffer behind this pointer had to be allocated with HeapAlloc.
* Returns the data received by the web service after the call.
*
* @param DataLength
* Pointer to a DWORD, which contains the length of the data to submit (in bytes).
* Returns the length of the data received by the web service after the call (in bytes).
*
* @return
* TRUE if everything went well, FALSE if an error occured while submitting the request.
* In case of an error, the function will output an appropriate error message through StringOut.
*/
static BOOL
IntDoRequest(char** Data, PDWORD DataLength)
{
const WCHAR Headers[] = L"Content-Type: application/x-www-form-urlencoded";
HINTERNET hHTTP;
HINTERNET hHTTPRequest;
HINTERNET hInet;
/* Establish an internet connection to the "testman" server */
hInet = InternetOpenW(L"rosautotest", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
if(!hInet)
{
StringOut("InternetOpenW failed\n");
return FALSE;
}
hHTTP = InternetConnectW(hInet, SERVER_HOSTNAME, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
if(!hHTTP)
{
StringOut("InternetConnectW failed\n");
return FALSE;
}
/* Post our test results to the web service */
hHTTPRequest = HttpOpenRequestW(hHTTP, L"POST", SERVER_FILE, NULL, NULL, NULL, INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
if(!hHTTPRequest)
{
StringOut("HttpOpenRequestW failed\n");
return FALSE;
}
if(!HttpSendRequestW(hHTTPRequest, Headers, wcslen(Headers), *Data, *DataLength))
{
StringOut("HttpSendRequestW failed\n");
return FALSE;
}
HeapFree(hProcessHeap, 0, *Data);
/* Get the response */
if(!InternetQueryDataAvailable(hHTTPRequest, DataLength, 0, 0))
{
StringOut("InternetQueryDataAvailable failed\n");
return FALSE;
}
*Data = HeapAlloc(hProcessHeap, 0, *DataLength + 1);
if(!InternetReadFile(hHTTPRequest, *Data, *DataLength, DataLength))
{
StringOut("InternetReadFile failed\n");
return FALSE;
}
(*Data)[*DataLength] = 0;
InternetCloseHandle(hHTTPRequest);
InternetCloseHandle(hHTTP);
InternetCloseHandle(hInet);
return TRUE;
}
/**
* Determines whether a string contains entirely numeric values.
*
* @param Input
* The string to check.
*
* @return
* TRUE if the string is entirely numeric, FALSE otherwise.
*/
static BOOL
IsNumber(PCHAR Input)
{
do
{
if(!isdigit(*Input))
return FALSE;
++Input;
}
while(*Input);
return TRUE;
}
/**
* Requests a Test ID from the web service for our test run.
*
* @param TestType
* Value from the TESTTYPES enum indicating the type of test we are about to submit.
*
* @return
* Returns the Test ID as a CHAR array if successful or NULL otherwise.
*/
PCHAR
GetTestID(TESTTYPES TestType)
{
const CHAR GetTestIDAction[] = "gettestid";
DWORD DataLength;
PCHAR Data;
/* Build the full request string */
DataLength = sizeof(ActionProp) - 1 + sizeof(GetTestIDAction) - 1;
DataLength += strlen(AuthenticationRequestString) + strlen(SystemInfoRequestString);
DataLength += sizeof(TestTypeProp) - 1;
switch(TestType)
{
case WineTest:
DataLength += sizeof(WineTestType) - 1;
break;
}
Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
strcpy(Data, ActionProp);
strcat(Data, GetTestIDAction);
strcat(Data, AuthenticationRequestString);
strcat(Data, SystemInfoRequestString);
strcat(Data, TestTypeProp);
switch(TestType)
{
case WineTest:
strcat(Data, WineTestType);
break;
}
if(!IntDoRequest(&Data, &DataLength))
return NULL;
/* Verify that this is really a number */
if(!IsNumber(Data))
{
StringOut("Expected Test ID, but received:\n");
StringOut(Data);
StringOut("\n");
HeapFree(hProcessHeap, 0, Data);
return NULL;
}
return Data;
}
/**
* Requests a Suite ID from the web service for our module/test combination.
*
* @param TestType
* Value from the TESTTYPES enum indicating the type of test we are about to submit.
*
* @param TestData
* Pointer to a *_GETSUITEID_DATA structure appropriate for our selected test type.
* Contains other input information for this request.
*
* @return
* Returns the Suite ID as a CHAR array if successful or NULL otherwise.
*/
PCHAR
GetSuiteID(TESTTYPES TestType, const PVOID TestData)
{
const CHAR GetSuiteIDAction[] = "getsuiteid";
const CHAR ModuleProp[] = "&module=";
const CHAR TestProp[] = "&test=";
DWORD DataLength;
PCHAR Data;
PWINE_GETSUITEID_DATA WineData;
DataLength = sizeof(ActionProp) - 1 + sizeof(GetSuiteIDAction) - 1;
DataLength += strlen(AuthenticationRequestString);
DataLength += sizeof(TestTypeProp) - 1;
switch(TestType)
{
case WineTest:
DataLength += sizeof(WineTestType) - 1;
WineData = (PWINE_GETSUITEID_DATA)TestData;
DataLength += sizeof(ModuleProp) - 1;
DataLength += strlen(WineData->Module);
DataLength += sizeof(TestProp) - 1;
DataLength += strlen(WineData->Test);
break;
}
Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
strcpy(Data, ActionProp);
strcat(Data, GetSuiteIDAction);
strcat(Data, AuthenticationRequestString);
strcat(Data, TestTypeProp);
switch(TestType)
{
case WineTest:
strcat(Data, WineTestType);
/* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
WineData = (PWINE_GETSUITEID_DATA)TestData;
strcat(Data, ModuleProp);
strcat(Data, WineData->Module);
strcat(Data, TestProp);
strcat(Data, WineData->Test);
break;
}
if(!IntDoRequest(&Data, &DataLength))
return NULL;
/* Verify that this is really a number */
if(!IsNumber(Data))
{
StringOut("Expected Suite ID, but received:\n");
StringOut(Data);
StringOut("\n");
HeapFree(hProcessHeap, 0, Data);
return NULL;
}
return Data;
}
/**
* Submits the result of one test call to the web service.
*
* @param TestType
* Value from the TESTTYPES enum indicating the type of test we are about to submit.
*
* @param TestData
* Pointer to a *_SUBMIT_DATA structure appropriate for our selected test type.
* Contains other input information for this request.
*
* @return
* TRUE if everything went well, FALSE otherwise.
*/
BOOL
Submit(TESTTYPES TestType, const PVOID TestData)
{
const CHAR SubmitAction[] = "submit";
const CHAR SuiteIDProp[] = "&suiteid=";
const CHAR LogProp[] = "&log=";
DWORD DataLength;
PCHAR Data;
PCHAR pData;
PGENERAL_SUBMIT_DATA GeneralData;
PWINE_SUBMIT_DATA WineData;
/* Compute the full length of the POST data */
DataLength = sizeof(ActionProp) - 1 + sizeof(SubmitAction) - 1;
DataLength += strlen(AuthenticationRequestString);
GeneralData = (PGENERAL_SUBMIT_DATA)TestData;
DataLength += sizeof(TestIDProp) - 1;
DataLength += strlen(GeneralData->TestID);
DataLength += sizeof(SuiteIDProp) - 1;
DataLength += strlen(GeneralData->SuiteID);
/* The rest of the POST data depends on the test type */
DataLength += sizeof(TestTypeProp) - 1;
switch(TestType)
{
case WineTest:
DataLength += sizeof(WineTestType) - 1;
WineData = (PWINE_SUBMIT_DATA)TestData;
DataLength += sizeof(LogProp) - 1;
DataLength += 3 * strlen(WineData->Log);
break;
}
/* Now collect all the POST data */
Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
strcpy(Data, ActionProp);
strcat(Data, SubmitAction);
strcat(Data, AuthenticationRequestString);
strcat(Data, TestIDProp);
strcat(Data, GeneralData->TestID);
strcat(Data, SuiteIDProp);
strcat(Data, GeneralData->SuiteID);
strcat(Data, TestTypeProp);
switch(TestType)
{
case WineTest:
strcat(Data, WineTestType);
/* Stupid GCC and MSVC: WineData is already initialized above, still it's reported as a potentially uninitialized variable :-( */
WineData = (PWINE_SUBMIT_DATA)TestData;
strcat(Data, LogProp);
pData = Data + strlen(Data);
EscapeString(pData, WineData->Log);
break;
}
/* DataLength still contains the maximum length of the buffer, but not the actual data length we need for the request.
Determine that one now. */
DataLength = strlen(Data);
/* Send all the stuff */
if(!IntDoRequest(&Data, &DataLength))
return FALSE;
/* Output the response */
StringOut("The server responded:\n");
StringOut(Data);
StringOut("\n");
if(!strcmp(Data, "OK"))
return TRUE;
return FALSE;
}
/**
* Finishes a test run for the web service.
*
* @param TestType
* Value from the TESTTYPES enum indicating the type of test we are about to submit.
*
* @param TestData
* Pointer to a *_FINISH_DATA structure appropriate for our selected test type.
* Contains other input information for this request.
*
* @return
* TRUE if everything went well, FALSE otherwise.
*/
BOOL
Finish(TESTTYPES TestType, const PVOID TestData)
{
const CHAR FinishAction[] = "finish";
DWORD DataLength;
PCHAR Data;
PGENERAL_FINISH_DATA GeneralData;
/* Build the full request string */
DataLength = sizeof(ActionProp) - 1 + sizeof(FinishAction) - 1;
DataLength += strlen(AuthenticationRequestString);
GeneralData = (PGENERAL_FINISH_DATA)TestData;
DataLength += sizeof(TestIDProp) - 1;
DataLength += strlen(GeneralData->TestID);
DataLength += sizeof(TestTypeProp) - 1;
switch(TestType)
{
case WineTest:
DataLength += sizeof(WineTestType) - 1;
break;
}
Data = HeapAlloc(hProcessHeap, 0, DataLength + 1);
strcpy(Data, ActionProp);
strcat(Data, FinishAction);
strcat(Data, AuthenticationRequestString);
strcat(Data, TestIDProp);
strcat(Data, GeneralData->TestID);
strcat(Data, TestTypeProp);
switch(TestType)
{
case WineTest:
strcat(Data, WineTestType);
break;
}
if(!IntDoRequest(&Data, &DataLength))
return FALSE;
if(!strcmp(Data, "OK"))
return TRUE;
return FALSE;
}