ReactOS 0.4.15-dev-7958-gcd0bb1a
tests.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Local Spooler API Tests
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Test list
5 * COPYRIGHT: Copyright 2015-2016 Colin Finck (colin@reactos.org)
6 */
7
8/*
9 * The original localspl.dll from Windows Server 2003 is not easily testable.
10 * It relies on a proper initialization inside spoolsv.exe, so we can't just load it in an API-Test as usual.
11 * See https://www.reactos.org/pipermail/ros-dev/2015-June/017395.html for more information.
12 *
13 * To make testing possible anyway, this program basically does four things:
14 * - Injecting our testing code into spoolsv.exe.
15 * - Registering and running us as a service in the SYSTEM security context like spoolsv.exe, so that injection is possible at all.
16 * - Sending the test name and receiving the console output over named pipes.
17 * - Redirecting the received console output to stdout again, so it looks and feels like a standard API-Test.
18 *
19 * To simplify debugging of the injected code, it is entirely separated into a DLL file localspl_apitest.dll.
20 * What we actually inject is a LoadLibraryW call, so that the DLL is loaded gracefully without any hacks.
21 * Therefore, you can just attach your debugger to the spoolsv.exe process and set breakpoints on the localspl_apitest.dll code.
22 */
23
24#include <apitest.h>
25
26#define WIN32_NO_STATUS
27#include <stdio.h>
28#include <stdlib.h>
29#include <windef.h>
30#include <winbase.h>
31#include <wingdi.h>
32#include <winreg.h>
33#include <winsvc.h>
34#include <winspool.h>
35#include <winsplp.h>
36
37#include "localspl_apitest.h"
38
39
40static void
41_RunRemoteTest(const char* szTestName)
42{
43 BOOL bSuccessful = FALSE;
44 char szBuffer[1024];
45 DWORD cbRead;
46 DWORD cbWritten;
47 HANDLE hCommandPipe = INVALID_HANDLE_VALUE;
49 HANDLE hOutputPipe = INVALID_HANDLE_VALUE;
50 PWSTR p;
51 SC_HANDLE hSC = NULL;
52 SC_HANDLE hService = NULL;
54 WCHAR wszFilePath[MAX_PATH + 20];
56
57 // Do a dummy EnumPrintersW call.
58 // This guarantees that the Spooler Service has actually loaded localspl.dll, which is a requirement for our injected DLL to work properly.
59 EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbRead, &cbWritten);
60
61 // Get the full path to our EXE file.
62 if (!GetModuleFileNameW(NULL, wszFilePath, MAX_PATH))
63 {
64 skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
65 goto Cleanup;
66 }
67
68 // Replace the extension.
69 p = wcsrchr(wszFilePath, L'.');
70 if (!p)
71 {
72 skip("File path has no file extension: %S\n", wszFilePath);
73 goto Cleanup;
74 }
75
76 wcscpy(p, L".dll");
77
78 // Check if the corresponding DLL file exists.
79 hFind = FindFirstFileW(wszFilePath, &fd);
80 if (hFind == INVALID_HANDLE_VALUE)
81 {
82 skip("My DLL file \"%S\" does not exist!\n", wszFilePath);
83 goto Cleanup;
84 }
85
86 // Change the extension back to .exe and add the parameters.
87 wcscpy(p, L".exe service dummy");
88
89 // Open a handle to the service manager.
91 if (!hSC)
92 {
93 skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
94 goto Cleanup;
95 }
96
97 // Ensure that the spooler service is running.
98 hService = OpenServiceW(hSC, L"spooler", SERVICE_QUERY_STATUS);
99 if (!hService)
100 {
101 skip("OpenServiceW failed for the spooler service with error %lu!\n", GetLastError());
102 goto Cleanup;
103 }
104
105 if (!QueryServiceStatus(hService, &ServiceStatus))
106 {
107 skip("QueryServiceStatus failed for the spooler service with error %lu!\n", GetLastError());
108 goto Cleanup;
109 }
110
112 {
113 skip("Spooler Service is not running!\n");
114 goto Cleanup;
115 }
116
117 CloseServiceHandle(hService);
118
119 // Try to open the service if we've created it in a previous run.
121 if (!hService)
122 {
124 {
125 // Create the service.
127 if (!hService)
128 {
129 skip("CreateServiceW failed with error %lu!\n", GetLastError());
130 goto Cleanup;
131 }
132 }
133 else
134 {
135 skip("OpenServiceW failed with error %lu!\n", GetLastError());
136 goto Cleanup;
137 }
138 }
139
140 // Create pipes for the communication with the injected DLL.
142 if (hCommandPipe == INVALID_HANDLE_VALUE)
143 {
144 skip("CreateNamedPipeW failed for the command pipe with error %lu!\n", GetLastError());
145 goto Cleanup;
146 }
147
149 if (hOutputPipe == INVALID_HANDLE_VALUE)
150 {
151 skip("CreateNamedPipeW failed for the output pipe with error %lu!\n", GetLastError());
152 goto Cleanup;
153 }
154
155 // Start the service with "service" and a dummy parameter (to distinguish it from a call by rosautotest to localspl_apitest:service)
156 if (!StartServiceW(hService, 0, NULL))
157 {
158 skip("StartServiceW failed with error %lu!\n", GetLastError());
159 goto Cleanup;
160 }
161
162 // Wait till it has injected the DLL and the DLL expects its test name.
163 if (!ConnectNamedPipe(hCommandPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED)
164 {
165 skip("ConnectNamedPipe failed for the command pipe with error %lu!\n", GetLastError());
166 goto Cleanup;
167 }
168
169 // Send the test name.
170 if (!WriteFile(hCommandPipe, szTestName, lstrlenA(szTestName) + sizeof(char), &cbWritten, NULL))
171 {
172 skip("WriteFile failed with error %lu!\n", GetLastError());
173 goto Cleanup;
174 }
175
176 // Now wait for the DLL to connect to the output pipe.
177 if (!ConnectNamedPipe(hOutputPipe, NULL))
178 {
179 skip("ConnectNamedPipe failed for the output pipe with error %lu!\n", GetLastError());
180 goto Cleanup;
181 }
182
183 // Get all testing messages from the pipe and output them on stdout.
184 while (ReadFile(hOutputPipe, szBuffer, sizeof(szBuffer), &cbRead, NULL) && cbRead)
185 fwrite(szBuffer, sizeof(char), cbRead, stdout);
186
187 bSuccessful = TRUE;
188
189Cleanup:
190 if (hCommandPipe != INVALID_HANDLE_VALUE)
191 CloseHandle(hCommandPipe);
192
193 if (hOutputPipe != INVALID_HANDLE_VALUE)
194 CloseHandle(hOutputPipe);
195
196 if (hFind != INVALID_HANDLE_VALUE)
197 FindClose(hFind);
198
199 if (hService)
200 CloseServiceHandle(hService);
201
202 if (hSC)
204
205 // If we successfully received test output through the named pipe, we have also output a summary line already.
206 // Prevent the testing framework from outputting another "0 tests executed" line in this case.
207 if (bSuccessful)
208 ExitProcess(0);
209}
210
211START_TEST(fpEnumPrinters)
212{
213#if defined(_M_AMD64)
215 {
216 skip("ROSTESTS-366: Skipping localspl_apitest:fpEnumPrinters because it hangs on Windows Server 2003 x64-Testbot. Set winetest_interactive to run it anyway.\n");
217 return;
218 }
219#endif
220
221 _RunRemoteTest("fpEnumPrinters");
222}
223
224START_TEST(fpGetPrintProcessorDirectory)
225{
226#if defined(_M_AMD64)
228 {
229 skip("ROSTESTS-366: Skipping localspl_apitest:fpGetPrintProcessorDirectory because it hangs on Windows Server 2003 x64-Testbot. Set winetest_interactive to run it anyway.\n");
230 return;
231 }
232#endif
233
234 _RunRemoteTest("fpGetPrintProcessorDirectory");
235}
236
237START_TEST(fpSetJob)
238{
239#if defined(_M_AMD64)
241 {
242 skip("ROSTESTS-366: Skipping localspl_apitest:fpSetJob because it hangs on Windows Server 2003 x64-Testbot. Set winetest_interactive to run it anyway.\n");
243 return;
244 }
245#endif
246
247 _RunRemoteTest("fpSetJob");
248}
#define skip(...)
Definition: atltest.h:64
#define START_TEST(x)
Definition: atltest.h:75
static SERVICE_STATUS ServiceStatus
Definition: browser.c:22
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define CloseHandle
Definition: compat.h:739
#define wcsrchr
Definition: compat.h:16
#define ReadFile(a, b, c, d, e)
Definition: compat.h:742
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
#define MAX_PATH
Definition: compat.h:34
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
DWORD WINAPI GetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize)
Definition: loader.c:600
VOID WINAPI ExitProcess(IN UINT uExitCode)
Definition: proc.c:1487
static const WCHAR Cleanup[]
Definition: register.c:80
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLfloat GLfloat p
Definition: glext.h:8902
#define stdout
Definition: stdio.h:99
_Check_return_opt_ _CRTIMP size_t __cdecl fwrite(_In_reads_bytes_(_Size *_Count) const void *_Str, _In_ size_t _Size, _In_ size_t _Count, _Inout_ FILE *_File)
#define COMMAND_PIPE_NAME
#define OUTPUT_PIPE_NAME
int WINAPI lstrlenA(LPCSTR lpString)
Definition: lstring.c:145
BOOL WINAPI ConnectNamedPipe(IN HANDLE hNamedPipe, IN LPOVERLAPPED lpOverlapped)
Definition: npipe.c:701
HANDLE WINAPI CreateNamedPipeW(LPCWSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
Definition: npipe.c:246
#define L(x)
Definition: ntvdm.h:50
BOOL WINAPI QueryServiceStatus(SC_HANDLE hService, LPSERVICE_STATUS lpServiceStatus)
Definition: scm.c:2845
SC_HANDLE WINAPI OpenSCManagerW(LPCWSTR lpMachineName, LPCWSTR lpDatabaseName, DWORD dwDesiredAccess)
Definition: scm.c:2068
SC_HANDLE WINAPI OpenServiceW(SC_HANDLE hSCManager, LPCWSTR lpServiceName, DWORD dwDesiredAccess)
Definition: scm.c:2160
SC_HANDLE WINAPI CreateServiceW(SC_HANDLE hSCManager, LPCWSTR lpServiceName, LPCWSTR lpDisplayName, DWORD dwDesiredAccess, DWORD dwServiceType, DWORD dwStartType, DWORD dwErrorControl, LPCWSTR lpBinaryPathName, LPCWSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCWSTR lpDependencies, LPCWSTR lpServiceStartName, LPCWSTR lpPassword)
Definition: scm.c:812
BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs, LPCWSTR *lpServiceArgVectors)
Definition: scm.c:2980
BOOL WINAPI CloseServiceHandle(SC_HANDLE hSCObject)
Definition: scm.c:580
_CRTIMP wchar_t *__cdecl wcscpy(_Out_writes_z_(_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
int winetest_interactive
static int fd
Definition: io.c:51
DWORD dwCurrentState
Definition: winsvc.h:100
static void _RunRemoteTest(const char *szTestName)
Definition: tests.c:41
uint16_t * PWSTR
Definition: typedefs.h:56
#define PIPE_ACCESS_INBOUND
Definition: winbase.h:165
#define PIPE_READMODE_BYTE
Definition: winbase.h:169
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
#define PIPE_WAIT
Definition: winbase.h:171
#define PIPE_READMODE_MESSAGE
Definition: winbase.h:170
#define PIPE_ACCESS_OUTBOUND
Definition: winbase.h:166
#define PIPE_TYPE_BYTE
Definition: winbase.h:167
#define PIPE_TYPE_MESSAGE
Definition: winbase.h:168
#define ERROR_SERVICE_DOES_NOT_EXIST
Definition: winerror.h:611
#define ERROR_PIPE_CONNECTED
Definition: winerror.h:352
#define PRINTER_ENUM_LOCAL
Definition: winspool.h:896
WINBOOL WINAPI EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
#define PRINTER_ENUM_NAME
Definition: winspool.h:899
#define SERVICE_QUERY_STATUS
Definition: winsvc.h:55
#define SERVICE_ALL_ACCESS
Definition: winsvc.h:62
#define SERVICE_RUNNING
Definition: winsvc.h:24
#define SC_MANAGER_ALL_ACCESS
Definition: winsvc.h:13
#define SERVICE_NAME
Definition: wlansvc.c:18
#define SERVICE_DEMAND_START
Definition: cmtypes.h:978
#define SERVICE_WIN32_OWN_PROCESS
Definition: cmtypes.h:962
#define SERVICE_ERROR_IGNORE
Definition: cmtypes.h:981
__wchar_t WCHAR
Definition: xmlstorage.h:180