[ADVAPI32_APITEST]: Add a small helper lib 'svchlp' for common routines for test...
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 5 Dec 2016 16:22:30 +0000 (16:22 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Mon, 5 Dec 2016 16:22:30 +0000 (16:22 +0000)
CORE-12414

svn path=/trunk/; revision=73430

rostests/apitests/advapi32/CMakeLists.txt
rostests/apitests/advapi32/svchlp.c [new file with mode: 0644]
rostests/apitests/advapi32/svchlp.h [new file with mode: 0644]

index 13a4fea..5ef6049 100644 (file)
@@ -14,6 +14,7 @@ list(APPEND SOURCE
     RtlEncryptMemory.c
     SaferIdentifyLevel.c
     ServiceArgs.c
+    svchlp.c
     testlist.c)
 
 add_executable(advapi32_apitest ${SOURCE})
diff --git a/rostests/apitests/advapi32/svchlp.c b/rostests/apitests/advapi32/svchlp.c
new file mode 100644 (file)
index 0000000..764c5d8
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * PROJECT:         ReactOS api tests
+ * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Support helpers for embedded services inside api tests.
+ * PROGRAMMERS:     Jacek Caban for CodeWeavers
+ *                  Thomas Faber <thomas.faber@reactos.org>
+ *                  Hermes Belusca-Maito
+ *
+ * NOTE: Room for improvements:
+ * - One test_runner managing 1 pipe for 1 service process that is shared
+ *   by multiple services of type SERVICE_WIN32_SHARE_PROCESS.
+ * - Find a way to elegantly determine the registered service name inside
+ *   the service process, without really passing it 
+ */
+
+#include <apitest.h>
+#include <winnls.h>
+#include <winsvc.h>
+#include <strsafe.h>
+
+static HANDLE hClientPipe = INVALID_HANDLE_VALUE;
+static WCHAR named_pipe_name[100]; // Shared: FIXME!
+
+static CHAR  service_nameA[100];
+static WCHAR service_nameW[100];
+
+
+/**********  S E R V I C E   ( C L I E N T )   M O D U L E   S I D E  *********/
+
+void send_msg(const char *type, const char *msg)
+{
+    DWORD written = 0;
+    char buf[512];
+
+    StringCbPrintfA(buf, sizeof(buf), "%s:%s", type, msg);
+    WriteFile(hClientPipe, buf, strlen(buf)+1, &written, NULL);
+}
+
+void service_trace(const char *msg, ...)
+{
+    va_list valist;
+    char buf[512];
+
+    va_start(valist, msg);
+    StringCbVPrintfA(buf, sizeof(buf), msg, valist);
+    va_end(valist);
+
+    send_msg("TRACE", buf);
+}
+
+void service_ok(int cnd, const char *msg, ...)
+{
+    va_list valist;
+    char buf[512];
+
+    va_start(valist, msg);
+    StringCbVPrintfA(buf, sizeof(buf), msg, valist);
+    va_end(valist);
+
+    send_msg(cnd ? "OK" : "FAIL", buf);
+}
+
+void service_process(BOOL (*start_service)(PCSTR, PCWSTR), int argc, char** argv)
+{
+    BOOL res;
+
+    StringCbCopyA(service_nameA, sizeof(service_nameA), argv[2]);
+    MultiByteToWideChar(CP_ACP, 0, service_nameA, -1, service_nameW, _countof(service_nameW));
+    StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW);
+
+    res = WaitNamedPipeW(named_pipe_name, NMPWAIT_USE_DEFAULT_WAIT);
+    if (!res)
+        return;
+
+    hClientPipe = CreateFileW(named_pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hClientPipe == INVALID_HANDLE_VALUE)
+        return;
+
+    service_trace("Service process starting...\n");
+    res = start_service(service_nameA, service_nameW);
+    service_trace("Service process stopped.\n");
+
+    CloseHandle(hClientPipe);
+}
+
+
+/***********  T E S T E R   ( S E R V E R )   M O D U L E   S I D E  **********/
+
+SC_HANDLE register_service_exA(
+    SC_HANDLE scm_handle,
+    PCSTR test_name,
+    PCSTR service_name, // LPCSTR lpServiceName,
+    PCSTR extra_args OPTIONAL,
+    DWORD dwDesiredAccess,
+    DWORD dwServiceType,
+    DWORD dwStartType,
+    DWORD dwErrorControl,
+    LPCSTR lpLoadOrderGroup OPTIONAL,
+    LPDWORD lpdwTagId OPTIONAL,
+    LPCSTR lpDependencies OPTIONAL,
+    LPCSTR lpServiceStartName OPTIONAL,
+    LPCSTR lpPassword OPTIONAL)
+{
+    SC_HANDLE service;
+    CHAR service_cmd[MAX_PATH+150];
+
+    /* Retrieve our full path */
+    if (!GetModuleFileNameA(NULL, service_cmd, MAX_PATH))
+    {
+        skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
+        return NULL;
+    }
+
+    /*
+     * Build up our custom command line. The first parameter is the test name,
+     * the second parameter is the flag used to decide whether we should start
+     * as a service.
+     */
+    StringCbCatA(service_cmd, sizeof(service_cmd), " ");
+    StringCbCatA(service_cmd, sizeof(service_cmd), test_name);
+    StringCbCatA(service_cmd, sizeof(service_cmd), " ");
+    StringCbCatA(service_cmd, sizeof(service_cmd), service_name);
+    if (extra_args)
+    {
+        StringCbCatA(service_cmd, sizeof(service_cmd), " ");
+        StringCbCatA(service_cmd, sizeof(service_cmd), extra_args);
+    }
+
+    trace("service_cmd \"%s\"\n", service_cmd);
+
+    service = CreateServiceA(scm_handle, service_name, service_name,
+                             dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
+                             service_cmd, lpLoadOrderGroup, lpdwTagId, lpDependencies,
+                             lpServiceStartName, lpPassword);
+    if (!service && GetLastError() == ERROR_ACCESS_DENIED)
+    {
+        skip("Not enough access right to create service.\n");
+        return NULL;
+    }
+
+    ok(service != NULL, "CreateService failed: %lu\n", GetLastError());
+    return service;
+}
+
+SC_HANDLE register_service_exW(
+    SC_HANDLE scm_handle,
+    PCWSTR test_name,
+    PCWSTR service_name, // LPCWSTR lpServiceName,
+    PCWSTR extra_args OPTIONAL,
+    DWORD dwDesiredAccess,
+    DWORD dwServiceType,
+    DWORD dwStartType,
+    DWORD dwErrorControl,
+    LPCWSTR lpLoadOrderGroup OPTIONAL,
+    LPDWORD lpdwTagId OPTIONAL,
+    LPCWSTR lpDependencies OPTIONAL,
+    LPCWSTR lpServiceStartName OPTIONAL,
+    LPCWSTR lpPassword OPTIONAL)
+{
+    SC_HANDLE service;
+    WCHAR service_cmd[MAX_PATH+150];
+
+    /* Retrieve our full path */
+    if (!GetModuleFileNameW(NULL, service_cmd, MAX_PATH))
+    {
+        skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
+        return NULL;
+    }
+
+    /*
+     * Build up our custom command line. The first parameter is the test name,
+     * the second parameter is the flag used to decide whether we should start
+     * as a service.
+     */
+    StringCbCatW(service_cmd, sizeof(service_cmd), L" ");
+    StringCbCatW(service_cmd, sizeof(service_cmd), test_name);
+    StringCbCatW(service_cmd, sizeof(service_cmd), L" ");
+    StringCbCatW(service_cmd, sizeof(service_cmd), service_name);
+    if (extra_args)
+    {
+        StringCbCatW(service_cmd, sizeof(service_cmd), L" ");
+        StringCbCatW(service_cmd, sizeof(service_cmd), extra_args);
+    }
+
+    trace("service_cmd \"%ls\"\n", service_cmd);
+
+    service = CreateServiceW(scm_handle, service_name, service_name,
+                             dwDesiredAccess, dwServiceType, dwStartType, dwErrorControl,
+                             service_cmd, lpLoadOrderGroup, lpdwTagId, lpDependencies,
+                             lpServiceStartName, lpPassword);
+    if (!service && GetLastError() == ERROR_ACCESS_DENIED)
+    {
+        skip("Not enough access right to create service.\n");
+        return NULL;
+    }
+
+    ok(service != NULL, "CreateService failed: %lu\n", GetLastError());
+    return service;
+}
+
+SC_HANDLE register_serviceA(
+    SC_HANDLE scm_handle,
+    PCSTR test_name,
+    PCSTR service_name,
+    PCSTR extra_args OPTIONAL)
+{
+    return register_service_exA(scm_handle, test_name, service_name, extra_args,
+                                SERVICE_ALL_ACCESS,
+                                SERVICE_WIN32_OWN_PROCESS,
+                                SERVICE_DEMAND_START,
+                                SERVICE_ERROR_IGNORE,
+                                NULL, NULL, NULL, NULL, NULL);
+}
+
+SC_HANDLE register_serviceW(
+    SC_HANDLE scm_handle,
+    PCWSTR test_name,
+    PCWSTR service_name,
+    PCWSTR extra_args OPTIONAL)
+{
+    return register_service_exW(scm_handle, test_name, service_name, extra_args,
+                                SERVICE_ALL_ACCESS,
+                                SERVICE_WIN32_OWN_PROCESS,
+                                SERVICE_DEMAND_START,
+                                SERVICE_ERROR_IGNORE,
+                                NULL, NULL, NULL, NULL, NULL);
+}
+
+static DWORD WINAPI pipe_thread(void *param)
+{
+    HANDLE hServerPipe = (HANDLE)param;
+    DWORD read;
+    BOOL res;
+    char buf[512];
+
+    // printf("pipe_thread -- ConnectNamedPipe...\n");
+    res = ConnectNamedPipe(hServerPipe, NULL);
+    ok(res || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe failed: %lu\n", GetLastError());
+    // printf("pipe_thread -- ConnectNamedPipe ok\n");
+
+    while (1)
+    {
+        res = ReadFile(hServerPipe, buf, sizeof(buf), &read, NULL);
+        if (!res)
+        {
+            ok(GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_INVALID_HANDLE,
+               "ReadFile failed: %lu\n", GetLastError());
+            // printf("pipe_thread -- break loop\n");
+            break;
+        }
+
+        if (!strncmp(buf, "TRACE:", 6))
+        {
+            trace("service trace: %s", buf+6);
+        }
+        else if (!strncmp(buf, "OK:", 3))
+        {
+            ok(1, "service: %s", buf+3);
+        }
+        else if (!strncmp(buf, "FAIL:", 5))
+        {
+            ok(0, "service: %s", buf+5);
+        }
+        else
+        {
+            ok(0, "malformed service message: %s\n", buf);
+        }
+    }
+
+    // printf("pipe_thread -- DisconnectNamedPipe\n");
+
+    /*
+     * Flush the pipe to allow the client to read
+     * the pipe's contents before disconnecting.
+     */
+    FlushFileBuffers(hServerPipe); 
+
+    DisconnectNamedPipe(hServerPipe);
+    trace("pipe disconnected\n");
+    return 0;
+}
+
+void test_runner(void (*run_test)(PCSTR, PCWSTR, void*), void *param)
+{
+    HANDLE hServerPipe = INVALID_HANDLE_VALUE;
+    HANDLE hThread;
+
+    StringCbPrintfW(service_nameW, sizeof(service_nameW), L"WineTestService%lu", GetTickCount());
+    WideCharToMultiByte(CP_ACP, 0, service_nameW, -1, service_nameA, _countof(service_nameA), NULL, NULL);
+    //trace("service_name: %ls\n", service_nameW);
+    StringCbPrintfW(named_pipe_name, sizeof(named_pipe_name), L"\\\\.\\pipe\\%ls_pipe", service_nameW);
+
+    hServerPipe = CreateNamedPipeW(named_pipe_name, PIPE_ACCESS_INBOUND,
+                                   PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT, 10, 2048, 2048, 10000, NULL);
+    ok(hServerPipe != INVALID_HANDLE_VALUE, "CreateNamedPipe failed: %lu\n", GetLastError());
+    if (hServerPipe == INVALID_HANDLE_VALUE)
+        return;
+
+    hThread = CreateThread(NULL, 0, pipe_thread, (LPVOID)hServerPipe, 0, NULL);
+    ok(hThread != NULL, "CreateThread failed: %lu\n", GetLastError());
+    if (!hThread)
+        goto Quit;
+
+    run_test(service_nameA, service_nameW, param);
+
+    ok(WaitForSingleObject(hThread, 10000) == WAIT_OBJECT_0, "Timeout waiting for thread\n");
+
+Quit:
+    if (hThread)
+    {
+        /* Be sure to kill the thread if it did not already terminate */
+        TerminateThread(hThread, 0);
+        CloseHandle(hThread);
+    }
+
+    if (hServerPipe != INVALID_HANDLE_VALUE)
+        CloseHandle(hServerPipe);
+}
diff --git a/rostests/apitests/advapi32/svchlp.h b/rostests/apitests/advapi32/svchlp.h
new file mode 100644 (file)
index 0000000..3e503f8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * PROJECT:         ReactOS api tests
+ * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
+ * PURPOSE:         Support helpers for embedded services inside api tests.
+ * PROGRAMMERS:     Jacek Caban for CodeWeavers
+ *                  Thomas Faber <thomas.faber@reactos.org>
+ *                  Hermes Belusca-Maito
+ */
+
+// #include <apitest.h>
+
+
+/**********  S E R V I C E   ( C L I E N T )   M O D U L E   S I D E  *********/
+
+void send_msg(const char *type, const char *msg);
+void service_trace(const char *msg, ...);
+void service_ok(int cnd, const char *msg, ...);
+void service_process(BOOL (*start_service)(PCSTR, PCWSTR), int argc, char** argv);
+
+
+/***********  T E S T E R   ( S E R V E R )   M O D U L E   S I D E  **********/
+
+SC_HANDLE register_service_exA(
+    SC_HANDLE scm_handle,
+    PCSTR test_name,
+    PCSTR service_name, // LPCSTR lpServiceName,
+    PCSTR extra_args OPTIONAL,
+    DWORD dwDesiredAccess,
+    DWORD dwServiceType,
+    DWORD dwStartType,
+    DWORD dwErrorControl,
+    LPCSTR lpLoadOrderGroup OPTIONAL,
+    LPDWORD lpdwTagId OPTIONAL,
+    LPCSTR lpDependencies OPTIONAL,
+    LPCSTR lpServiceStartName OPTIONAL,
+    LPCSTR lpPassword OPTIONAL);
+
+SC_HANDLE register_service_exW(
+    SC_HANDLE scm_handle,
+    PCWSTR test_name,
+    PCWSTR service_name, // LPCWSTR lpServiceName,
+    PCWSTR extra_args OPTIONAL,
+    DWORD dwDesiredAccess,
+    DWORD dwServiceType,
+    DWORD dwStartType,
+    DWORD dwErrorControl,
+    LPCWSTR lpLoadOrderGroup OPTIONAL,
+    LPDWORD lpdwTagId OPTIONAL,
+    LPCWSTR lpDependencies OPTIONAL,
+    LPCWSTR lpServiceStartName OPTIONAL,
+    LPCWSTR lpPassword OPTIONAL);
+
+SC_HANDLE register_serviceA(
+    SC_HANDLE scm_handle,
+    PCSTR test_name,
+    PCSTR service_name,
+    PCSTR extra_args OPTIONAL);
+
+SC_HANDLE register_serviceW(
+    SC_HANDLE scm_handle,
+    PCWSTR test_name,
+    PCWSTR service_name,
+    PCWSTR extra_args OPTIONAL);
+
+#ifdef UNICODE
+#define register_service_ex register_service_exW
+#define register_service    register_serviceW
+#else
+#define register_service_ex register_service_exA
+#define register_service    register_serviceA
+#endif
+
+void test_runner(void (*run_test)(PCSTR, PCWSTR, void*), void *param);