mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 09:13:01 +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">
|
<directory name="regtests_by_casper">
|
||||||
<xi:include href="regtests_by_casper/directory.rbuild" />
|
<xi:include href="regtests_by_casper/directory.rbuild" />
|
||||||
</directory>
|
</directory>
|
||||||
|
<directory name="rosautotest">
|
||||||
|
<xi:include href="rosautotest/rosautotest.rbuild" />
|
||||||
|
</directory>
|
||||||
<directory name="tests">
|
<directory name="tests">
|
||||||
<xi:include href="tests/directory.rbuild" />
|
<xi:include href="tests/directory.rbuild" />
|
||||||
</directory>
|
</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