Big testing system commit
authorColin Finck <colin@reactos.org>
Sat, 21 Mar 2009 01:39:04 +0000 (01:39 +0000)
committerColin Finck <colin@reactos.org>
Sat, 21 Mar 2009 01:39:04 +0000 (01:39 +0000)
rosautotest
- Rewrite rosautotest in C++
  Should increase maintainability and expandability, since most of the functionality is encapsulated in classes and there exist some abstract classes for further enhancements (i.e. new test types).
  Furthermore, due to the usage of STL strings, we don't need x lines anymore just for building a string out of several small parts.
- The new codebase made it fairly easy to implement a Crash Recovery feature based on a journal.
  If you start rosautotest with the /r option under ReactOS, it will keep a journal about the tests to run and the tests already ran. In case of a crash, it can just continue with the next test in the list then.
- Add some reasonable timeouts to avoid certain hangs in case a test crashes

sysreg2
- Make the necessary changes to sysreg2 to restart the VM in case of such a crash in 3rd stage, but set a maximum number of allowed crashes as well.
  Christoph, please test and review that on the Buildslave :-)
- Prepend all sysreg messages with [SYSREG] through a new function SysregPrintf, so the BuildBot aggregator script of testman can distinguish between debug output and sysreg messages.
- Put all header includes into the central header file "sysreg.h"
- Remove unnecessary libs from the Makefile

testman
- Change the testman Web Interface to show such crashes as CRASH in the Compare and Detail views.

svn path=/trunk/; revision=40147

34 files changed:
rostests/rosautotest/CConfiguration.cpp [new file with mode: 0644]
rostests/rosautotest/CConfiguration.h [new file with mode: 0644]
rostests/rosautotest/CFatalException.cpp [new file with mode: 0644]
rostests/rosautotest/CFatalException.h [new file with mode: 0644]
rostests/rosautotest/CInvalidParameterException.cpp [new file with mode: 0644]
rostests/rosautotest/CInvalidParameterException.h [new file with mode: 0644]
rostests/rosautotest/CJournaledTestList.cpp [new file with mode: 0644]
rostests/rosautotest/CJournaledTestList.h [new file with mode: 0644]
rostests/rosautotest/CProcess.cpp [new file with mode: 0644]
rostests/rosautotest/CProcess.h [new file with mode: 0644]
rostests/rosautotest/CSimpleException.cpp [new file with mode: 0644]
rostests/rosautotest/CSimpleException.h [new file with mode: 0644]
rostests/rosautotest/CTest.cpp [new file with mode: 0644]
rostests/rosautotest/CTest.h [new file with mode: 0644]
rostests/rosautotest/CTestInfo.cpp [new file with mode: 0644]
rostests/rosautotest/CTestInfo.h [new file with mode: 0644]
rostests/rosautotest/CTestList.cpp [new file with mode: 0644]
rostests/rosautotest/CTestList.h [new file with mode: 0644]
rostests/rosautotest/CVirtualTestList.cpp [new file with mode: 0644]
rostests/rosautotest/CVirtualTestList.h [new file with mode: 0644]
rostests/rosautotest/CWebService.cpp [new file with mode: 0644]
rostests/rosautotest/CWebService.h [new file with mode: 0644]
rostests/rosautotest/CWineTest.cpp [new file with mode: 0644]
rostests/rosautotest/CWineTest.h [new file with mode: 0644]
rostests/rosautotest/auto_array_ptr.h [new file with mode: 0644]
rostests/rosautotest/main.c [deleted file]
rostests/rosautotest/main.cpp [new file with mode: 0644]
rostests/rosautotest/precomp.h
rostests/rosautotest/rosautotest.rbuild
rostests/rosautotest/shutdown.cpp [moved from rostests/rosautotest/shutdown.c with 87% similarity]
rostests/rosautotest/tools.c [deleted file]
rostests/rosautotest/tools.cpp [new file with mode: 0644]
rostests/rosautotest/webservice.c [deleted file]
rostests/rosautotest/winetests.c [deleted file]

diff --git a/rostests/rosautotest/CConfiguration.cpp b/rostests/rosautotest/CConfiguration.cpp
new file mode 100644 (file)
index 0000000..9e57795
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class for managing all the configuration parameters
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+#define CONFIGURATION_FILENAMEA   "rosautotest.ini"
+#define CONFIGURATION_FILENAMEW   L"rosautotest.ini"
+
+typedef void (WINAPI *GETSYSINFO)(LPSYSTEM_INFO);
+
+/**
+ * Constructs an empty CConfiguration object
+ */
+CConfiguration::CConfiguration()
+{
+    WCHAR WindowsDirectory[MAX_PATH];
+
+    /* Zero-initialize variables */
+    m_CrashRecovery = false;
+    m_Shutdown = false;
+    m_Submit = false;
+
+    /* Check if we are running under ReactOS from the SystemRoot directory */
+    if(!GetWindowsDirectoryW(WindowsDirectory, MAX_PATH))
+        FATAL("GetWindowsDirectoryW failed");
+
+    m_IsReactOS = !_wcsnicmp(&WindowsDirectory[3], L"reactos", 7);
+}
+
+/**
+ * Parses the passed parameters and sets the appropriate configuration settings.
+ *
+ * @param argc
+ * The number of parameters (argc parameter of the wmain function)
+ *
+ * @param argv
+ * Pointer to a wchar_t array containing the parameters (argv parameter of the wmain function)
+ */
+void
+CConfiguration::ParseParameters(int argc, wchar_t* argv[])
+{
+    /* Parse the command line arguments */
+    for(int i = 1; i < argc; i++)
+    {
+        if(argv[i][0] == '-' || argv[i][0] == '/')
+        {
+            switch(argv[i][1])
+            {
+                case 'c':
+                    ++i;
+                    m_Comment = UnicodeToAscii(argv[i]);
+                    break;
+
+                case 'r':
+                    m_CrashRecovery = true;
+                    break;
+
+                case 's':
+                    m_Shutdown = true;
+                    break;
+
+                case 'w':
+                    m_Submit = true;
+                    break;
+
+                default:
+                    throw CInvalidParameterException();
+            }
+        }
+        else
+        {
+            /* Which parameter is this? */
+            if(m_Module.empty())
+            {
+                /* Copy the parameter */
+                m_Module = argv[i];
+            }
+            else if(m_Test.empty())
+            {
+                /* Copy the parameter converted to ASCII */
+                m_Test = UnicodeToAscii(argv[i]);
+            }
+            else
+            {
+                throw CInvalidParameterException();
+            }
+        }
+    }
+
+    /* The /r and /w options shouldn't be used in conjunction */
+    if(m_CrashRecovery && m_Submit)
+        throw CInvalidParameterException();
+}
+
+/**
+ * Gets information about the running system and sets the appropriate configuration settings.
+ */
+void
+CConfiguration::GetSystemInformation()
+{
+    char ProductType;
+    GETSYSINFO GetSysInfo;
+    HMODULE hKernel32;
+    OSVERSIONINFOEXW os;
+    stringstream ss;
+    SYSTEM_INFO si;
+
+    /* Get the build from the define */
+    ss << "&revision=";
+    ss << KERNEL_VERSION_BUILD_HEX;
+
+    ss << "&platform=";
+
+    if(m_IsReactOS)
+    {
+        ss << "reactos";
+    }
+    else
+    {
+        /* No, then use the info from GetVersionExW */
+        os.dwOSVersionInfoSize = sizeof(os);
+
+        if(!GetVersionExW((LPOSVERSIONINFOW)&os))
+            FATAL("GetVersionExW failed\n");
+
+        if(os.dwMajorVersion < 5)
+            EXCEPTION("Application requires at least Windows 2000!\n");
+
+        if(os.wProductType == VER_NT_WORKSTATION)
+            ProductType = 'w';
+        else
+            ProductType = 's';
+
+        /* Print all necessary identification information into the Platform string */
+        ss << 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);
+    ss << si.wProcessorArchitecture;
+
+    m_SystemInfoRequestString = ss.str();
+}
+
+/**
+ * Reads additional configuration options from the INI file.
+ *
+ * ParseParameters should be called before this function to get the desired result.
+ */
+void
+CConfiguration::GetConfigurationFromFile()
+{
+    DWORD Length;
+    string Value;
+    WCHAR ConfigFile[MAX_PATH];
+
+    /* Most values are only needed if we're going to submit anything */
+    if(m_Submit)
+    {
+        /* Build the path to the configuration file from the application's path */
+        GetModuleFileNameW(NULL, ConfigFile, MAX_PATH);
+        Length = wcsrchr(ConfigFile, '\\') - ConfigFile + 1;
+        wcscpy(&ConfigFile[Length], CONFIGURATION_FILENAMEW);
+
+        /* Check if it exists */
+        if(GetFileAttributesW(ConfigFile) == INVALID_FILE_ATTRIBUTES)
+            EXCEPTION("Missing \"" CONFIGURATION_FILENAMEA "\" configuration file!\n");
+
+        /* Get the user name */
+        m_AuthenticationRequestString = "&username=";
+        Value = GetINIValue(L"Login", L"UserName", ConfigFile);
+
+        if(Value.empty())
+            EXCEPTION("UserName is missing in the configuration file\n");
+
+        m_AuthenticationRequestString += EscapeString(Value);
+
+        /* Get the password */
+        m_AuthenticationRequestString += "&password=";
+        Value = GetINIValue(L"Login", L"Password", ConfigFile);
+
+        if(Value.empty())
+            EXCEPTION("Password is missing in the configuration file\n");
+
+        m_AuthenticationRequestString += EscapeString(Value);
+
+        /* If we don't have any Comment string yet, try to find one in the INI file */
+        if(m_Comment.empty())
+            m_Comment = GetINIValue(L"Submission", L"Comment", ConfigFile);
+    }
+}
diff --git a/rostests/rosautotest/CConfiguration.h b/rostests/rosautotest/CConfiguration.h
new file mode 100644 (file)
index 0000000..b65db7d
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class for managing all the configuration parameters
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CConfiguration
+{
+private:
+    bool m_CrashRecovery;
+    bool m_IsReactOS;
+    bool m_Shutdown;
+    bool m_Submit;
+    string m_Comment;
+    wstring m_Module;
+    string m_Test;
+
+    string m_AuthenticationRequestString;
+    string m_SystemInfoRequestString;
+
+public:
+    CConfiguration();
+    void ParseParameters(int argc, wchar_t* argv[]);
+    void GetSystemInformation();
+    void GetConfigurationFromFile();
+
+    bool DoCrashRecovery() const { return m_CrashRecovery; }
+    bool DoShutdown() const { return m_Shutdown; }
+    bool DoSubmit() const { return m_Submit; }
+    bool IsReactOS() const { return m_IsReactOS; }
+    const string& GetComment() const { return m_Comment; }
+    const wstring& GetModule() const { return m_Module; }
+    const string& GetTest() const { return m_Test; }
+
+    const string& GetAuthenticationRequestString() const { return m_AuthenticationRequestString; }
+    const string& GetSystemInfoRequestString() const { return m_SystemInfoRequestString; }
+};
diff --git a/rostests/rosautotest/CFatalException.cpp b/rostests/rosautotest/CFatalException.cpp
new file mode 100644 (file)
index 0000000..1541b5a
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Fatal program exception with automatically added information
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * Constructs a CFatalException object, which is catched in wmain as an exception.
+ * You should always use the FATAL macro for throwing this exception.
+ *
+ * @param File
+ * Constant pointer to a char array with the source file where the exception occured (__FILE__)
+ *
+ * @param Line
+ * Constant pointer to a char array with the appropriate source line (#__LINE__)
+ *
+ * @param Message
+ * Constant pointer to a char array containing a short message about the exception
+ */
+CFatalException::CFatalException(const char* File, const char* Line, const char* Message)
+    : m_File(File), m_Line(Line), m_Message(Message)
+{
+}
diff --git a/rostests/rosautotest/CFatalException.h b/rostests/rosautotest/CFatalException.h
new file mode 100644 (file)
index 0000000..78d1b00
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Fatal program exception with automatically added information
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CFatalException
+{
+private:
+    string m_File;
+    string m_Line;
+    string m_Message;
+
+public:
+    CFatalException(const char* File, const char* Line, const char* Message);
+
+    const string& GetFile() const { return m_File; }
+    const string& GetLine() const { return m_Line; }
+    const string& GetMessage() const { return m_Message; }
+};
diff --git a/rostests/rosautotest/CInvalidParameterException.cpp b/rostests/rosautotest/CInvalidParameterException.cpp
new file mode 100644 (file)
index 0000000..f28903f
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Empty exception thrown when the parameter processor detects an invalid parameter
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * Constructs an empty CInvalidParameterException object, which is catched in wmain as an exception.
+ */
+CInvalidParameterException::CInvalidParameterException()
+{
+}
diff --git a/rostests/rosautotest/CInvalidParameterException.h b/rostests/rosautotest/CInvalidParameterException.h
new file mode 100644 (file)
index 0000000..f3a8757
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Empty exception thrown when the parameter processor detects an invalid parameter
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CInvalidParameterException
+{
+public:
+    CInvalidParameterException();
+};
diff --git a/rostests/rosautotest/CJournaledTestList.cpp b/rostests/rosautotest/CJournaledTestList.cpp
new file mode 100644 (file)
index 0000000..ed0a74a
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a journaled test list for the Crash Recovery feature
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static const char szJournalHeader[] = "RAT_J-V1";
+static const WCHAR szJournalFileName[] = L"rosautotest.journal";
+
+/**
+ * Constructs a CJournaledTestList object for an associated CTest-derived object.
+ *
+ * @param Test
+ * Pointer to a CTest-derived object, for which this test list shall serve.
+ */
+CJournaledTestList::CJournaledTestList(CTest* Test)
+    : CTestList(Test)
+{
+    WCHAR JournalFile[MAX_PATH];
+
+    m_hJournal = INVALID_HANDLE_VALUE;
+
+    /* Build the path to the journal file */
+    if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, JournalFile) != S_OK)
+        FATAL("SHGetFolderPathW failed\n");
+
+    m_JournalFile = JournalFile;
+    m_JournalFile += L"\\rosautotest\\";
+
+    /* Create the directory if necessary */
+    if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
+        CreateDirectoryW(m_JournalFile.c_str(), NULL);
+
+    m_JournalFile += szJournalFileName;
+
+    /* Check if the journal already exists */
+    if(GetFileAttributesW(m_JournalFile.c_str()) == INVALID_FILE_ATTRIBUTES)
+        WriteInitialJournalFile();
+    else
+        LoadJournalFile();
+}
+
+/**
+ * Destructs a CJournaledTestList object.
+ */
+CJournaledTestList::~CJournaledTestList()
+{
+    if(m_hJournal != INVALID_HANDLE_VALUE)
+        CloseHandle(m_hJournal);
+}
+
+/**
+ * Opens the journal file through the CreateFileW API using the m_hJournal handle.
+ *
+ * @param DesiredAccess
+ * dwDesiredAccess parameter passed to CreateFileW
+ *
+ * @param CreateNew
+ * true if the journal file shall be created, false if an existing one shall be opened
+ */
+void
+CJournaledTestList::OpenJournal(DWORD DesiredAccess, bool CreateNew)
+{
+    m_hJournal = CreateFileW(m_JournalFile.c_str(), DesiredAccess, 0, NULL, (CreateNew ? CREATE_ALWAYS : OPEN_EXISTING), FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if(m_hJournal == INVALID_HANDLE_VALUE)
+        FATAL("CreateFileW failed\n");
+}
+
+/**
+ * Serializes a std::string and writes it into the opened journal file.
+ *
+ * @param String
+ * The std::string to serialize
+ *
+ * @see UnserializeFromBuffer
+ */
+void
+CJournaledTestList::SerializeIntoJournal(const string& String)
+{
+    DWORD BytesWritten;
+    WriteFile(m_hJournal, String.c_str(), String.size() + 1, &BytesWritten, NULL);
+}
+
+/**
+ * Serializes a std::wstring and writes it into the opened journal file.
+ *
+ * @param String
+ * The std::wstring to serialize
+ *
+ * @see UnserializeFromBuffer
+ */
+void
+CJournaledTestList::SerializeIntoJournal(const wstring& String)
+{
+    DWORD BytesWritten;
+    WriteFile(m_hJournal, String.c_str(), (String.size() + 1) * sizeof(WCHAR), &BytesWritten, NULL);
+}
+
+/**
+ * Unserializes the next std::string from the journal buffer.
+ * The passed buffer pointer will point at the next element afterwards.
+ *
+ * @param Buffer
+ * Pointer to a pointer to a char array containing the journal buffer
+ *
+ * @param Output
+ * The std::string to unserialize the value into.
+ */
+void
+CJournaledTestList::UnserializeFromBuffer(char** Buffer, string& Output)
+{
+    Output = string(*Buffer);
+    *Buffer += Output.size() + 1;
+}
+
+/**
+ * Unserializes the next std::wstring from the journal buffer.
+ * The passed buffer pointer will point at the next element afterwards.
+ *
+ * @param Buffer
+ * Pointer to a pointer to a char array containing the journal buffer
+ *
+ * @param Output
+ * The std::wstring to unserialize the value into.
+ */
+void
+CJournaledTestList::UnserializeFromBuffer(char** Buffer, wstring& Output)
+{
+    Output = wstring((PWSTR)*Buffer);
+    *Buffer += (Output.size() + 1) * sizeof(WCHAR);
+}
+
+/**
+ * Gets all tests to be run and writes an initial journal file with this information.
+ */
+void
+CJournaledTestList::WriteInitialJournalFile()
+{
+    char TerminatingNull = 0;
+    CTestInfo* TestInfo;
+    DWORD BytesWritten;
+
+    StringOut("Writing initial journal file...\n\n");
+
+    m_ListIterator = 0;
+
+    /* Store all command lines in the m_List vector */
+    while((TestInfo = m_Test->GetNextTestInfo()) != 0)
+    {
+        m_List.push_back(*TestInfo);
+        delete TestInfo;
+    }
+
+    /* Serialize the vector and the iterator into a file */
+    OpenJournal(GENERIC_WRITE, true);
+
+    WriteFile(m_hJournal, szJournalHeader, sizeof(szJournalHeader), &BytesWritten, NULL);
+    WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
+
+    for(size_t i = 0; i < m_List.size(); i++)
+    {
+        SerializeIntoJournal(m_List[i].CommandLine);
+        SerializeIntoJournal(m_List[i].Module);
+        SerializeIntoJournal(m_List[i].Test);
+    }
+
+    WriteFile(m_hJournal, &TerminatingNull, sizeof(TerminatingNull), &BytesWritten, NULL);
+
+    CloseHandle(m_hJournal);
+    m_hJournal = INVALID_HANDLE_VALUE;
+
+    /* m_ListIterator will be incremented before its first use */
+    m_ListIterator = (size_t)-1;
+}
+
+/**
+ * Loads the existing journal file and sets all members to the values saved in that file.
+ */
+void
+CJournaledTestList::LoadJournalFile()
+{
+    char* Buffer;
+    char* pBuffer;
+    char FileHeader[sizeof(szJournalHeader)];
+    DWORD BytesRead;
+    DWORD RemainingSize;
+
+    StringOut("Loading journal file...\n\n");
+
+    OpenJournal(GENERIC_READ);
+    RemainingSize = GetFileSize(m_hJournal, NULL);
+
+    /* Verify the header of the journal file */
+    ReadFile(m_hJournal, FileHeader, sizeof(szJournalHeader), &BytesRead, NULL);
+    RemainingSize -= BytesRead;
+
+    if(BytesRead != sizeof(szJournalHeader))
+        EXCEPTION("Journal file contains no header!\n");
+
+    if(strcmp(FileHeader, szJournalHeader))
+        EXCEPTION("Journal file has an unsupported header!\n");
+
+    /* Read the iterator */
+    ReadFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesRead, NULL);
+    RemainingSize -= BytesRead;
+
+    if(BytesRead != sizeof(m_ListIterator))
+        EXCEPTION("Journal file contains no m_ListIterator member!\n");
+
+    /* Read the rest of the file into a buffer */
+    Buffer = new char[RemainingSize];
+    pBuffer = Buffer;
+    ReadFile(m_hJournal, Buffer, RemainingSize, &BytesRead, NULL);
+
+    CloseHandle(m_hJournal);
+    m_hJournal = NULL;
+
+    /* Now recreate the m_List vector out of that information */
+    while(*pBuffer)
+    {
+        CTestInfo TestInfo;
+
+        UnserializeFromBuffer(&pBuffer, TestInfo.CommandLine);
+        UnserializeFromBuffer(&pBuffer, TestInfo.Module);
+        UnserializeFromBuffer(&pBuffer, TestInfo.Test);
+
+        m_List.push_back(TestInfo);
+    }
+
+    delete[] Buffer;
+}
+
+/**
+ * Writes the current m_ListIterator value into the journal.
+ */
+void
+CJournaledTestList::UpdateJournal()
+{
+    DWORD BytesWritten;
+
+    OpenJournal(GENERIC_WRITE);
+
+    /* Skip the header */
+    SetFilePointer(m_hJournal, sizeof(szJournalHeader), NULL, FILE_CURRENT);
+
+    WriteFile(m_hJournal, &m_ListIterator, sizeof(m_ListIterator), &BytesWritten, NULL);
+
+    CloseHandle(m_hJournal);
+    m_hJournal = NULL;
+}
+
+/**
+ * Interface to other classes for receiving information about the next test to be run.
+ *
+ * @return
+ * A pointer to a CTestInfo object, which contains all available information about the next test.
+ * The caller needs to free that object.
+ */
+CTestInfo*
+CJournaledTestList::GetNextTestInfo()
+{
+    CTestInfo* TestInfo;
+
+    /* Always jump to the next test here.
+       - If we're at the beginning of a new test list, the iterator will be set to 0.
+       - If we started with a loaded one, we assume that the test m_ListIterator is currently set
+         to crashed, so we move to the next test. */
+    ++m_ListIterator;
+
+    /* Check whether the iterator would already exceed the number of stored elements */
+    if(m_ListIterator == m_List.size())
+    {
+        /* Delete the journal and return no pointer */
+        DeleteFileW(m_JournalFile.c_str());
+
+        TestInfo = NULL;
+    }
+    else
+    {
+        /* Update the journal with the current iterator and return the test information */
+        UpdateJournal();
+
+        TestInfo = new CTestInfo(m_List[m_ListIterator]);
+    }
+
+    return TestInfo;
+}
diff --git a/rostests/rosautotest/CJournaledTestList.h b/rostests/rosautotest/CJournaledTestList.h
new file mode 100644 (file)
index 0000000..47900d3
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a journaled test list for the Crash Recovery feature
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CJournaledTestList : public CTestList
+{
+private:
+    HANDLE m_hJournal;
+    size_t m_ListIterator;
+    vector<CTestInfo> m_List;
+    wstring m_JournalFile;
+
+    void LoadJournalFile();
+    void OpenJournal(DWORD DesiredAccess, bool CreateNew = false);
+    void SerializeIntoJournal(const string& String);
+    void SerializeIntoJournal(const wstring& String);
+    void UnserializeFromBuffer(char** Buffer, string& Output);
+    void UnserializeFromBuffer(char** Buffer, wstring& Output);
+    void UpdateJournal();
+    void WriteInitialJournalFile();
+
+public:
+    CJournaledTestList(CTest* Test);
+    ~CJournaledTestList();
+
+    CTestInfo* GetNextTestInfo();
+};
diff --git a/rostests/rosautotest/CProcess.cpp b/rostests/rosautotest/CProcess.cpp
new file mode 100644 (file)
index 0000000..6697318
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class able to create a new process and closing its handles on destruction (exception-safe)
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * Constructs a CProcess object and uses the CreateProcessW function to start the process immediately.
+ *
+ * @param CommandLine
+ * A std::wstring containing the command line to run
+ *
+ * @param StartupInfo
+ * Pointer to a STARTUPINFOW structure containing process startup information
+ */
+CProcess::CProcess(const wstring& CommandLine, LPSTARTUPINFOW StartupInfo)
+{
+    auto_array_ptr<WCHAR> CommandLinePtr(new WCHAR[CommandLine.size() + 1]);
+
+    wcscpy(CommandLinePtr, CommandLine.c_str());
+
+    if(!CreateProcessW(NULL, CommandLinePtr, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, StartupInfo, &m_ProcessInfo))
+        FATAL("CreateProcessW failed\n");
+}
+
+/**
+ * Destructs a CProcess object and closes all handles belonging to the process.
+ */
+CProcess::~CProcess()
+{
+    CloseHandle(m_ProcessInfo.hThread);
+    CloseHandle(m_ProcessInfo.hProcess);
+}
diff --git a/rostests/rosautotest/CProcess.h b/rostests/rosautotest/CProcess.h
new file mode 100644 (file)
index 0000000..a46310c
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class able to create a new process and closing its handles on destruction (exception-safe)
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CProcess
+{
+private:
+    PROCESS_INFORMATION m_ProcessInfo;
+
+public:
+    CProcess(const wstring& CommandLine, LPSTARTUPINFOW StartupInfo);
+    ~CProcess();
+
+    HANDLE GetProcessHandle() const { return m_ProcessInfo.hProcess; }
+};
diff --git a/rostests/rosautotest/CSimpleException.cpp b/rostests/rosautotest/CSimpleException.cpp
new file mode 100644 (file)
index 0000000..f7ae841
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Simple exception containing just a message
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * Constructs a CSimpleException object, which is catched in wmain as an exception.
+ * You should always use the EXCEPTION or SSEXCEPTION macro for throwing this exception.
+ *
+ * @param Message
+ * Constant pointer to a char array containing a short message about the exception
+ */
+CSimpleException::CSimpleException(const char* Message)
+    : m_Message(Message)
+{
+}
diff --git a/rostests/rosautotest/CSimpleException.h b/rostests/rosautotest/CSimpleException.h
new file mode 100644 (file)
index 0000000..08bd09c
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Simple exception containing just a message
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CSimpleException
+{
+private:
+    string m_Message;
+
+public:
+    CSimpleException(const char* Message);
+
+    const string& GetMessage() const { return m_Message; }
+};
diff --git a/rostests/rosautotest/CTest.cpp b/rostests/rosautotest/CTest.cpp
new file mode 100644 (file)
index 0000000..7421300
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a generic Test, needs to be used by a derived class
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
diff --git a/rostests/rosautotest/CTest.h b/rostests/rosautotest/CTest.h
new file mode 100644 (file)
index 0000000..1f559b3
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a generic Test, needs to be used by a derived class
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CTest
+{
+private:
+    virtual CTestInfo* GetNextTestInfo() = 0;
+
+public:
+    virtual void Run() = 0;
+
+    /* All CTestList-derived classes need to access the private GetNextTestInfo method */
+    friend class CJournaledTestList;
+    friend class CVirtualTestList;
+};
diff --git a/rostests/rosautotest/CTestInfo.cpp b/rostests/rosautotest/CTestInfo.cpp
new file mode 100644 (file)
index 0000000..bea2482
--- /dev/null
@@ -0,0 +1,8 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a bucket for Test information
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
diff --git a/rostests/rosautotest/CTestInfo.h b/rostests/rosautotest/CTestInfo.h
new file mode 100644 (file)
index 0000000..7914805
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a bucket for Test information
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CTestInfo
+{
+public:
+    wstring CommandLine;
+    string Module;
+    string Test;
+    string Log;
+};
diff --git a/rostests/rosautotest/CTestList.cpp b/rostests/rosautotest/CTestList.cpp
new file mode 100644 (file)
index 0000000..17e31d9
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a generic Test list, needs to be used by a derived class
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * Constructor serving for CTestList-derived classes.
+ *
+ * @param Test
+ * Pointer to a CTest-derived object, for which this test list shall serve.
+ */
+CTestList::CTestList(CTest* Test) :
+    m_Test(Test)
+{
+}
diff --git a/rostests/rosautotest/CTestList.h b/rostests/rosautotest/CTestList.h
new file mode 100644 (file)
index 0000000..49a559a
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a generic Test list, needs to be used by a derived class
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CTestList
+{
+protected:
+    CTest* m_Test;
+
+    CTestList(CTest* Test);
+
+public:
+    virtual CTestInfo* GetNextTestInfo() = 0;
+};
diff --git a/rostests/rosautotest/CVirtualTestList.cpp b/rostests/rosautotest/CVirtualTestList.cpp
new file mode 100644 (file)
index 0000000..0aade72
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a virtual test list for the tests to be ran
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+/**
+ * Constructs a CVirtualTestList object for an associated CTest-derived object.
+ *
+ * @param Test
+ * Pointer to a CTest-derived object, for which this test list shall serve.
+ */
+CVirtualTestList::CVirtualTestList(CTest* Test)
+    : CTestList(Test)
+{
+}
+
+/**
+ * Interface to other classes for receiving information about the next test to be run.
+ *
+ * @return
+ * A pointer to a CTestInfo object, which contains all available information about the next test.
+ * The caller needs to free that object.
+ */
+CTestInfo*
+CVirtualTestList::GetNextTestInfo()
+{
+    return m_Test->GetNextTestInfo();
+}
diff --git a/rostests/rosautotest/CVirtualTestList.h b/rostests/rosautotest/CVirtualTestList.h
new file mode 100644 (file)
index 0000000..d9d6d60
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing a virtual test list for the tests to be ran
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CVirtualTestList : public CTestList
+{
+public:
+    CVirtualTestList(CTest* Test);
+
+    CTestInfo* GetNextTestInfo();
+};
diff --git a/rostests/rosautotest/CWebService.cpp b/rostests/rosautotest/CWebService.cpp
new file mode 100644 (file)
index 0000000..fbf4900
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing the interface to the "testman" Web Service
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static const WCHAR szHostname[] = L"localhost";
+static const WCHAR szServerFile[] = L"testman/webservice/";
+
+/**
+ * Constructs a CWebService object and immediately establishes a connection to the "testman" Web Service.
+ */
+CWebService::CWebService()
+{
+    /* Zero-initialize variables */
+    m_hHTTP = NULL;
+    m_hHTTPRequest = NULL;
+    m_TestID = NULL;
+
+    /* Establish an internet connection to the "testman" server */
+    m_hInet = InternetOpenW(L"rosautotest", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
+
+    if(!m_hInet)
+        FATAL("InternetOpenW failed\n");
+
+    m_hHTTP = InternetConnectW(m_hInet, szHostname, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
+
+    if(!m_hHTTP)
+        FATAL("InternetConnectW failed\n");
+}
+
+/**
+ * Destructs a CWebService object and closes all connections to the Web Service.
+ */
+CWebService::~CWebService()
+{
+    if(m_hInet)
+        InternetCloseHandle(m_hInet);
+
+    if(m_hHTTP)
+        InternetCloseHandle(m_hHTTP);
+
+    if(m_hHTTPRequest)
+        InternetCloseHandle(m_hHTTPRequest);
+
+    if(m_TestID)
+        delete m_TestID;
+}
+
+/**
+ * Sends data to the Web Service.
+ *
+ * @param InputData
+ * A std::string containing all the data, which is going to be submitted as HTTP POST data.
+ *
+ * @return
+ * Returns a pointer to a char array containing the data received from the Web Service.
+ * The caller needs to free that pointer.
+ */
+PCHAR
+CWebService::DoRequest(const string& InputData)
+{
+    const WCHAR szHeaders[] = L"Content-Type: application/x-www-form-urlencoded";
+
+    auto_array_ptr<char> Data;
+    DWORD DataLength;
+
+    /* Post our test results to the web service */
+    m_hHTTPRequest = HttpOpenRequestW(m_hHTTP, L"POST", szServerFile, NULL, NULL, NULL, INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
+
+    if(!m_hHTTPRequest)
+        FATAL("HttpOpenRequestW failed\n");
+
+    Data.reset(new char[InputData.size() + 1]);
+    strcpy(Data, InputData.c_str());
+
+    if(!HttpSendRequestW(m_hHTTPRequest, szHeaders, wcslen(szHeaders), Data, InputData.size()))
+        FATAL("HttpSendRequestW failed\n");
+
+    /* Get the response */
+    if(!InternetQueryDataAvailable(m_hHTTPRequest, &DataLength, 0, 0))
+        FATAL("InternetQueryDataAvailable failed\n");
+
+    Data.reset(new char[DataLength + 1]);
+
+    if(!InternetReadFile(m_hHTTPRequest, Data, DataLength, &DataLength))
+        FATAL("InternetReadFile failed\n");
+
+    Data[DataLength] = 0;
+
+    return Data.release();
+}
+
+/**
+ * Requests a Test ID from the Web Service for our test run.
+ *
+ * @param TestType
+ * Constant pointer to a char array containing the test type to be run (i.e. "wine")
+ */
+void
+CWebService::GetTestID(const char* TestType)
+{
+    string Data;
+
+    Data = "action=gettestid";
+    Data += Configuration.GetAuthenticationRequestString();
+    Data += Configuration.GetSystemInfoRequestString();
+    Data += "&testtype=";
+    Data += TestType;
+
+    if(!Configuration.GetComment().empty())
+    {
+        Data += "&comment=";
+        Data += Configuration.GetComment();
+    }
+
+    m_TestID = DoRequest(Data);
+
+    /* Verify that this is really a number */
+    if(!IsNumber(m_TestID))
+    {
+        stringstream ss;
+
+        ss << "Expected Test ID, but received:" << endl << m_TestID << endl;
+        SSEXCEPTION;
+    }
+}
+
+/**
+ * Gets a Suite ID from the Web Service for this module/test combination.
+ *
+ * @param TestType
+ * Constant pointer to a char array containing the test type to be run (i.e. "wine")
+ *
+ * @param TestInfo
+ * Pointer to a CTestInfo object containing information about the test
+ *
+ * @return
+ * Returns a pointer to a char array containing the Suite ID received from the Web Service.
+ * The caller needs to free that pointer.
+ */
+PCHAR
+CWebService::GetSuiteID(const char* TestType, CTestInfo* TestInfo)
+{
+    auto_array_ptr<char> SuiteID;
+    string Data;
+
+    Data = "action=getsuiteid";
+    Data += Configuration.GetAuthenticationRequestString();
+    Data += "&testtype=";
+    Data += TestType;
+    Data += "&module=";
+    Data += TestInfo->Module;
+    Data += "&test=";
+    Data += TestInfo->Test;
+
+    SuiteID.reset(DoRequest(Data));
+
+    /* Verify that this is really a number */
+    if(!IsNumber(SuiteID))
+    {
+        stringstream ss;
+
+        ss << "Expected Suite ID, but received:" << endl << SuiteID << endl;
+        SSEXCEPTION;
+    }
+
+    return SuiteID.release();
+}
+
+/**
+ * Interface to other classes for submitting a result of one test
+ *
+ * @param TestType
+ * Constant pointer to a char array containing the test type to be run (i.e. "wine")
+ *
+ * @param TestInfo
+ * Pointer to a CTestInfo object containing information about the test
+ */
+void
+CWebService::Submit(const char* TestType, CTestInfo* TestInfo)
+{
+    auto_array_ptr<char> Response;
+    auto_array_ptr<char> SuiteID;
+    string Data;
+    stringstream ss;
+
+    if(!m_TestID)
+        GetTestID(TestType);
+
+    SuiteID.reset(GetSuiteID(TestType, TestInfo));
+
+    Data = "action=submit";
+    Data += Configuration.GetAuthenticationRequestString();
+    Data += "&testtype=";
+    Data += TestType;
+    Data += "&testid=";
+    Data += m_TestID;
+    Data += "&suiteid=";
+    Data += SuiteID;
+    Data += "&log=";
+    Data += TestInfo->Log;
+
+    Response.reset(DoRequest(Data));
+
+    ss << "The server responded:" << endl << Response << endl;
+    StringOut(ss.str());
+
+    if(strcmp(Response, "OK"))
+        EXCEPTION("Aborted!\n");
+}
diff --git a/rostests/rosautotest/CWebService.h b/rostests/rosautotest/CWebService.h
new file mode 100644 (file)
index 0000000..07dbf89
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing the interface to the "testman" Web Service
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CWebService
+{
+private:
+    HINTERNET m_hInet;
+    HINTERNET m_hHTTP;
+    HINTERNET m_hHTTPRequest;
+    PCHAR m_TestID;
+
+    PCHAR DoRequest(const string& InputData);
+    void GetTestID(const char* TestType);
+    PCHAR GetSuiteID(const char* TestType, CTestInfo* TestInfo);
+
+public:
+    CWebService();
+    ~CWebService();
+
+    void Submit(const char* TestType, CTestInfo* TestInfo);
+};
diff --git a/rostests/rosautotest/CWineTest.cpp b/rostests/rosautotest/CWineTest.cpp
new file mode 100644 (file)
index 0000000..471e6ab
--- /dev/null
@@ -0,0 +1,366 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing functions for handling Wine tests
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+#include "precomp.h"
+
+static const DWORD ListTimeout = 10000;
+
+/**
+ * Constructs a CWineTest object.
+ */
+CWineTest::CWineTest()
+{
+    WCHAR WindowsDirectory[MAX_PATH];
+
+    /* Zero-initialize variables */
+    m_hFind = NULL;
+    m_hReadPipe = NULL;
+    m_hWritePipe = NULL;
+    m_ListBuffer = NULL;
+    memset(&m_StartupInfo, 0, sizeof(m_StartupInfo));
+
+    /* Set up m_TestPath */
+    if(!GetWindowsDirectoryW(WindowsDirectory, MAX_PATH))
+        FATAL("GetWindowsDirectoryW failed");
+
+    m_TestPath = WindowsDirectory;
+    m_TestPath += L"\\bin\\";
+}
+
+/**
+ * Destructs a CWineTest object.
+ */
+CWineTest::~CWineTest()
+{
+    if(m_hFind)
+        FindClose(m_hFind);
+
+    if(m_hReadPipe)
+        CloseHandle(m_hReadPipe);
+
+    if(m_hWritePipe)
+        CloseHandle(m_hWritePipe);
+
+    if(m_ListBuffer)
+        delete m_ListBuffer;
+}
+
+/**
+ * Gets the next module test file using the FindFirstFileW/FindNextFileW API.
+ *
+ * @return
+ * true if we found a next file, otherwise false.
+ */
+bool
+CWineTest::GetNextFile()
+{
+    bool FoundFile = false;
+    WIN32_FIND_DATAW fd;
+
+    /* Did we already begin searching for files? */
+    if(m_hFind)
+    {
+        /* Then get the next file (if any) */
+        if(FindNextFileW(m_hFind, &fd))
+            FoundFile = true;
+    }
+    else
+    {
+        /* Start searching for test files */
+        wstring FindPath = m_TestPath;
+
+        /* Did the user specify a module? */
+        if(Configuration.GetModule().empty())
+        {
+            /* No module, so search for all files in that directory */
+            FindPath += L"*.exe";
+        }
+        else
+        {
+            /* Search for files with the pattern "modulename_*" */
+            FindPath += Configuration.GetModule();
+            FindPath += L"_*.exe";
+        }
+
+        /* Search for the first file and check whether we got one */
+        m_hFind = FindFirstFileW(FindPath.c_str(), &fd);
+
+        if(m_hFind != INVALID_HANDLE_VALUE)
+            FoundFile = true;
+    }
+
+    if(FoundFile)
+        m_CurrentFile = fd.cFileName;
+
+    return FoundFile;
+}
+
+/**
+ * Executes the --list command of a module test file to get information about the available tests.
+ *
+ * @return
+ * The number of bytes we read into the m_ListBuffer member variable by capturing the output of the --list command.
+ */
+DWORD
+CWineTest::DoListCommand()
+{
+    DWORD BytesAvailable;
+    DWORD Temp;
+    wstring CommandLine;
+
+    /* Build the command line */
+    CommandLine = m_TestPath;
+    CommandLine += m_CurrentFile;
+    CommandLine += L" --list";
+
+    {
+        /* Start the process for getting all available tests */
+        CProcess Process(CommandLine, &m_StartupInfo);
+
+        /* Wait till this process ended */
+        if(WaitForSingleObject(Process.GetProcessHandle(), ListTimeout) == WAIT_FAILED)
+            FATAL("WaitForSingleObject failed for the test list\n");
+    }
+
+    /* Read the output data into a buffer */
+    if(!PeekNamedPipe(m_hReadPipe, NULL, 0, NULL, &BytesAvailable, NULL))
+        FATAL("PeekNamedPipe failed for the test list\n");
+
+    /* Check if we got any */
+    if(!BytesAvailable)
+    {
+        stringstream ss;
+
+        ss << "The --list command did not return any data for " << UnicodeToAscii(m_CurrentFile) << endl;
+        SSEXCEPTION;
+    }
+
+    /* Read the data */
+    m_ListBuffer = new char[BytesAvailable];
+
+    if(!ReadFile(m_hReadPipe, m_ListBuffer, BytesAvailable, &Temp, NULL))
+        FATAL("ReadPipe failed\n");
+
+    return BytesAvailable;
+}
+
+/**
+ * Gets the next test from m_ListBuffer, which was filled with information from the --list command.
+ *
+ * @return
+ * true if a next test was found, otherwise false.
+ */
+bool
+CWineTest::GetNextTest()
+{
+    PCHAR pEnd;
+    static DWORD BufferSize;
+    static PCHAR pStart;
+
+    if(!m_ListBuffer)
+    {
+        /* Perform the --list command */
+        BufferSize = DoListCommand();
+
+        /* Move the pointer to the first test */
+        pStart = strchr(m_ListBuffer, '\n');
+        pStart += 5;
+    }
+
+    /* If we reach the buffer size, we finished analyzing the output of this test */
+    if(pStart >= (m_ListBuffer + BufferSize))
+    {
+        /* Clear m_CurrentFile to indicate that */
+        m_CurrentFile.clear();
+
+        /* Also free the memory for the list buffer */
+        delete m_ListBuffer;
+        m_ListBuffer = NULL;
+
+        return false;
+    }
+
+    /* Get start and end of this test name */
+    pEnd = pStart;
+
+    while(*pEnd != '\r')
+        ++pEnd;
+
+    /* Store the test name */
+    m_CurrentTest = string(pStart, pEnd);
+
+    /* Move the pointer to the next test */
+    pStart = pEnd + 6;
+
+    return true;
+}
+
+/**
+ * Interface to CTestList-derived classes for getting all information about the next test to be run.
+ *
+ * @return
+ * Returns a pointer to a CTestInfo object containing all available information about the next test.
+ */
+CTestInfo*
+CWineTest::GetNextTestInfo()
+{
+    while(!m_CurrentFile.empty() || GetNextFile())
+    {
+        while(GetNextTest())
+        {
+            /* If the user specified a test through the command line, check this here */
+            if(!Configuration.GetTest().empty() && Configuration.GetTest() != m_CurrentTest)
+                continue;
+
+            {
+                auto_ptr<CTestInfo> TestInfo(new CTestInfo());
+                size_t UnderscorePosition;
+
+                /* Build the command line */
+                TestInfo->CommandLine = m_TestPath;
+                TestInfo->CommandLine += m_CurrentFile;
+                TestInfo->CommandLine += ' ';
+                TestInfo->CommandLine += AsciiToUnicode(m_CurrentTest);
+
+                /* Store the Module name */
+                UnderscorePosition = m_CurrentFile.find('_');
+
+                if(UnderscorePosition == m_CurrentFile.npos)
+                {
+                    stringstream ss;
+
+                    ss << "Invalid test file name: " << UnicodeToAscii(m_CurrentFile) << endl;
+                    SSEXCEPTION;
+                }
+
+                TestInfo->Module = UnicodeToAscii(m_CurrentFile.substr(0, UnderscorePosition));
+
+                /* Store the test */
+                TestInfo->Test = m_CurrentTest;
+
+                return TestInfo.release();
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * Runs a Wine test and captures the output
+ *
+ * @param TestInfo
+ * Pointer to a CTestInfo object containing information about the test.
+ * Will contain the test log afterwards if the user wants to submit data.
+ */
+void
+CWineTest::RunTest(CTestInfo* TestInfo)
+{
+    bool BreakLoop = false;
+    DWORD BytesAvailable;
+    DWORD Temp;
+    stringstream ss;
+
+    ss << "Running Wine Test, Module: " << TestInfo->Module << ", Test: " << TestInfo->Test << endl;
+    StringOut(ss.str());
+
+    {
+        /* Execute the test */
+        CProcess Process(TestInfo->CommandLine, &m_StartupInfo);
+
+        /* 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(Process.GetProcessHandle(), 0) != WAIT_TIMEOUT)
+                BreakLoop = true;
+
+            if(!PeekNamedPipe(m_hReadPipe, NULL, 0, NULL, &BytesAvailable, NULL))
+                FATAL("PeekNamedPipe failed for the test run\n");
+
+            if(BytesAvailable)
+            {
+                /* There is data, so get it and output it */
+                auto_array_ptr<char> Buffer(new char[BytesAvailable + 1]);
+
+                if(!ReadFile(m_hReadPipe, Buffer, BytesAvailable, &Temp, NULL))
+                    FATAL("ReadFile failed for the test run\n");
+
+                /* Output all test output through StringOut, even while the test is still running */
+                Buffer[BytesAvailable] = 0;
+                StringOut(string(Buffer));
+
+                if(Configuration.DoSubmit())
+                    TestInfo->Log += Buffer;
+            }
+        }
+        while(!BreakLoop);
+    }
+}
+
+/**
+ * Interface to other classes for running all desired Wine tests.
+ */
+void
+CWineTest::Run()
+{
+    auto_ptr<CTestList> TestList;
+    auto_ptr<CWebService> WebService;
+    CTestInfo* TestInfo;
+    SECURITY_ATTRIBUTES SecurityAttributes;
+
+    /* Create a pipe for getting the output of the tests */
+    SecurityAttributes.nLength = sizeof(SecurityAttributes);
+    SecurityAttributes.bInheritHandle = TRUE;
+    SecurityAttributes.lpSecurityDescriptor = NULL;
+
+    if(!CreatePipe(&m_hReadPipe, &m_hWritePipe, &SecurityAttributes, 0))
+        FATAL("CreatePipe failed\n");
+
+    m_StartupInfo.cb = sizeof(m_StartupInfo);
+    m_StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+    m_StartupInfo.hStdOutput = m_hWritePipe;
+
+    /* The virtual test list is of course faster, so it should be preferred over
+       the journaled one.
+       Enable the journaled one only in case ...
+          - we're running under ReactOS (as the journal is only useful in conjunction with sysreg2)
+          - we shall keep information for Crash Recovery
+          - and the user didn't specify a module (then doing Crash Recovery doesn't really make sense) */
+    if(Configuration.IsReactOS() && Configuration.DoCrashRecovery() && Configuration.GetModule().empty())
+    {
+        /* Use a test list with a permanent journal */
+        TestList.reset(new CJournaledTestList(this));
+    }
+    else
+    {
+        /* Use the fast virtual test list with no additional overhead */
+        TestList.reset(new CVirtualTestList(this));
+    }
+
+    /* Initialize the Web Service interface if required */
+    if(Configuration.DoSubmit())
+        WebService.reset(new CWebService());
+
+    /* Get information for each test to run */
+    while((TestInfo = TestList->GetNextTestInfo()) != 0)
+    {
+        auto_ptr<CTestInfo> TestInfoPtr(TestInfo);
+
+        RunTest(TestInfo);
+
+        if(Configuration.DoSubmit() && !TestInfo->Log.empty())
+            WebService->Submit("wine", TestInfo);
+
+        StringOut("\n\n");
+    }
+}
diff --git a/rostests/rosautotest/CWineTest.h b/rostests/rosautotest/CWineTest.h
new file mode 100644 (file)
index 0000000..58ca558
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Class implementing functions for handling Wine tests
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+class CWineTest : public CTest
+{
+private:
+    HANDLE m_hFind;
+    HANDLE m_hReadPipe;
+    HANDLE m_hWritePipe;
+    PCHAR m_ListBuffer;
+    STARTUPINFOW m_StartupInfo;
+    string m_CurrentTest;
+    wstring m_CurrentFile;
+    wstring m_CurrentListCommand;
+    wstring m_TestPath;
+
+    bool GetNextFile();
+    bool GetNextTest();
+    CTestInfo* GetNextTestInfo();
+    DWORD DoListCommand();
+    void RunTest(CTestInfo* TestInfo);
+
+public:
+    CWineTest();
+    ~CWineTest();
+
+    void Run();
+};
diff --git a/rostests/rosautotest/auto_array_ptr.h b/rostests/rosautotest/auto_array_ptr.h
new file mode 100644 (file)
index 0000000..e1e3b29
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * PROJECT:     ReactOS Automatic Testing Utility
+ * LICENSE:     GNU GPLv2 or any later version as published by the Free Software Foundation
+ * PURPOSE:     Template similar to std::auto_ptr for arrays
+ * COPYRIGHT:   Copyright 2009 Colin Finck <colin@reactos.org>
+ */
+
+template<typename Type>
+class auto_array_ptr
+{
+private:
+    Type* m_Ptr;
+
+public:
+    typedef Type element_type;
+
+    /* Construct an auto_array_ptr from a pointer */
+    explicit auto_array_ptr(Type* Ptr = 0) throw()
+        : m_Ptr(Ptr)
+    {
+    }
+
+    /* Construct an auto_array_ptr from an existing auto_array_ptr */
+    auto_array_ptr(auto_array_ptr<Type>& Right) throw()
+        : m_Ptr(Right.release())
+    {
+    }
+
+    /* Destruct the auto_array_ptr and remove the corresponding array from memory */
+    ~auto_array_ptr() throw()
+    {
+        delete[] m_Ptr;
+    }
+
+    /* Get the pointer address */
+    Type* get() const throw()
+    {
+        return m_Ptr;
+    }
+
+    /* Release the pointer */
+    Type* release() throw()
+    {
+        Type* Tmp = m_Ptr;
+        m_Ptr = 0;
+
+        return Tmp;
+    }
+
+    /* Reset to a new pointer */
+    void reset(Type* Ptr = 0) throw()
+    {
+        if(Ptr != m_Ptr)
+            delete[] m_Ptr;
+
+        m_Ptr = Ptr;
+    }
+
+    /* Simulate all the functionality of real arrays by casting the auto_array_ptr to Type* on demand */
+    operator Type*() const throw()
+    {
+        return m_Ptr;
+    }
+};
diff --git a/rostests/rosautotest/main.c b/rostests/rosautotest/main.c
deleted file mode 100644 (file)
index 4bfc8bd..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * 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 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
- * Password=TestPassword
- */
-static BOOL
-IntGetConfigurationValues()
-{
-    const CHAR PasswordProp[] = "&password=";
-    const CHAR UserNameProp[] = "&username=";
-
-    BOOL ReturnValue = FALSE;
-    DWORD DataLength;
-    DWORD Length;
-    PCHAR Password = NULL;
-    PCHAR UserName = NULL;
-    WCHAR ConfigFile[MAX_PATH];
-
-    /* Most values are only needed if we're going to submit */
-    if(AppOptions.Submit)
-    {
-        /* Build the path to the configuration file from the application's path */
-        GetModuleFileNameW(NULL, ConfigFile, MAX_PATH);
-        Length = wcsrchr(ConfigFile, '\\') - ConfigFile;
-        wcscpy(&ConfigFile[Length], L"\\rosautotest.ini");
-
-        /* Check if it exists */
-        if(GetFileAttributesW(ConfigFile) == INVALID_FILE_ATTRIBUTES)
-        {
-            StringOut("Missing \"rosautotest.ini\" configuration file!\n");
-            goto Cleanup;
-        }
-
-        /* 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");
-            goto Cleanup;
-        }
-
-        /* Some characters might need to be escaped and an escaped character takes 3 bytes */
-        DataLength += 3 * Length;
-
-        DataLength += sizeof(PasswordProp) - 1;
-        Length = IntGetINIValueA(L"Login", L"Password", ConfigFile, &Password);
-
-        if(!Length)
-        {
-            StringOut("Password is missing in the configuration file\n");
-            goto Cleanup;
-        }
-
-        DataLength += 3 * Length;
-
-        /* Build the string */
-        AuthenticationRequestString = HeapAlloc(hProcessHeap, 0, DataLength + 1);
-
-        strcpy(AuthenticationRequestString, UserNameProp);
-        EscapeString(&AuthenticationRequestString[strlen(AuthenticationRequestString)], UserName);
-
-        strcat(AuthenticationRequestString, PasswordProp);
-        EscapeString(&AuthenticationRequestString[strlen(AuthenticationRequestString)], Password);
-
-        /* If we don't have any Comment string yet, try to find one in the INI file */
-        if(!AppOptions.Comment)
-            IntGetINIValueA(L"Submission", L"Comment", ConfigFile, &AppOptions.Comment);
-    }
-
-    ReturnValue = TRUE;
-
-Cleanup:
-    if(UserName)
-        HeapFree(hProcessHeap, 0, UserName);
-
-    if(Password)
-        HeapFree(hProcessHeap, 0, Password);
-
-    return ReturnValue;
-}
-
-/**
- * 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("    /c <comment> - Specifies the comment to be submitted to the Web Service.\n");
-    printf("                   Skips the comment set in the configuration file (if any).\n");
-    printf("                   Only has an effect when /w is also used.\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 ReturnValue = 0;
-    size_t Length;
-    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 'c':
-                    ++i;
-
-                    /* Copy the parameter converted to ASCII */
-                    Length = WideCharToMultiByte(CP_ACP, 0, argv[i], -1, NULL, 0, NULL, NULL);
-                    AppOptions.Comment = HeapAlloc(hProcessHeap, 0, Length);
-                    WideCharToMultiByte(CP_ACP, 0, argv[i], -1, AppOptions.Comment, Length, NULL, NULL);
-
-                    break;
-
-                case 's':
-                    AppOptions.Shutdown = TRUE;
-                    break;
-
-                case 'w':
-                    AppOptions.Submit = TRUE;
-                    break;
-
-                default:
-                    ReturnValue = 1;
-                    /* Fall through */
-
-                case '?':
-                    IntPrintUsage();
-                    goto Cleanup;
-            }
-        }
-        else
-        {
-            /* 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
-            {
-                ReturnValue = 1;
-                IntPrintUsage();
-                goto Cleanup;
-            }
-        }
-    }
-
-    if(!IntGetConfigurationValues() || !IntGetBuildAndPlatform() || !RunWineTests())
-    {
-        ReturnValue = 1;
-        goto Cleanup;
-    }
-
-    /* For sysreg */
-    OutputDebugStringA("SYSREG_CHECKPOINT:THIRDBOOT_COMPLETE\n");
-
-Cleanup:
-    if(AppOptions.Comment)
-        HeapFree(hProcessHeap, 0, AppOptions.Comment);
-
-    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())
-        ReturnValue = 1;
-
-    return ReturnValue;
-}
diff --git a/rostests/rosautotest/main.cpp b/rostests/rosautotest/main.cpp
new file mode 100644 (file)
index 0000000..6b54ac8
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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"
+#include <cstdio>
+
+CConfiguration Configuration;
+
+/**
+ * Prints the application usage.
+ */
+static void
+IntPrintUsage()
+{
+    cout << "rosautotest - ReactOS Automatic Testing Utility" << endl
+         << "Usage: rosautotest [options] [module] [test]" << endl
+         << "  options:" << endl
+         << "    /?           - Shows this help." << endl
+         << "    /c <comment> - Specifies the comment to be submitted to the Web Service." << endl
+         << "                   Skips the comment set in the configuration file (if any)." << endl
+         << "                   Only has an effect when /w is also used." << endl
+         << "    /r           - Maintain information to resume from ReactOS crashes" << endl
+         << "                   Can only be run under ReactOS and relies on sysreg2," << endl
+         << "                   so incompatible with /w" << endl
+         << "    /s           - Shut down the system after finishing the tests." << endl
+         << "    /w           - Submit the results to the webservice." << endl
+         << "                   Requires a \"rosautotest.ini\" with valid login data." << endl
+         << "                   Incompatible with the /r option." << endl
+         << endl
+         << "  module:" << endl
+         << "    The module to be tested (i.e. \"advapi32\")" << endl
+         << "    If this parameter is specified without any test parameter," << endl
+         << "    all tests of the specified module are run." << endl
+         << endl
+         << "  test:" << endl
+         << "    The test to be run. Needs to be a test of the specified module." << endl;
+}
+
+/**
+ * Main entry point
+ */
+extern "C" int
+wmain(int argc, wchar_t* argv[])
+{
+    CWineTest WineTest;
+    int ReturnValue = 1;
+
+    try
+    {
+        /* Set up the configuration */
+        Configuration.ParseParameters(argc, argv);
+        Configuration.GetSystemInformation();
+        Configuration.GetConfigurationFromFile();
+
+        /* Run the tests */
+        WineTest.Run();
+
+        /* For sysreg */
+        DbgPrint("SYSREG_CHECKPOINT:THIRDBOOT_COMPLETE\n");
+
+        ReturnValue = 0;
+    }
+    catch(CInvalidParameterException)
+    {
+        IntPrintUsage();
+    }
+    catch(CSimpleException& e)
+    {
+        StringOut(e.GetMessage());
+    }
+    catch(CFatalException& e)
+    {
+        stringstream ss;
+
+        ss << "An exception occured in rosautotest." << endl
+           << "Message: " << e.GetMessage() << endl
+           << "File: " << e.GetFile() << endl
+           << "Line: " << e.GetLine() << endl
+           << "Last Win32 Error: " << GetLastError() << endl;
+        StringOut(ss.str());
+    }
+
+    /* Shut down the system if requested, also in case of an exception above */
+    if(Configuration.DoShutdown() && !ShutdownSystem())
+        ReturnValue = 1;
+
+    return ReturnValue;
+}
index 9b8d6d7..57acaf8 100644 (file)
@@ -1,82 +1,63 @@
-/* Includes */
-#include <stdio.h>
+/* General includes */
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
 
+using namespace std;
+
+#define WIN32_NO_STATUS
 #include <windows.h>
+#include <ndk/rtlfuncs.h>
 #include <reason.h>
+#include <shlobj.h>
 #include <wininet.h>
 
+#include <ndk/rtlfuncs.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;
-    PCHAR Comment;
-    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;
+/* Class includes */
+#include "auto_array_ptr.h"
+#include "CConfiguration.h"
+#include "CFatalException.h"
+#include "CInvalidParameterException.h"
+#include "CProcess.h"
+#include "CSimpleException.h"
+#include "CTestInfo.h"
+#include "CTest.h"
+#include "CTestList.h"
+#include "CJournaledTestList.h"
+#include "CVirtualTestList.h"
+#include "CWebService.h"
+#include "CWineTest.h"
+
+/* Useful macros */
+#define STRINGIZER(Value)    #Value
+#define EXCEPTION(Message)   throw CSimpleException(Message)
+#define FATAL(Message)       throw CFatalException(__FILE__, STRINGIZER(__LINE__), Message)
+#define SSEXCEPTION          throw CSimpleException(ss.str().c_str())
 
 /* main.c */
-extern APP_OPTIONS AppOptions;
-extern HANDLE hProcessHeap;
-extern PCHAR AuthenticationRequestString;
-extern PCHAR SystemInfoRequestString;
+extern CConfiguration Configuration;
 
 /* shutdown.c */
-BOOL ShutdownSystem();
+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();
+wstring AsciiToUnicode(const char* AsciiString);
+wstring AsciiToUnicode(const string& AsciiString);
+string EscapeString(const char* Input);
+string EscapeString(const string& Input);
+string GetINIValue(PCWCH AppName, PCWCH KeyName, PCWCH FileName);
+bool IsNumber(const char* Input);
+void StringOut(const string& String);
+string UnicodeToAscii(PCWSTR UnicodeString);
+string UnicodeToAscii(const wstring& UnicodeString);
+
+
+/* Lazy HACK to allow compiling/debugging with MSVC while we lack support
+   for linking against "debugsup_ntdll" in MSVC */
+#ifdef _MSC_VER
+    #define DbgPrint
+#endif
index b372227..7213c05 100644 (file)
@@ -1,15 +1,27 @@
 <?xml version="1.0"?>
 <!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
 <module name="rosautotest" type="win32cui" installbase="system32" installname="rosautotest.exe" unicode="yes">
+       <compilerflag compiler="cpp">-fno-rtti</compilerflag>
        <include base="rosautotest">.</include>
        <library>advapi32</library>
        <library>kernel32</library>
+       <library>shell32</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>
+       <file>CConfiguration.cpp</file>
+       <file>CFatalException.cpp</file>
+       <file>CInvalidParameterException.cpp</file>
+       <file>CJournaledTestList.cpp</file>
+       <file>CProcess.cpp</file>
+       <file>CSimpleException.cpp</file>
+       <file>CTest.cpp</file>
+       <file>CTestInfo.cpp</file>
+       <file>CTestList.cpp</file>
+       <file>CVirtualTestList.cpp</file>
+       <file>CWebService.cpp</file>
+       <file>CWineTest.cpp</file>
+       <file>main.cpp</file>
+       <file>shutdown.cpp</file>
+       <file>tools.cpp</file>
        <pch>precomp.h</pch>
 </module>
similarity index 87%
rename from rostests/rosautotest/shutdown.c
rename to rostests/rosautotest/shutdown.cpp
index 7493d79..dc243fa 100644 (file)
@@ -11,9 +11,9 @@
  * Shuts down the system.
  *
  * @return
- * TRUE if everything went well, FALSE if there was a problem while trying to shut down the system.
+ * true if everything went well, false if there was a problem while trying to shut down the system.
  */
-BOOL ShutdownSystem()
+bool ShutdownSystem()
 {
     HANDLE hToken;
     TOKEN_PRIVILEGES Privileges;
@@ -21,14 +21,14 @@ BOOL ShutdownSystem()
     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
     {
         StringOut("OpenProcessToken failed\n");
-        return FALSE;
+        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;
+        return false;
     }
 
     /* Assign the Shutdown privilege to our process */
@@ -38,15 +38,15 @@ BOOL ShutdownSystem()
     if (!AdjustTokenPrivileges(hToken, FALSE, &Privileges, 0, NULL, NULL))
     {
         StringOut("AdjustTokenPrivileges failed\n");
-        return FALSE;
+        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 false;
     }
 
-    return TRUE;
+    return true;
 }
diff --git a/rostests/rosautotest/tools.c b/rostests/rosautotest/tools.c
deleted file mode 100644 (file)
index adb7b73..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- * The string may have LF or CRLF line endings.
- *
- * @param String
- * The string to output
- */
-VOID
-StringOut(PCHAR String)
-{
-    PCHAR NewString;
-    PCHAR pNewString;
-    size_t Length;
-
-    /* The piped output of the tests may use CRLF line endings, so convert them to LF.
-       As both printf and OutputDebugStringA operate in text mode, the line-endings will be properly converted again later. */
-    Length = strlen(String);
-    NewString = HeapAlloc(hProcessHeap, 0, Length + 1);
-    pNewString = NewString;
-
-    do
-    {
-        /* If this is a CRLF line-ending, only copy a \n to the new string and skip the next character */
-        if(*String == '\r' && *(String + 1) == '\n')
-        {
-            *pNewString = '\n';
-            ++String;
-        }
-        else
-        {
-            /* Otherwise copy the string */
-            *pNewString = *String;
-        }
-
-        ++pNewString;
-    }
-    while(*++String);
-
-    /* Null-terminate it */
-    *pNewString = 0;
-
-    /* Output it */
-    printf(NewString);
-    OutputDebugStringA(NewString);
-
-    /* Cleanup */
-    HeapFree(hProcessHeap, 0, NewString);
-}
diff --git a/rostests/rosautotest/tools.cpp b/rostests/rosautotest/tools.cpp
new file mode 100644 (file)
index 0000000..6b1165c
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * 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"
+
+#define DBGPRINT_BUFSIZE   511
+static const char HexCharacters[] = "0123456789ABCDEF";
+
+/**
+ * Escapes a string according to RFC 1738.
+ * Required for passing parameters to the web service.
+ *
+ * @param Input
+ * Constant pointer to a char array, which contains the input buffer to escape.
+ *
+ * @return
+ * The escaped string as std::string.
+ */
+string
+EscapeString(const char* Input)
+{
+    string ReturnedString;
+
+    do
+    {
+        if(strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~", *Input))
+        {
+            /* It's a character we don't need to escape, just add it to the output string */
+            ReturnedString += *Input;
+        }
+        else
+        {
+            /* We need to escape this character */
+            ReturnedString += '%';
+            ReturnedString += HexCharacters[((UCHAR)*Input >> 4) % 16];
+            ReturnedString += HexCharacters[(UCHAR)*Input % 16];
+        }
+    }
+    while(*++Input);
+
+    return ReturnedString;
+}
+
+/**
+ * Escapes a string according to RFC 1738.
+ * Required for passing parameters to the web service.
+ *
+ * @param Input
+ * Pointer to a std::string, which contains the input buffer to escape.
+ *
+ * @return
+ * The escaped string as std::string.
+ */
+string
+EscapeString(const string& Input)
+{
+    return EscapeString(Input.c_str());
+}
+
+/**
+ * Determines whether a string contains entirely numeric values.
+ *
+ * @param Input
+ * Constant pointer to a char array containing the input to check.
+ *
+ * @return
+ * true if the string is entirely numeric, false otherwise.
+ */
+bool
+IsNumber(const char* Input)
+{
+    do
+    {
+        if(!isdigit(*Input))
+            return false;
+
+        ++Input;
+    }
+    while(*Input);
+
+    return true;
+}
+
+/**
+ * Outputs a string through the standard output and the debug output.
+ * The string may have LF or CRLF line endings.
+ *
+ * @param String
+ * The std::string to output
+ */
+void
+StringOut(const string& String)
+{
+    char DbgString[DBGPRINT_BUFSIZE + 1];
+    size_t i;
+    string NewString;
+
+    /* Unify the line endings (the piped output of the tests may use CRLF) */
+    for(i = 0; i < String.size(); i++)
+    {
+        /* If this is a CRLF line-ending, only copy a \n to the new string and skip the next character */
+        if(String[i] == '\r' && String[i + 1] == '\n')
+        {
+            NewString += '\n';
+            ++i;
+        }
+        else
+        {
+            /* Otherwise copy the string */
+            NewString += String[i];
+        }
+    }
+
+    /* Output the string.
+       For DbgPrint, this must be done in chunks of 512 bytes. */
+    cout << NewString;
+
+    for(i = 0; i < NewString.size(); i += DBGPRINT_BUFSIZE)
+    {
+        size_t BytesToCopy;
+
+        if(NewString.size() - i > DBGPRINT_BUFSIZE)
+            BytesToCopy = DBGPRINT_BUFSIZE;
+        else
+            BytesToCopy = NewString.size() - i;
+
+        memcpy(DbgString, NewString.c_str() + i, BytesToCopy);
+        DbgString[BytesToCopy] = 0;
+
+        DbgPrint(DbgString);
+    }
+}
+
+/**
+ * Gets a value from a specified INI file and returns it converted to ASCII.
+ *
+ * @param AppName
+ * Constant pointer to a WCHAR array with the INI section to look in (lpAppName parameter passed to GetPrivateProfileStringW)
+ *
+ * @param KeyName
+ * Constant pointer to a WCHAR array containing the key to look for in the specified section (lpKeyName parameter passed to GetPrivateProfileStringW)
+ *
+ * @param FileName
+ * Constant pointer to a WCHAR array containing the path to the INI file
+ *
+ * @return
+ * Returns the data of the value as std::string or an empty string if no data could be retrieved.
+ */
+string
+GetINIValue(PCWCH AppName, PCWCH KeyName, PCWCH FileName)
+{
+    DWORD Length;
+    PCHAR AsciiBuffer;
+    string ReturnedString;
+    WCHAR Buffer[2048];
+
+    /* Load the value into a temporary Unicode buffer */
+    Length = GetPrivateProfileStringW(AppName, KeyName, NULL, Buffer, sizeof(Buffer) / sizeof(WCHAR), FileName);
+
+    if(Length)
+    {
+        /* Convert the string to ASCII charset */
+        AsciiBuffer = new char[Length + 1];
+        WideCharToMultiByte(CP_ACP, 0, Buffer, Length + 1, AsciiBuffer, Length + 1, NULL, NULL);
+
+        ReturnedString = AsciiBuffer;
+        delete AsciiBuffer;
+    }
+
+    return ReturnedString;
+}
+
+/**
+ * Converts an ASCII string to a Unicode one.
+ *
+ * @param AsciiString
+ * Constant pointer to a char array containing the ASCII string
+ *
+ * @return
+ * The Unicode string as std::wstring
+ */
+wstring
+AsciiToUnicode(const char* AsciiString)
+{
+    DWORD Length;
+    PWSTR UnicodeString;
+    wstring ReturnString;
+
+    Length = MultiByteToWideChar(CP_ACP, 0, AsciiString, -1, NULL, 0);
+
+    UnicodeString = new WCHAR[Length];
+    MultiByteToWideChar(CP_ACP, 0, AsciiString, -1, UnicodeString, Length);
+    ReturnString = UnicodeString;
+    delete UnicodeString;
+
+    return ReturnString;
+}
+
+/**
+ * Converts an ASCII string to a Unicode one.
+ *
+ * @param AsciiString
+ * Pointer to a std::string containing the ASCII string
+ *
+ * @return
+ * The Unicode string as std::wstring
+ */
+wstring
+AsciiToUnicode(const string& AsciiString)
+{
+    return AsciiToUnicode(AsciiString.c_str());
+}
+
+/**
+ * Converts a Unicode string to an ASCII one.
+ *
+ * @param UnicodeString
+ * Constant pointer to a WCHAR array containing the Unicode string
+ *
+ * @return
+ * The ASCII string as std::string
+ */
+string
+UnicodeToAscii(PCWSTR UnicodeString)
+{
+    DWORD Length;
+    PCHAR AsciiString;
+    string ReturnString;
+
+    Length = WideCharToMultiByte(CP_ACP, 0, UnicodeString, -1, NULL, 0, NULL, NULL);
+
+    AsciiString = new char[Length];
+    WideCharToMultiByte(CP_ACP, 0, UnicodeString, -1, AsciiString, Length, NULL, NULL);
+    ReturnString = AsciiString;
+    delete AsciiString;
+
+    return ReturnString;
+}
+
+/**
+ * Converts a Unicode string to an ASCII one.
+ *
+ * @param UnicodeString
+ * Pointer to a std::wstring containing the Unicode string
+ *
+ * @return
+ * The ASCII string as std::string
+ */
+string
+UnicodeToAscii(const wstring& UnicodeString)
+{
+    return UnicodeToAscii(UnicodeString.c_str());
+}
diff --git a/rostests/rosautotest/webservice.c b/rostests/rosautotest/webservice.c
deleted file mode 100644 (file)
index 852ee3d..0000000
+++ /dev/null
@@ -1,464 +0,0 @@
-/*
- * 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";
-
-    BOOL ReturnValue = FALSE;
-    HINTERNET hHTTP = NULL;
-    HINTERNET hHTTPRequest = NULL;
-    HINTERNET hInet = NULL;
-
-    /* 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");
-        goto Cleanup;
-    }
-
-    hHTTP = InternetConnectW(hInet, SERVER_HOSTNAME, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
-
-    if(!hHTTP)
-    {
-        StringOut("InternetConnectW failed\n");
-        goto Cleanup;
-    }
-
-    /* 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");
-        goto Cleanup;
-    }
-
-    if(!HttpSendRequestW(hHTTPRequest, Headers, wcslen(Headers), *Data, *DataLength))
-    {
-        StringOut("HttpSendRequestW failed\n");
-        goto Cleanup;
-    }
-
-    HeapFree(hProcessHeap, 0, *Data);
-    *Data = NULL;
-
-    /* Get the response */
-    if(!InternetQueryDataAvailable(hHTTPRequest, DataLength, 0, 0))
-    {
-        StringOut("InternetQueryDataAvailable failed\n");
-        goto Cleanup;
-    }
-
-    *Data = HeapAlloc(hProcessHeap, 0, *DataLength + 1);
-
-    if(!InternetReadFile(hHTTPRequest, *Data, *DataLength, DataLength))
-    {
-        StringOut("InternetReadFile failed\n");
-        goto Cleanup;
-    }
-
-    (*Data)[*DataLength] = 0;
-    ReturnValue = TRUE;
-
-Cleanup:
-    if(hHTTPRequest)
-        InternetCloseHandle(hHTTPRequest);
-
-    if(hHTTP)
-        InternetCloseHandle(hHTTP);
-
-    if(hInet)
-        InternetCloseHandle(hInet);
-
-    return ReturnValue;
-}
-
-/**
- * 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.
- * The caller needs to HeapFree the returned pointer in case of success.
- */
-PCHAR
-GetTestID(TESTTYPES TestType)
-{
-    const CHAR GetTestIDAction[] = "gettestid";
-    const CHAR CommentProp[] = "&comment=";
-
-    DWORD DataLength;
-    PCHAR Data;
-    PCHAR ReturnValue = NULL;
-
-    /* Build the full request string */
-    DataLength  = sizeof(ActionProp) - 1 + sizeof(GetTestIDAction) - 1;
-    DataLength += strlen(AuthenticationRequestString) + strlen(SystemInfoRequestString);
-
-    if(AppOptions.Comment)
-        DataLength += sizeof(CommentProp) - 1 + strlen(AppOptions.Comment);
-
-    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);
-
-    if(AppOptions.Comment)
-    {
-        strcat(Data, CommentProp);
-        strcat(Data, AppOptions.Comment);
-    }
-
-    strcat(Data, TestTypeProp);
-
-    switch(TestType)
-    {
-        case WineTest:
-            strcat(Data, WineTestType);
-            break;
-    }
-
-    if(!IntDoRequest(&Data, &DataLength))
-        goto Cleanup;
-
-    /* Verify that this is really a number */
-    if(!IsNumber(Data))
-    {
-        StringOut("Expected Test ID, but received:\n");
-        StringOut(Data);
-        StringOut("\n");
-        goto Cleanup;
-    }
-
-    ReturnValue = Data;
-
-Cleanup:
-    if(Data && ReturnValue != Data)
-        HeapFree(hProcessHeap, 0, Data);
-
-    return ReturnValue;
-}
-
-/**
- * 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.
- * The caller needs to HeapFree the returned pointer in case of success.
- */
-PCHAR
-GetSuiteID(TESTTYPES TestType, const PVOID TestData)
-{
-    const CHAR GetSuiteIDAction[] = "getsuiteid";
-    const CHAR ModuleProp[] = "&module=";
-    const CHAR TestProp[] = "&test=";
-
-    DWORD DataLength;
-    PCHAR Data;
-    PCHAR ReturnValue = NULL;
-    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))
-        goto Cleanup;
-
-    /* Verify that this is really a number */
-    if(!IsNumber(Data))
-    {
-        StringOut("Expected Suite ID, but received:\n");
-        StringOut(Data);
-        StringOut("\n");
-        goto Cleanup;
-    }
-
-    ReturnValue = Data;
-
-Cleanup:
-    if(Data && ReturnValue != Data)
-        HeapFree(hProcessHeap, 0, Data);
-
-    return ReturnValue;
-}
-
-/**
- * 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=";
-
-    BOOL ReturnValue = FALSE;
-    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))
-        goto Cleanup;
-
-    /* Output the response */
-    StringOut("The server responded:\n");
-    StringOut(Data);
-    StringOut("\n");
-
-    if(!strcmp(Data, "OK"))
-        ReturnValue = TRUE;
-
-Cleanup:
-    if(Data)
-        HeapFree(hProcessHeap, 0, Data);
-
-    return ReturnValue;
-}
-
-/**
- * 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";
-
-    BOOL ReturnValue = FALSE;
-    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))
-        goto Cleanup;
-
-    if(!strcmp(Data, "OK"))
-        ReturnValue = TRUE;
-
-Cleanup:
-    if(Data)
-        HeapFree(hProcessHeap, 0, Data);
-
-    return ReturnValue;
-}
diff --git a/rostests/rosautotest/winetests.c b/rostests/rosautotest/winetests.c
deleted file mode 100644 (file)
index b1c62c7..0000000
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * 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;
-    BOOL ReturnValue = FALSE;
-    DWORD BytesAvailable;
-    DWORD LogAvailable = 0;
-    DWORD LogLength = 0;
-    DWORD LogPosition = 0;
-    DWORD Temp;
-    PCHAR Buffer = NULL;
-    PROCESS_INFORMATION ProcessInfo = {0};
-
-    if(AppOptions.Submit)
-    {
-        /* Allocate one block for the log */
-        SubmitData->Log = HeapAlloc(hProcessHeap, 0, BUFFER_BLOCKSIZE);
-        LogAvailable = BUFFER_BLOCKSIZE;
-        LogLength = BUFFER_BLOCKSIZE;
-    }
-
-    /* Allocate a buffer with the exact size of the output string.
-       We have to output this string in one call to prevent a race condition, when another application also outputs a string over the debug line. */
-    Buffer = HeapAlloc(hProcessHeap, 0, 27 + strlen(GetSuiteIDData->Module) + 8 + strlen(GetSuiteIDData->Test) + 2);
-    sprintf(Buffer, "Running Wine Test, Module: %s, Test: %s\n", GetSuiteIDData->Module, GetSuiteIDData->Test);
-    StringOut(Buffer);
-    HeapFree(hProcessHeap, 0, Buffer);
-    Buffer = NULL;
-
-    /* Execute the test */
-    if(!CreateProcessW(NULL, CommandLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, StartupInfo, &ProcessInfo))
-    {
-        StringOut("CreateProcessW for running the test failed\n");
-        goto Cleanup;
-    }
-
-    /* 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");
-            goto Cleanup;
-        }
-
-        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");
-                goto Cleanup;
-            }
-
-            /* 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);
-            Buffer = NULL;
-        }
-    }
-    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)
-                    goto Cleanup;
-            }
-
-            /* Get a Suite ID for this combination */
-            SubmitData->General.SuiteID = GetSuiteID(WineTest, GetSuiteIDData);
-
-            if(!SubmitData->General.SuiteID)
-                goto Cleanup;
-
-            /* Submit the stuff */
-            Submit(WineTest, SubmitData);
-        }
-    }
-
-    StringOut("\n\n");
-
-    ReturnValue = TRUE;
-
-Cleanup:
-    if(Buffer)
-        HeapFree(hProcessHeap, 0, Buffer);
-
-    if(ProcessInfo.hProcess)
-        HeapFree(hProcessHeap, 0, ProcessInfo.hProcess);
-
-    if(ProcessInfo.hThread)
-        HeapFree(hProcessHeap, 0, ProcessInfo.hThread);
-
-    if(SubmitData->General.SuiteID)
-    {
-        HeapFree(hProcessHeap, 0, SubmitData->General.SuiteID);
-        SubmitData->General.SuiteID = NULL;
-    }
-
-    if(SubmitData->Log)
-    {
-        HeapFree(hProcessHeap, 0, SubmitData->Log);
-        SubmitData->Log = NULL;
-    }
-
-    return ReturnValue;
-}
-
-/**
- * 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)
-{
-    BOOL ReturnValue = FALSE;
-    DWORD BytesAvailable;
-    DWORD Length;
-    DWORD Temp;
-    PCHAR Buffer = NULL;
-    PCHAR pStart;
-    PCHAR pEnd;
-    PROCESS_INFORMATION ProcessInfo = {0};
-    PWSTR pUnderscore;
-    size_t FilePosition;
-    WINE_GETSUITEID_DATA GetSuiteIDData = {0};
-
-    /* Build the full command line */
-    FilePosition = wcslen(FilePath);
-    FilePath[FilePosition++] = ' ';
-    FilePath[FilePosition] = 0;
-    wcscat(FilePath, L"--list");
-
-    /* Find the underscore in the file name */
-    pUnderscore = wcschr(File, L'_');
-
-    if(!pUnderscore)
-    {
-        StringOut("Invalid test file name: ");
-
-        Length = wcslen(File);
-        Buffer = HeapAlloc(hProcessHeap, 0, Length + 1);
-        WideCharToMultiByte(CP_ACP, 0, File, Length + 1, Buffer, Length + 1, NULL, NULL);
-
-        StringOut(Buffer);
-        StringOut("\n");
-
-        goto Cleanup;
-    }
-
-    /* Store the tested module name */
-    Length = pUnderscore - 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");
-        goto Cleanup;
-    }
-
-    /* Wait till this process ended */
-    if(WaitForSingleObject(ProcessInfo.hProcess, INFINITE) == WAIT_FAILED)
-    {
-        StringOut("WaitForSingleObject failed for the test list\n");
-        goto Cleanup;
-    }
-
-    /* Read the output data into a buffer */
-    if(!PeekNamedPipe(hReadPipe, NULL, 0, NULL, &BytesAvailable, NULL))
-    {
-        StringOut("PeekNamedPipe failed for the test list\n");
-        goto Cleanup;
-    }
-
-    /* Check if we got any */
-    if(!BytesAvailable)
-    {
-        StringOut("The --list command did not return any data for ");
-
-        Length = wcslen(File);
-        Buffer = HeapAlloc(hProcessHeap, 0, Length + 1);
-        WideCharToMultiByte(CP_ACP, 0, File, Length + 1, Buffer, Length + 1, NULL, NULL);
-
-        StringOut(Buffer);
-        StringOut("\n");
-
-        goto Cleanup;
-    }
-
-    /* Read the data */
-    Buffer = HeapAlloc(hProcessHeap, 0, BytesAvailable);
-
-    if(!ReadFile(hReadPipe, Buffer, BytesAvailable, &Temp, NULL))
-    {
-        StringOut("ReadFile failed\n");
-        goto Cleanup;
-    }
-
-    /* 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))
-                goto Cleanup;
-        }
-
-        /* Cleanup */
-        HeapFree(hProcessHeap, 0, GetSuiteIDData.Test);
-        GetSuiteIDData.Test = NULL;
-
-        /* Move to the next test */
-        pStart = pEnd + 6;
-    }
-
-    ReturnValue = TRUE;
-
-Cleanup:
-    if(GetSuiteIDData.Module)
-        HeapFree(hProcessHeap, 0, GetSuiteIDData.Module);
-
-    if(GetSuiteIDData.Test)
-        HeapFree(hProcessHeap, 0, GetSuiteIDData.Test);
-
-    if(Buffer)
-        HeapFree(hProcessHeap, 0, Buffer);
-
-    if(ProcessInfo.hProcess)
-        CloseHandle(ProcessInfo.hProcess);
-
-    if(ProcessInfo.hThread)
-        CloseHandle(ProcessInfo.hThread);
-
-    return ReturnValue;
-}
-
-/**
- * Runs the Wine tests according to the options specified by the parameters.
- *
- * @return
- * TRUE if everything went well, FALSE otherwise.
- */
-BOOL
-RunWineTests()
-{
-    BOOL ReturnValue = FALSE;
-    GENERAL_FINISH_DATA FinishData;
-    HANDLE hFind = NULL;
-    HANDLE hReadPipe = NULL;
-    HANDLE hWritePipe = NULL;
-    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");
-        goto Cleanup;
-    }
-
-    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");
-        goto Cleanup;
-    }
-
-    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");
-        goto Cleanup;
-    }
-
-    /* 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))
-            goto Cleanup;
-    }
-    while(FindNextFileW(hFind, &fd));
-
-    /* Close this test run if necessary */
-    if(SubmitData.General.TestID)
-    {
-        FinishData.TestID = SubmitData.General.TestID;
-
-        if(!Finish(WineTest, &FinishData))
-            goto Cleanup;
-    }
-
-    ReturnValue = TRUE;
-
-Cleanup:
-    if(SubmitData.General.TestID)
-        HeapFree(hProcessHeap, 0, SubmitData.General.TestID);
-
-    if(hFind && hFind != INVALID_HANDLE_VALUE)
-        FindClose(hFind);
-
-    if(hReadPipe)
-        CloseHandle(hReadPipe);
-
-    if(hWritePipe)
-        CloseHandle(hWritePipe);
-
-    return ReturnValue;
-}