mirror of
https://github.com/reactos/reactos.git
synced 2025-08-04 02:45:41 +00:00
Introducing the "ReactOS Automatic Testing Utility", superseding our current syssetup/cmd/dbgprint hack for running automatic regression tests.
Without any parameters, it mostly works the same as our current solution, but all in a standalone application. Adding the /w parameter will submit all results to the web service committed in my previous commit. The application would also make it possible to run Wine Tests regularly on a Windows machine and submitting the results. This would make sure that all Wine tests also pass under Windows. svn path=/trunk/; revision=38580
This commit is contained in:
parent
45661165b8
commit
d2148478c6
8 changed files with 1325 additions and 0 deletions
|
@ -13,6 +13,9 @@
|
|||
<directory name="regtests_by_casper">
|
||||
<xi:include href="regtests_by_casper/directory.rbuild" />
|
||||
</directory>
|
||||
<directory name="rosautotest">
|
||||
<xi:include href="rosautotest/rosautotest.rbuild" />
|
||||
</directory>
|
||||
<directory name="tests">
|
||||
<xi:include href="tests/directory.rbuild" />
|
||||
</directory>
|
||||
|
|
326
rostests/rosautotest/main.c
Normal file
326
rostests/rosautotest/main.c
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Automatic Testing Utility
|
||||
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
|
||||
* PURPOSE: Main implementation file
|
||||
* COPYRIGHT: Copyright 2008-2009 Colin Finck <colin@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
typedef void (WINAPI *GETSYSINFO)(LPSYSTEM_INFO);
|
||||
|
||||
APP_OPTIONS AppOptions = {0};
|
||||
HANDLE hProcessHeap;
|
||||
PCHAR AuthenticationRequestString = NULL;
|
||||
PCHAR SystemInfoRequestString = NULL;
|
||||
|
||||
/**
|
||||
* Gets a value from a specified INI file and returns it converted to ASCII.
|
||||
*
|
||||
* @param AppName
|
||||
* The INI section to look in (lpAppName parameter passed to GetPrivateProfileStringW)
|
||||
*
|
||||
* @param KeyName
|
||||
* The key to look for in the specified section (lpKeyName parameter passed to GetPrivateProfileStringW)
|
||||
*
|
||||
* @param FileName
|
||||
* The path to the INI file
|
||||
*
|
||||
* @param ReturnedValue
|
||||
* Pointer to a CHAR pointer, which will receive the read and converted value.
|
||||
* The caller needs to HeapFree that value manually.
|
||||
*
|
||||
* @return
|
||||
* Returns the string length of the read value (in characters) or zero if we didn't get any value.
|
||||
*/
|
||||
static DWORD
|
||||
IntGetINIValueA(PCWCH AppName, PCWCH KeyName, PCWCH FileName, char** ReturnedValue)
|
||||
{
|
||||
DWORD Length;
|
||||
WCHAR Buffer[2048];
|
||||
|
||||
/* Load the value into a temporary Unicode buffer */
|
||||
Length = GetPrivateProfileStringW(AppName, KeyName, NULL, Buffer, sizeof(Buffer) / sizeof(WCHAR), FileName);
|
||||
|
||||
if(!Length)
|
||||
return 0;
|
||||
|
||||
/* Convert the string to ANSI charset */
|
||||
*ReturnedValue = HeapAlloc(hProcessHeap, 0, Length + 1);
|
||||
WideCharToMultiByte(CP_ACP, 0, Buffer, Length + 1, *ReturnedValue, Length + 1, NULL, NULL);
|
||||
|
||||
return Length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the username and password hash from the "rosautotest.ini" file if the user enabled submitting the results to the web service.
|
||||
* The "rosautotest.ini" file should look like this:
|
||||
*
|
||||
* [Login]
|
||||
* UserName=TestMan
|
||||
* PasswordHash=1234567890abcdef1234567890abcdef
|
||||
*/
|
||||
static BOOL
|
||||
IntGetConfigurationValues()
|
||||
{
|
||||
const CHAR PasswordHashProp[] = "&passwordhash=";
|
||||
const CHAR UserNameProp[] = "&username=";
|
||||
|
||||
DWORD DataLength;
|
||||
DWORD Length;
|
||||
PCHAR PasswordHash;
|
||||
PCHAR UserName;
|
||||
WCHAR ConfigFile[MAX_PATH];
|
||||
|
||||
/* We only need this if the results are going to be submitted */
|
||||
if(!AppOptions.Submit)
|
||||
return TRUE;
|
||||
|
||||
/* Build the path to the configuration file */
|
||||
Length = GetWindowsDirectoryW(ConfigFile, MAX_PATH);
|
||||
wcscpy(&ConfigFile[Length], L"\\rosautotest.ini");
|
||||
|
||||
/* Check if it exists */
|
||||
if(GetFileAttributesW(ConfigFile) == INVALID_FILE_ATTRIBUTES)
|
||||
return FALSE;
|
||||
|
||||
/* Get the required length of the authentication request string */
|
||||
DataLength = sizeof(UserNameProp) - 1;
|
||||
Length = IntGetINIValueA(L"Login", L"UserName", ConfigFile, &UserName);
|
||||
|
||||
if(!Length)
|
||||
{
|
||||
StringOut("UserName is missing in the configuration file\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Some characters might need to be escaped and an escaped character takes 3 bytes */
|
||||
DataLength += 3 * Length;
|
||||
|
||||
DataLength += sizeof(PasswordHashProp) - 1;
|
||||
Length = IntGetINIValueA(L"Login", L"PasswordHash", ConfigFile, &PasswordHash);
|
||||
|
||||
if(!Length)
|
||||
{
|
||||
StringOut("PasswordHash is missing in the configuration file\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DataLength += 3 * Length;
|
||||
|
||||
/* Build the string */
|
||||
AuthenticationRequestString = HeapAlloc(hProcessHeap, 0, DataLength + 1);
|
||||
|
||||
strcpy(AuthenticationRequestString, UserNameProp);
|
||||
EscapeString(&AuthenticationRequestString[strlen(AuthenticationRequestString)], UserName);
|
||||
|
||||
strcat(AuthenticationRequestString, PasswordHashProp);
|
||||
EscapeString(&AuthenticationRequestString[strlen(AuthenticationRequestString)], PasswordHash);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines on which platform we're running on.
|
||||
* Prepares the appropriate request strings needed if we want to submit test results to the web service.
|
||||
*/
|
||||
static BOOL
|
||||
IntGetBuildAndPlatform()
|
||||
{
|
||||
const CHAR PlatformProp[] = "&platform=";
|
||||
const CHAR RevisionProp[] = "&revision=";
|
||||
|
||||
CHAR BuildNo[BUILDNO_LENGTH];
|
||||
CHAR Platform[PLATFORM_LENGTH];
|
||||
CHAR PlatformArchitecture[3];
|
||||
CHAR ProductType;
|
||||
DWORD DataLength;
|
||||
GETSYSINFO GetSysInfo;
|
||||
HANDLE hKernel32;
|
||||
OSVERSIONINFOEXW os;
|
||||
SYSTEM_INFO si;
|
||||
WCHAR WindowsDirectory[MAX_PATH];
|
||||
|
||||
/* Get the build from the define */
|
||||
_ultoa(KERNEL_VERSION_BUILD_HEX, BuildNo, 10);
|
||||
|
||||
/* Check if we are running under ReactOS from the SystemRoot directory */
|
||||
GetWindowsDirectoryW(WindowsDirectory, MAX_PATH);
|
||||
|
||||
if(!_wcsnicmp(&WindowsDirectory[3], L"reactos", 7))
|
||||
{
|
||||
/* Yes, we are most-probably under ReactOS */
|
||||
strcpy(Platform, "reactos");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No, then use the info from GetVersionExW */
|
||||
os.dwOSVersionInfoSize = sizeof(os);
|
||||
|
||||
if(!GetVersionExW((LPOSVERSIONINFOW)&os))
|
||||
{
|
||||
StringOut("GetVersionExW failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(os.dwMajorVersion < 5)
|
||||
{
|
||||
StringOut("Application requires at least Windows 2000!\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(os.wProductType == VER_NT_WORKSTATION)
|
||||
ProductType = 'w';
|
||||
else
|
||||
ProductType = 's';
|
||||
|
||||
/* Print all necessary identification information into the Platform string */
|
||||
sprintf(Platform, "%lu.%lu.%lu.%u.%u.%c", os.dwMajorVersion, os.dwMinorVersion, os.dwBuildNumber, os.wServicePackMajor, os.wServicePackMinor, ProductType);
|
||||
}
|
||||
|
||||
/* We also need to know about the processor architecture.
|
||||
To retrieve this information accurately, check whether "GetNativeSystemInfo" is exported and use it then, otherwise fall back to "GetSystemInfo". */
|
||||
hKernel32 = GetModuleHandleW(L"KERNEL32.DLL");
|
||||
GetSysInfo = (GETSYSINFO)GetProcAddress(hKernel32, "GetNativeSystemInfo");
|
||||
|
||||
if(!GetSysInfo)
|
||||
GetSysInfo = (GETSYSINFO)GetProcAddress(hKernel32, "GetSystemInfo");
|
||||
|
||||
GetSysInfo(&si);
|
||||
|
||||
PlatformArchitecture[0] = '.';
|
||||
_ultoa(si.wProcessorArchitecture, &PlatformArchitecture[1], 10);
|
||||
PlatformArchitecture[2] = 0;
|
||||
strcat(Platform, PlatformArchitecture);
|
||||
|
||||
/* Get the required length of the system info request string */
|
||||
DataLength = sizeof(RevisionProp) - 1;
|
||||
DataLength += strlen(BuildNo);
|
||||
DataLength += sizeof(PlatformProp) - 1;
|
||||
DataLength += strlen(Platform);
|
||||
|
||||
/* Now build the string */
|
||||
SystemInfoRequestString = HeapAlloc(hProcessHeap, 0, DataLength + 1);
|
||||
strcpy(SystemInfoRequestString, RevisionProp);
|
||||
strcat(SystemInfoRequestString, BuildNo);
|
||||
strcat(SystemInfoRequestString, PlatformProp);
|
||||
strcat(SystemInfoRequestString, Platform);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the application usage.
|
||||
*/
|
||||
static VOID
|
||||
IntPrintUsage()
|
||||
{
|
||||
printf("rosautotest - ReactOS Automatic Testing Utility\n");
|
||||
printf("Usage: rosautotest [options] [module] [test]\n");
|
||||
printf(" options:\n");
|
||||
printf(" /? - Shows this help\n");
|
||||
printf(" /s - Shut down the system after finishing the tests\n");
|
||||
printf(" /w - Submit the results to the webservice\n");
|
||||
printf(" Requires a \"rosautotest.ini\" with valid login data.\n");
|
||||
printf("\n");
|
||||
printf(" module:\n");
|
||||
printf(" The module to be tested (i.e. \"advapi32\")\n");
|
||||
printf(" If this parameter is specified without any test parameter,\n");
|
||||
printf(" all tests of the specified module are run.\n");
|
||||
printf("\n");
|
||||
printf(" test:\n");
|
||||
printf(" The test to be run. Needs to be a test of the specified module.\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point
|
||||
*/
|
||||
int
|
||||
wmain(int argc, wchar_t* argv[])
|
||||
{
|
||||
int Result = 0;
|
||||
UINT i;
|
||||
|
||||
hProcessHeap = GetProcessHeap();
|
||||
|
||||
/* Parse the command line arguments */
|
||||
for(i = 1; i < (UINT)argc; i++)
|
||||
{
|
||||
if(argv[i][0] == '-' || argv[i][0] == '/')
|
||||
{
|
||||
switch(argv[i][1])
|
||||
{
|
||||
case 's':
|
||||
AppOptions.Shutdown = TRUE;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
AppOptions.Submit = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
Result = 1;
|
||||
/* Fall through */
|
||||
|
||||
case '?':
|
||||
IntPrintUsage();
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t Length;
|
||||
|
||||
/* Which parameter is this? */
|
||||
if(!AppOptions.Module)
|
||||
{
|
||||
/* Copy the parameter */
|
||||
Length = (wcslen(argv[i]) + 1) * sizeof(WCHAR);
|
||||
AppOptions.Module = HeapAlloc(hProcessHeap, 0, Length);
|
||||
memcpy(AppOptions.Module, argv[i], Length);
|
||||
}
|
||||
else if(!AppOptions.Test)
|
||||
{
|
||||
/* Copy the parameter converted to ASCII */
|
||||
Length = WideCharToMultiByte(CP_ACP, 0, argv[i], -1, NULL, 0, NULL, NULL);
|
||||
AppOptions.Test = HeapAlloc(hProcessHeap, 0, Length);
|
||||
WideCharToMultiByte(CP_ACP, 0, argv[i], -1, AppOptions.Test, Length, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = 1;
|
||||
IntPrintUsage();
|
||||
goto End;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!IntGetConfigurationValues() || !IntGetBuildAndPlatform() || !RunWineTests())
|
||||
{
|
||||
Result = 1;
|
||||
goto End;
|
||||
}
|
||||
|
||||
/* For sysreg */
|
||||
OutputDebugStringA("SYSREG_CHECKPOINT:THIRDBOOT_COMPLETE\n");
|
||||
|
||||
End:
|
||||
/* Cleanup */
|
||||
if(AppOptions.Module)
|
||||
HeapFree(hProcessHeap, 0, AppOptions.Module);
|
||||
|
||||
if(AppOptions.Test)
|
||||
HeapFree(hProcessHeap, 0, AppOptions.Test);
|
||||
|
||||
if(AuthenticationRequestString)
|
||||
HeapFree(hProcessHeap, 0, AuthenticationRequestString);
|
||||
|
||||
if(SystemInfoRequestString)
|
||||
HeapFree(hProcessHeap, 0, SystemInfoRequestString);
|
||||
|
||||
/* Shut down the system if requested */
|
||||
if(AppOptions.Shutdown && !ShutdownSystem())
|
||||
Result = 1;
|
||||
|
||||
return Result;
|
||||
}
|
81
rostests/rosautotest/precomp.h
Normal file
81
rostests/rosautotest/precomp.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/* Includes */
|
||||
#include <stdio.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <reason.h>
|
||||
#include <wininet.h>
|
||||
|
||||
#include <reactos/buildno.h>
|
||||
|
||||
/* Defines */
|
||||
#define BUFFER_BLOCKSIZE 2048
|
||||
#define BUILDNO_LENGTH 10
|
||||
#define PLATFORM_LENGTH 25
|
||||
#define SERVER_HOSTNAME L"localhost"
|
||||
#define SERVER_FILE L"testman/webservice/"
|
||||
|
||||
/* Enums */
|
||||
typedef enum _TESTTYPES
|
||||
{
|
||||
WineTest
|
||||
}
|
||||
TESTTYPES;
|
||||
|
||||
/* Structs */
|
||||
typedef struct _APP_OPTIONS
|
||||
{
|
||||
BOOL Shutdown;
|
||||
BOOL Submit;
|
||||
PWSTR Module;
|
||||
PCHAR Test;
|
||||
}
|
||||
APP_OPTIONS, *PAPP_OPTIONS;
|
||||
|
||||
typedef struct _WINE_GETSUITEID_DATA
|
||||
{
|
||||
PCHAR Module;
|
||||
PCHAR Test;
|
||||
}
|
||||
WINE_GETSUITEID_DATA, *PWINE_GETSUITEID_DATA;
|
||||
|
||||
typedef struct _GENERAL_SUBMIT_DATA
|
||||
{
|
||||
PCHAR TestID;
|
||||
PCHAR SuiteID;
|
||||
}
|
||||
GENERAL_SUBMIT_DATA, *PGENERAL_SUBMIT_DATA;
|
||||
|
||||
typedef struct _WINE_SUBMIT_DATA
|
||||
{
|
||||
GENERAL_SUBMIT_DATA General;
|
||||
PCHAR Log;
|
||||
}
|
||||
WINE_SUBMIT_DATA, *PWINE_SUBMIT_DATA;
|
||||
|
||||
typedef struct _GENERAL_FINISH_DATA
|
||||
{
|
||||
PCHAR TestID;
|
||||
}
|
||||
GENERAL_FINISH_DATA, *PGENERAL_FINISH_DATA;
|
||||
|
||||
/* main.c */
|
||||
extern APP_OPTIONS AppOptions;
|
||||
extern HANDLE hProcessHeap;
|
||||
extern PCHAR AuthenticationRequestString;
|
||||
extern PCHAR SystemInfoRequestString;
|
||||
|
||||
/* shutdown.c */
|
||||
BOOL ShutdownSystem();
|
||||
|
||||
/* tools.c */
|
||||
VOID EscapeString(PCHAR Output, PCHAR Input);
|
||||
VOID StringOut(PCHAR String);
|
||||
|
||||
/* webservice.c */
|
||||
PCHAR GetTestID(TESTTYPES TestType);
|
||||
PCHAR GetSuiteID(TESTTYPES TestType, const PVOID TestData);
|
||||
BOOL Submit(TESTTYPES TestType, const PVOID TestData);
|
||||
BOOL Finish(TESTTYPES TestType, const PVOID TestData);
|
||||
|
||||
/* winetests.c */
|
||||
BOOL RunWineTests();
|
15
rostests/rosautotest/rosautotest.rbuild
Normal file
15
rostests/rosautotest/rosautotest.rbuild
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
|
||||
<module name="rosautotest" type="win32cui" installbase="system32" installname="rosautotest.exe" unicode="yes">
|
||||
<include base="rosautotest">.</include>
|
||||
<library>advapi32</library>
|
||||
<library>kernel32</library>
|
||||
<library>user32</library>
|
||||
<library>wininet</library>
|
||||
<file>main.c</file>
|
||||
<file>shutdown.c</file>
|
||||
<file>tools.c</file>
|
||||
<file>webservice.c</file>
|
||||
<file>winetests.c</file>
|
||||
<pch>precomp.h</pch>
|
||||
</module>
|
52
rostests/rosautotest/shutdown.c
Normal file
52
rostests/rosautotest/shutdown.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Automatic Testing Utility
|
||||
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
|
||||
* PURPOSE: Helper function for shutting down the system
|
||||
* COPYRIGHT: Copyright 2008-2009 Colin Finck <colin@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
/**
|
||||
* Shuts down the system.
|
||||
*
|
||||
* @return
|
||||
* TRUE if everything went well, FALSE if there was a problem while trying to shut down the system.
|
||||
*/
|
||||
BOOL ShutdownSystem()
|
||||
{
|
||||
HANDLE hToken;
|
||||
TOKEN_PRIVILEGES Privileges;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
|
||||
{
|
||||
StringOut("OpenProcessToken failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Get the LUID for the Shutdown privilege */
|
||||
if (!LookupPrivilegeValueW(NULL, SE_SHUTDOWN_NAME, &Privileges.Privileges[0].Luid))
|
||||
{
|
||||
StringOut("LookupPrivilegeValue failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Assign the Shutdown privilege to our process */
|
||||
Privileges.PrivilegeCount = 1;
|
||||
Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (!AdjustTokenPrivileges(hToken, FALSE, &Privileges, 0, NULL, NULL))
|
||||
{
|
||||
StringOut("AdjustTokenPrivileges failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Finally shut down the system */
|
||||
if(!ExitWindowsEx(EWX_POWEROFF, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED))
|
||||
{
|
||||
StringOut("ExitWindowsEx failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
57
rostests/rosautotest/tools.c
Normal file
57
rostests/rosautotest/tools.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Automatic Testing Utility
|
||||
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
|
||||
* PURPOSE: Various helper functions
|
||||
* COPYRIGHT: Copyright 2008-2009 Colin Finck <colin@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
/**
|
||||
* Escapes a string according to RFC 1738.
|
||||
* Required for passing parameters to the web service.
|
||||
*
|
||||
* @param Output
|
||||
* Pointer to a CHAR array, which will receive the escaped string.
|
||||
* The output buffer must be large enough to hold the full escaped string. You're on the safe side
|
||||
* if you make the output buffer three times as large as the input buffer.
|
||||
*
|
||||
* @param Input
|
||||
* Pointer to a CHAR array, which contains the input buffer to escape.
|
||||
*/
|
||||
VOID
|
||||
EscapeString(PCHAR Output, PCHAR Input)
|
||||
{
|
||||
const CHAR HexCharacters[] = "0123456789ABCDEF";
|
||||
|
||||
do
|
||||
{
|
||||
if(strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~", *Input) )
|
||||
{
|
||||
/* It's a character we don't need to escape, just add it to the output string */
|
||||
*Output++ = *Input;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We need to escape this character */
|
||||
*Output++ = '%';
|
||||
*Output++ = HexCharacters[((UCHAR)*Input >> 4) % 16];
|
||||
*Output++ = HexCharacters[(UCHAR)*Input % 16];
|
||||
}
|
||||
} while(*++Input);
|
||||
|
||||
*Output = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a string through the standard output and the debug output.
|
||||
*
|
||||
* @param String
|
||||
* The string to output
|
||||
*/
|
||||
VOID
|
||||
StringOut(PCHAR String)
|
||||
{
|
||||
printf(String);
|
||||
OutputDebugStringA(String);
|
||||
}
|
417
rostests/rosautotest/webservice.c
Normal file
417
rostests/rosautotest/webservice.c
Normal file
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* 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);
|
||||
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);
|
||||
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;
|
||||
}
|
374
rostests/rosautotest/winetests.c
Normal file
374
rostests/rosautotest/winetests.c
Normal file
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Automatic Testing Utility
|
||||
* LICENSE: GNU GPLv2 or any later version as published by the Free Software Foundation
|
||||
* PURPOSE: Running Wine Tests automatically
|
||||
* COPYRIGHT: Copyright 2008-2009 Colin Finck <colin@reactos.org>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
|
||||
/**
|
||||
* Runs a specific test for a specific module.
|
||||
* If we get results for a test, they are submitted to the Web Service.
|
||||
*
|
||||
* @param CommandLine
|
||||
* Command line to run (should be the path to a module's test suite together with a parameter for the specified test)
|
||||
*
|
||||
* @param hReadPipe
|
||||
* Handle to the Read Pipe set up in RunWineTests.
|
||||
*
|
||||
* @param StartupInfo
|
||||
* Pointer to the StartupInfo structure set up in RunWineTests.
|
||||
*
|
||||
* @param GetSuiteIDData
|
||||
* Pointer to the GetSuiteIDData structure set up in IntRunModuleTests.
|
||||
*
|
||||
* @param SubmitData
|
||||
* Pointer to the SubmitData structure set up in RunWineTests.
|
||||
*
|
||||
* @return
|
||||
* TRUE if everything went well, FALSE otherwise.
|
||||
*/
|
||||
static BOOL
|
||||
IntRunTest(PWSTR CommandLine, HANDLE hReadPipe, LPSTARTUPINFOW StartupInfo, PWINE_GETSUITEID_DATA GetSuiteIDData, PWINE_SUBMIT_DATA SubmitData)
|
||||
{
|
||||
BOOL BreakLoop = FALSE;
|
||||
DWORD BytesAvailable;
|
||||
DWORD LogAvailable = 0;
|
||||
DWORD LogLength = 0;
|
||||
DWORD LogPosition = 0;
|
||||
DWORD Temp;
|
||||
PCHAR Buffer;
|
||||
PROCESS_INFORMATION ProcessInfo;
|
||||
|
||||
if(AppOptions.Submit)
|
||||
{
|
||||
/* Allocate one block for the log */
|
||||
SubmitData->Log = HeapAlloc(hProcessHeap, 0, BUFFER_BLOCKSIZE);
|
||||
LogAvailable = BUFFER_BLOCKSIZE;
|
||||
LogLength = BUFFER_BLOCKSIZE;
|
||||
}
|
||||
|
||||
/* Execute the test */
|
||||
StringOut("Running Wine Test, Module: ");
|
||||
StringOut(GetSuiteIDData->Module);
|
||||
StringOut(", Test: ");
|
||||
StringOut(GetSuiteIDData->Test);
|
||||
StringOut("\n");
|
||||
|
||||
if(!CreateProcessW(NULL, CommandLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, StartupInfo, &ProcessInfo))
|
||||
{
|
||||
StringOut("CreateProcessW for running the test failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Receive all the data from the pipe */
|
||||
do
|
||||
{
|
||||
/* When the application finished, make sure that we peek the pipe one more time, so that we get all data.
|
||||
If the following condition would be the while() condition, we might hit a race condition:
|
||||
- We check for data with PeekNamedPipe -> no data available
|
||||
- The application outputs its data and finishes
|
||||
- WaitForSingleObject reports that the application has finished and we break the loop without receiving any data
|
||||
*/
|
||||
if(WaitForSingleObject(ProcessInfo.hProcess, 0) == WAIT_OBJECT_0)
|
||||
BreakLoop = TRUE;
|
||||
|
||||
if(!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesAvailable, NULL))
|
||||
{
|
||||
StringOut("PeekNamedPipe failed for the test run\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(BytesAvailable)
|
||||
{
|
||||
/* There is data, so get it and output it */
|
||||
Buffer = HeapAlloc(hProcessHeap, 0, BytesAvailable + 1);
|
||||
|
||||
if(!ReadFile(hReadPipe, Buffer, BytesAvailable, &Temp, NULL))
|
||||
{
|
||||
StringOut("ReadFile failed for the test run\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Output all test output through StringOut, even while the test is still running */
|
||||
Buffer[BytesAvailable] = 0;
|
||||
StringOut(Buffer);
|
||||
|
||||
if(AppOptions.Submit)
|
||||
{
|
||||
/* Also store it in the buffer */
|
||||
if(BytesAvailable > LogAvailable)
|
||||
{
|
||||
/* Allocate enough new blocks to hold all available data */
|
||||
Temp = ((BytesAvailable - LogAvailable) / BUFFER_BLOCKSIZE + 1) * BUFFER_BLOCKSIZE;
|
||||
LogAvailable += Temp;
|
||||
LogLength += Temp;
|
||||
SubmitData->Log = HeapReAlloc(hProcessHeap, 0, SubmitData->Log, LogLength);
|
||||
}
|
||||
|
||||
memcpy(&SubmitData->Log[LogPosition], Buffer, BytesAvailable);
|
||||
LogPosition += BytesAvailable;
|
||||
LogAvailable -= BytesAvailable;
|
||||
}
|
||||
|
||||
HeapFree(hProcessHeap, 0, Buffer);
|
||||
}
|
||||
}
|
||||
while(!BreakLoop);
|
||||
|
||||
if(AppOptions.Submit)
|
||||
{
|
||||
SubmitData->Log[LogLength - LogAvailable] = 0;
|
||||
|
||||
/* If we got any output, submit it to the web service */
|
||||
if(*SubmitData->Log)
|
||||
{
|
||||
/* We don't want to waste any ID's, so only request them if we can be sure that we have results to submit. */
|
||||
|
||||
/* Get a Test ID if we don't have one yet */
|
||||
if(!SubmitData->General.TestID)
|
||||
{
|
||||
SubmitData->General.TestID = GetTestID(WineTest);
|
||||
|
||||
if(!SubmitData->General.TestID)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Get a Suite ID for this combination */
|
||||
SubmitData->General.SuiteID = GetSuiteID(WineTest, GetSuiteIDData);
|
||||
|
||||
if(!SubmitData->General.SuiteID)
|
||||
return FALSE;
|
||||
|
||||
/* Submit the stuff */
|
||||
Submit(WineTest, SubmitData);
|
||||
|
||||
/* Cleanup */
|
||||
HeapFree(hProcessHeap, 0, SubmitData->General.SuiteID);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
HeapFree(hProcessHeap, 0, SubmitData->Log);
|
||||
}
|
||||
|
||||
StringOut("\n\n");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the desired tests for a specified module.
|
||||
*
|
||||
* @param File
|
||||
* The file name of the module's test suite.
|
||||
*
|
||||
* @param FilePath
|
||||
* The full path to the file of the module's test suite.
|
||||
*
|
||||
* @param hReadPipe
|
||||
* Handle to the Read Pipe set up in RunWineTests.
|
||||
*
|
||||
* @param StartupInfo
|
||||
* Pointer to the StartupInfo structure set up in RunWineTests.
|
||||
*
|
||||
* @param SubmitData
|
||||
* Pointer to the SubmitData structure set up in RunWineTests.
|
||||
*
|
||||
* @return
|
||||
* TRUE if everything went well, FALSE otherwise.
|
||||
*/
|
||||
static BOOL
|
||||
IntRunModuleTests(PWSTR File, PWSTR FilePath, HANDLE hReadPipe, LPSTARTUPINFOW StartupInfo, PWINE_SUBMIT_DATA SubmitData)
|
||||
{
|
||||
DWORD BytesAvailable;
|
||||
DWORD Length;
|
||||
DWORD Temp;
|
||||
PCHAR Buffer;
|
||||
PCHAR pStart;
|
||||
PCHAR pEnd;
|
||||
PROCESS_INFORMATION ProcessInfo;
|
||||
size_t FilePosition;
|
||||
WINE_GETSUITEID_DATA GetSuiteIDData;
|
||||
|
||||
/* Build the full command line */
|
||||
FilePosition = wcslen(FilePath);
|
||||
FilePath[FilePosition++] = ' ';
|
||||
FilePath[FilePosition] = 0;
|
||||
wcscat(FilePath, L"--list");
|
||||
|
||||
/* Store the tested module name */
|
||||
Length = wcschr(File, L'_') - File;
|
||||
GetSuiteIDData.Module = HeapAlloc(hProcessHeap, 0, Length + 1);
|
||||
WideCharToMultiByte(CP_ACP, 0, File, Length, GetSuiteIDData.Module, Length, NULL, NULL);
|
||||
GetSuiteIDData.Module[Length] = 0;
|
||||
|
||||
/* Start the process for getting all available tests */
|
||||
if(!CreateProcessW(NULL, FilePath, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, StartupInfo, &ProcessInfo))
|
||||
{
|
||||
StringOut("CreateProcessW for getting the available tests failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Wait till this process ended */
|
||||
if(WaitForSingleObject(ProcessInfo.hProcess, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
StringOut("WaitForSingleObject failed for the test list\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Read the output data into a buffer */
|
||||
if(!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesAvailable, NULL))
|
||||
{
|
||||
StringOut("PeekNamedPipe failed for the test list\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Buffer = HeapAlloc(hProcessHeap, 0, BytesAvailable);
|
||||
|
||||
if(!ReadFile(hReadPipe, Buffer, BytesAvailable, &Temp, NULL))
|
||||
{
|
||||
StringOut("ReadFile failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Jump to the first available test */
|
||||
pStart = strchr(Buffer, '\n');
|
||||
pStart += 5;
|
||||
|
||||
while(pStart < (Buffer + BytesAvailable))
|
||||
{
|
||||
/* Get start and end of this test name */
|
||||
pEnd = pStart;
|
||||
|
||||
while(*pEnd != '\r')
|
||||
++pEnd;
|
||||
|
||||
/* Store the test name */
|
||||
GetSuiteIDData.Test = HeapAlloc(hProcessHeap, 0, pEnd - pStart + 1);
|
||||
memcpy(GetSuiteIDData.Test, pStart, pEnd - pStart);
|
||||
GetSuiteIDData.Test[pEnd - pStart] = 0;
|
||||
|
||||
/* If the user gave us a test to run, we check whether the module's test suite really provides this test. */
|
||||
if(!AppOptions.Test || (AppOptions.Test && !strcmp(AppOptions.Test, GetSuiteIDData.Test)))
|
||||
{
|
||||
/* Build the command line for this test */
|
||||
Length = MultiByteToWideChar(CP_ACP, 0, pStart, pEnd - pStart, NULL, 0);
|
||||
MultiByteToWideChar(CP_ACP, 0, pStart, pEnd - pStart, &FilePath[FilePosition], Length * sizeof(WCHAR));
|
||||
FilePath[FilePosition + Length] = 0;
|
||||
|
||||
if(!IntRunTest(FilePath, hReadPipe, StartupInfo, &GetSuiteIDData, SubmitData))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
HeapFree(hProcessHeap, 0, GetSuiteIDData.Test);
|
||||
|
||||
/* Move to the next test */
|
||||
pStart = pEnd + 6;
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
HeapFree(hProcessHeap, 0, GetSuiteIDData.Module);
|
||||
HeapFree(hProcessHeap, 0, Buffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the Wine tests according to the options specified by the parameters.
|
||||
*
|
||||
* @return
|
||||
* TRUE if everything went well, FALSE otherwise.
|
||||
*/
|
||||
BOOL
|
||||
RunWineTests()
|
||||
{
|
||||
GENERAL_FINISH_DATA FinishData;
|
||||
HANDLE hFind;
|
||||
HANDLE hReadPipe;
|
||||
HANDLE hWritePipe;
|
||||
SECURITY_ATTRIBUTES SecurityAttributes;
|
||||
STARTUPINFOW StartupInfo = {0};
|
||||
size_t PathPosition;
|
||||
WCHAR FilePath[MAX_PATH];
|
||||
WIN32_FIND_DATAW fd;
|
||||
WINE_SUBMIT_DATA SubmitData = { {0} };
|
||||
|
||||
/* Create a pipe for getting the output of the tests */
|
||||
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
||||
SecurityAttributes.bInheritHandle = TRUE;
|
||||
SecurityAttributes.lpSecurityDescriptor = NULL;
|
||||
|
||||
if(!CreatePipe(&hReadPipe, &hWritePipe, &SecurityAttributes, 0))
|
||||
{
|
||||
StringOut("CreatePipe failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
StartupInfo.cb = sizeof(StartupInfo);
|
||||
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
StartupInfo.hStdOutput = hWritePipe;
|
||||
|
||||
/* Build the path for finding the tests */
|
||||
if(GetWindowsDirectoryW(FilePath, MAX_PATH) > MAX_PATH - 60)
|
||||
{
|
||||
StringOut("Windows directory path is too long\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
wcscat(FilePath, L"\\bin\\");
|
||||
PathPosition = wcslen(FilePath);
|
||||
|
||||
if(AppOptions.Module)
|
||||
{
|
||||
/* Find a test belonging to this module */
|
||||
wcscat(FilePath, AppOptions.Module);
|
||||
wcscat(FilePath, L"_*.exe");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Find all tests */
|
||||
wcscat(FilePath, L"*.exe");
|
||||
}
|
||||
|
||||
hFind = FindFirstFileW(FilePath, &fd);
|
||||
|
||||
if(hFind == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
StringOut("FindFirstFileW failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Run the tests */
|
||||
do
|
||||
{
|
||||
/* Build the full path to the test suite */
|
||||
wcscpy(&FilePath[PathPosition], fd.cFileName);
|
||||
|
||||
/* Run it */
|
||||
if(!IntRunModuleTests(fd.cFileName, FilePath, hReadPipe, &StartupInfo, &SubmitData))
|
||||
return FALSE;
|
||||
}
|
||||
while(FindNextFileW(hFind, &fd));
|
||||
|
||||
/* Cleanup */
|
||||
FindClose(hFind);
|
||||
|
||||
if(AppOptions.Submit && SubmitData.General.TestID)
|
||||
{
|
||||
/* We're done with the tests, so close this test run */
|
||||
FinishData.TestID = SubmitData.General.TestID;
|
||||
|
||||
if(!Finish(WineTest, &FinishData))
|
||||
return FALSE;
|
||||
|
||||
/* Cleanup */
|
||||
HeapFree(hProcessHeap, 0, FinishData.TestID);
|
||||
}
|
||||
|
||||
CloseHandle(hReadPipe);
|
||||
CloseHandle(hWritePipe);
|
||||
|
||||
return TRUE;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue