From: Hermès Bélusca-Maïto Date: Mon, 5 Dec 2016 16:22:30 +0000 (+0000) Subject: [ADVAPI32_APITEST]: Add a small helper lib 'svchlp' for common routines for test... X-Git-Tag: ReactOS-0.4.4-FOSDEM2017~105 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=8e18f1fbd9e86daf000acb2dadee8f03f6939d57 [ADVAPI32_APITEST]: Add a small helper lib 'svchlp' for common routines for test-services embedded in api tests. Taken & heavily adapted from Jacek Caban's services_service test and Thomas Faber's ServiceArgs.c (the latter will be able to use 'svchlp' when it'll be ready). CORE-12414 svn path=/trunk/; revision=73430 --- diff --git a/rostests/apitests/advapi32/CMakeLists.txt b/rostests/apitests/advapi32/CMakeLists.txt index 13a4feaf546..5ef604958d7 100644 --- a/rostests/apitests/advapi32/CMakeLists.txt +++ b/rostests/apitests/advapi32/CMakeLists.txt @@ -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 index 00000000000..764c5d8359e --- /dev/null +++ b/rostests/apitests/advapi32/svchlp.c @@ -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 + * 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 +#include +#include +#include + +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 index 00000000000..3e503f8b49d --- /dev/null +++ b/rostests/apitests/advapi32/svchlp.h @@ -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 + * Hermes Belusca-Maito + */ + +// #include + + +/********** 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);