ReactOS  0.4.14-dev-50-g13bb5e2
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 
40 static 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.
120  hService = OpenServiceW(hSC, SERVICE_NAME, SERVICE_ALL_ACCESS);
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, strlen(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 
189 Cleanup:
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)
203  CloseServiceHandle(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 
211 START_TEST(fpEnumPrinters)
212 {
213  _RunRemoteTest("fpEnumPrinters");
214 }
215 
216 START_TEST(fpGetPrintProcessorDirectory)
217 {
218  _RunRemoteTest("fpGetPrintProcessorDirectory");
219 }
220 
221 START_TEST(fpSetJob)
222 {
223  _RunRemoteTest("fpSetJob");
224 }
DWORD WINAPI GetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize)
Definition: loader.c:609
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
#define SERVICE_ERROR_IGNORE
Definition: cmtypes.h:979
#define TRUE
Definition: types.h:120
#define CloseHandle
Definition: compat.h:398
DWORD dwCurrentState
Definition: winsvc.h:100
#define PIPE_TYPE_MESSAGE
Definition: winbase.h:168
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
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:808
uint16_t * PWSTR
Definition: typedefs.h:54
static SERVICE_STATUS ServiceStatus
Definition: dcomlaunch.c:27
#define PIPE_WAIT
Definition: winbase.h:171
#define OUTPUT_PIPE_NAME
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
VOID WINAPI ExitProcess(IN UINT uExitCode)
Definition: proc.c:1487
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
START_TEST(fpEnumPrinters)
Definition: tests.c:211
static int fd
Definition: io.c:51
#define SERVICE_ALL_ACCESS
Definition: winsvc.h:62
_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)
FILE * stdout
unsigned int BOOL
Definition: ntddk_ex.h:94
#define SERVICE_RUNNING
Definition: winsvc.h:24
SC_HANDLE WINAPI OpenServiceW(SC_HANDLE hSCManager, LPCWSTR lpServiceName, DWORD dwDesiredAccess)
Definition: scm.c:2103
#define PIPE_TYPE_BYTE
Definition: winbase.h:167
BOOL WINAPI CloseServiceHandle(SC_HANDLE hSCObject)
Definition: scm.c:577
smooth NULL
Definition: ftsmooth.c:416
_Check_return_ _CRTIMP _CONST_RETURN wchar_t *__cdecl wcsrchr(_In_z_ const wchar_t *_Str, _In_ wchar_t _Ch)
#define SERVICE_QUERY_STATUS
Definition: winsvc.h:55
#define SERVICE_WIN32_OWN_PROCESS
Definition: cmtypes.h:960
BOOL WINAPI StartServiceW(SC_HANDLE hService, DWORD dwNumServiceArgs, LPCWSTR *lpServiceArgVectors)
Definition: scm.c:2923
__wchar_t WCHAR
Definition: xmlstorage.h:180
#define PRINTER_ENUM_NAME
Definition: winspool.h:806
#define MAX_PATH
Definition: compat.h:26
#define PRINTER_ENUM_LOCAL
Definition: winspool.h:803
unsigned long DWORD
Definition: ntddk_ex.h:95
BOOL WINAPI QueryServiceStatus(SC_HANDLE hService, LPSERVICE_STATUS lpServiceStatus)
Definition: scm.c:2788
WINBOOL WINAPI EnumPrintersW(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
#define PIPE_ACCESS_OUTBOUND
Definition: winbase.h:166
_CRTIMP wchar_t *__cdecl wcscpy(_Out_writes_z_(_String_length_(_Source)+1) wchar_t *_Dest, _In_z_ const wchar_t *_Source)
static const WCHAR L[]
Definition: oid.c:1250
#define ERROR_PIPE_CONNECTED
Definition: winerror.h:352
SC_HANDLE WINAPI OpenSCManagerW(LPCWSTR lpMachineName, LPCWSTR lpDatabaseName, DWORD dwDesiredAccess)
Definition: scm.c:2011
static const WCHAR Cleanup[]
Definition: register.c:80
#define PIPE_READMODE_MESSAGE
Definition: winbase.h:170
#define SERVICE_NAME
Definition: wlansvc.c:18
#define PIPE_ACCESS_INBOUND
Definition: winbase.h:165
static void _RunRemoteTest(const char *szTestName)
Definition: tests.c:41
#define SC_MANAGER_ALL_ACCESS
Definition: winsvc.h:13
#define ERROR_SERVICE_DOES_NOT_EXIST
Definition: winerror.h:611
#define skip(...)
Definition: atltest.h:64
#define COMMAND_PIPE_NAME
GLfloat GLfloat p
Definition: glext.h:8902
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 SERVICE_DEMAND_START
Definition: cmtypes.h:976
BOOL WINAPI ReadFile(IN HANDLE hFile, IN LPVOID lpBuffer, IN DWORD nNumberOfBytesToRead, OUT LPDWORD lpNumberOfBytesRead OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:123
BOOL WINAPI ConnectNamedPipe(IN HANDLE hNamedPipe, IN LPOVERLAPPED lpOverlapped)
Definition: npipe.c:701
#define PIPE_READMODE_BYTE
Definition: winbase.h:169
HANDLE WINAPI FindFirstFileW(IN LPCWSTR lpFileName, OUT LPWIN32_FIND_DATAW lpFindFileData)
Definition: find.c:320
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502