1c2c66affSColin Finck /*
2c2c66affSColin Finck * PROJECT: ReactOS Local Spooler API Tests
3c2c66affSColin Finck * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4c2c66affSColin Finck * PURPOSE: Test list
5c2c66affSColin Finck * COPYRIGHT: Copyright 2015-2016 Colin Finck (colin@reactos.org)
6c2c66affSColin Finck */
7c2c66affSColin Finck
8c2c66affSColin Finck /*
9c2c66affSColin Finck * The original localspl.dll from Windows Server 2003 is not easily testable.
10c2c66affSColin Finck * It relies on a proper initialization inside spoolsv.exe, so we can't just load it in an API-Test as usual.
11c2c66affSColin Finck * See https://www.reactos.org/pipermail/ros-dev/2015-June/017395.html for more information.
12c2c66affSColin Finck *
13c2c66affSColin Finck * To make testing possible anyway, this program basically does four things:
14c2c66affSColin Finck * - Injecting our testing code into spoolsv.exe.
15c2c66affSColin Finck * - Registering and running us as a service in the SYSTEM security context like spoolsv.exe, so that injection is possible at all.
16c2c66affSColin Finck * - Sending the test name and receiving the console output over named pipes.
17c2c66affSColin Finck * - Redirecting the received console output to stdout again, so it looks and feels like a standard API-Test.
18c2c66affSColin Finck *
19c2c66affSColin Finck * To simplify debugging of the injected code, it is entirely separated into a DLL file localspl_apitest.dll.
20c2c66affSColin Finck * What we actually inject is a LoadLibraryW call, so that the DLL is loaded gracefully without any hacks.
21c2c66affSColin Finck * Therefore, you can just attach your debugger to the spoolsv.exe process and set breakpoints on the localspl_apitest.dll code.
22c2c66affSColin Finck */
23c2c66affSColin Finck
24c2c66affSColin Finck #include <apitest.h>
25c2c66affSColin Finck
26c2c66affSColin Finck #define WIN32_NO_STATUS
27c2c66affSColin Finck #include <stdio.h>
28c2c66affSColin Finck #include <stdlib.h>
29c2c66affSColin Finck #include <windef.h>
30c2c66affSColin Finck #include <winbase.h>
31c2c66affSColin Finck #include <wingdi.h>
32c2c66affSColin Finck #include <winreg.h>
33c2c66affSColin Finck #include <winsvc.h>
34c2c66affSColin Finck #include <winspool.h>
35c2c66affSColin Finck #include <winsplp.h>
36c2c66affSColin Finck
37c2c66affSColin Finck #include "localspl_apitest.h"
38c2c66affSColin Finck
39c2c66affSColin Finck
40c2c66affSColin Finck static void
_RunRemoteTest(const char * szTestName)41c2c66affSColin Finck _RunRemoteTest(const char* szTestName)
42c2c66affSColin Finck {
43c2c66affSColin Finck BOOL bSuccessful = FALSE;
44c2c66affSColin Finck char szBuffer[1024];
45c2c66affSColin Finck DWORD cbRead;
46c2c66affSColin Finck DWORD cbWritten;
47c2c66affSColin Finck HANDLE hCommandPipe = INVALID_HANDLE_VALUE;
48c2c66affSColin Finck HANDLE hFind = INVALID_HANDLE_VALUE;
49c2c66affSColin Finck HANDLE hOutputPipe = INVALID_HANDLE_VALUE;
50c2c66affSColin Finck PWSTR p;
51c2c66affSColin Finck SC_HANDLE hSC = NULL;
52c2c66affSColin Finck SC_HANDLE hService = NULL;
53c2c66affSColin Finck SERVICE_STATUS ServiceStatus;
54c2c66affSColin Finck WCHAR wszFilePath[MAX_PATH + 20];
55c2c66affSColin Finck WIN32_FIND_DATAW fd;
56c2c66affSColin Finck
57c2c66affSColin Finck // Do a dummy EnumPrintersW call.
58c2c66affSColin Finck // This guarantees that the Spooler Service has actually loaded localspl.dll, which is a requirement for our injected DLL to work properly.
59c2c66affSColin Finck EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbRead, &cbWritten);
60c2c66affSColin Finck
61c2c66affSColin Finck // Get the full path to our EXE file.
62c2c66affSColin Finck if (!GetModuleFileNameW(NULL, wszFilePath, MAX_PATH))
63c2c66affSColin Finck {
64c2c66affSColin Finck skip("GetModuleFileNameW failed with error %lu!\n", GetLastError());
65c2c66affSColin Finck goto Cleanup;
66c2c66affSColin Finck }
67c2c66affSColin Finck
68c2c66affSColin Finck // Replace the extension.
69c2c66affSColin Finck p = wcsrchr(wszFilePath, L'.');
70c2c66affSColin Finck if (!p)
71c2c66affSColin Finck {
72c2c66affSColin Finck skip("File path has no file extension: %S\n", wszFilePath);
73c2c66affSColin Finck goto Cleanup;
74c2c66affSColin Finck }
75c2c66affSColin Finck
76c2c66affSColin Finck wcscpy(p, L".dll");
77c2c66affSColin Finck
78c2c66affSColin Finck // Check if the corresponding DLL file exists.
79c2c66affSColin Finck hFind = FindFirstFileW(wszFilePath, &fd);
80c2c66affSColin Finck if (hFind == INVALID_HANDLE_VALUE)
81c2c66affSColin Finck {
82c2c66affSColin Finck skip("My DLL file \"%S\" does not exist!\n", wszFilePath);
83c2c66affSColin Finck goto Cleanup;
84c2c66affSColin Finck }
85c2c66affSColin Finck
86c2c66affSColin Finck // Change the extension back to .exe and add the parameters.
87c2c66affSColin Finck wcscpy(p, L".exe service dummy");
88c2c66affSColin Finck
89c2c66affSColin Finck // Open a handle to the service manager.
90c2c66affSColin Finck hSC = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
91c2c66affSColin Finck if (!hSC)
92c2c66affSColin Finck {
93c2c66affSColin Finck skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
94c2c66affSColin Finck goto Cleanup;
95c2c66affSColin Finck }
96c2c66affSColin Finck
97c2c66affSColin Finck // Ensure that the spooler service is running.
98c2c66affSColin Finck hService = OpenServiceW(hSC, L"spooler", SERVICE_QUERY_STATUS);
99c2c66affSColin Finck if (!hService)
100c2c66affSColin Finck {
101c2c66affSColin Finck skip("OpenServiceW failed for the spooler service with error %lu!\n", GetLastError());
102c2c66affSColin Finck goto Cleanup;
103c2c66affSColin Finck }
104c2c66affSColin Finck
105c2c66affSColin Finck if (!QueryServiceStatus(hService, &ServiceStatus))
106c2c66affSColin Finck {
107c2c66affSColin Finck skip("QueryServiceStatus failed for the spooler service with error %lu!\n", GetLastError());
108c2c66affSColin Finck goto Cleanup;
109c2c66affSColin Finck }
110c2c66affSColin Finck
111c2c66affSColin Finck if (ServiceStatus.dwCurrentState != SERVICE_RUNNING)
112c2c66affSColin Finck {
113c2c66affSColin Finck skip("Spooler Service is not running!\n");
114c2c66affSColin Finck goto Cleanup;
115c2c66affSColin Finck }
116c2c66affSColin Finck
117c2c66affSColin Finck CloseServiceHandle(hService);
118c2c66affSColin Finck
119c2c66affSColin Finck // Try to open the service if we've created it in a previous run.
120c2c66affSColin Finck hService = OpenServiceW(hSC, SERVICE_NAME, SERVICE_ALL_ACCESS);
121c2c66affSColin Finck if (!hService)
122c2c66affSColin Finck {
123c2c66affSColin Finck if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
124c2c66affSColin Finck {
125c2c66affSColin Finck // Create the service.
126c2c66affSColin Finck hService = CreateServiceW(hSC, SERVICE_NAME, NULL, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, wszFilePath, NULL, NULL, NULL, NULL, NULL);
127c2c66affSColin Finck if (!hService)
128c2c66affSColin Finck {
129c2c66affSColin Finck skip("CreateServiceW failed with error %lu!\n", GetLastError());
130c2c66affSColin Finck goto Cleanup;
131c2c66affSColin Finck }
132c2c66affSColin Finck }
133c2c66affSColin Finck else
134c2c66affSColin Finck {
135c2c66affSColin Finck skip("OpenServiceW failed with error %lu!\n", GetLastError());
136c2c66affSColin Finck goto Cleanup;
137c2c66affSColin Finck }
138c2c66affSColin Finck }
139c2c66affSColin Finck
140c2c66affSColin Finck // Create pipes for the communication with the injected DLL.
141c2c66affSColin Finck hCommandPipe = CreateNamedPipeW(COMMAND_PIPE_NAME, PIPE_ACCESS_OUTBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 1024, 1024, 10000, NULL);
142c2c66affSColin Finck if (hCommandPipe == INVALID_HANDLE_VALUE)
143c2c66affSColin Finck {
144c2c66affSColin Finck skip("CreateNamedPipeW failed for the command pipe with error %lu!\n", GetLastError());
145c2c66affSColin Finck goto Cleanup;
146c2c66affSColin Finck }
147c2c66affSColin Finck
148c2c66affSColin Finck hOutputPipe = CreateNamedPipeW(OUTPUT_PIPE_NAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 10000, NULL);
149c2c66affSColin Finck if (hOutputPipe == INVALID_HANDLE_VALUE)
150c2c66affSColin Finck {
151c2c66affSColin Finck skip("CreateNamedPipeW failed for the output pipe with error %lu!\n", GetLastError());
152c2c66affSColin Finck goto Cleanup;
153c2c66affSColin Finck }
154c2c66affSColin Finck
155c2c66affSColin Finck // Start the service with "service" and a dummy parameter (to distinguish it from a call by rosautotest to localspl_apitest:service)
156c2c66affSColin Finck if (!StartServiceW(hService, 0, NULL))
157c2c66affSColin Finck {
158c2c66affSColin Finck skip("StartServiceW failed with error %lu!\n", GetLastError());
159c2c66affSColin Finck goto Cleanup;
160c2c66affSColin Finck }
161c2c66affSColin Finck
162c2c66affSColin Finck // Wait till it has injected the DLL and the DLL expects its test name.
163c2c66affSColin Finck if (!ConnectNamedPipe(hCommandPipe, NULL) && GetLastError() != ERROR_PIPE_CONNECTED)
164c2c66affSColin Finck {
165c2c66affSColin Finck skip("ConnectNamedPipe failed for the command pipe with error %lu!\n", GetLastError());
166c2c66affSColin Finck goto Cleanup;
167c2c66affSColin Finck }
168c2c66affSColin Finck
169c2c66affSColin Finck // Send the test name.
17042d2d5ecSTimo Kreuzer if (!WriteFile(hCommandPipe, szTestName, lstrlenA(szTestName) + sizeof(char), &cbWritten, NULL))
171c2c66affSColin Finck {
172c2c66affSColin Finck skip("WriteFile failed with error %lu!\n", GetLastError());
173c2c66affSColin Finck goto Cleanup;
174c2c66affSColin Finck }
175c2c66affSColin Finck
176c2c66affSColin Finck // Now wait for the DLL to connect to the output pipe.
177c2c66affSColin Finck if (!ConnectNamedPipe(hOutputPipe, NULL))
178c2c66affSColin Finck {
179c2c66affSColin Finck skip("ConnectNamedPipe failed for the output pipe with error %lu!\n", GetLastError());
180c2c66affSColin Finck goto Cleanup;
181c2c66affSColin Finck }
182c2c66affSColin Finck
183c2c66affSColin Finck // Get all testing messages from the pipe and output them on stdout.
184c2c66affSColin Finck while (ReadFile(hOutputPipe, szBuffer, sizeof(szBuffer), &cbRead, NULL) && cbRead)
185c2c66affSColin Finck fwrite(szBuffer, sizeof(char), cbRead, stdout);
186c2c66affSColin Finck
187c2c66affSColin Finck bSuccessful = TRUE;
188c2c66affSColin Finck
189c2c66affSColin Finck Cleanup:
190c2c66affSColin Finck if (hCommandPipe != INVALID_HANDLE_VALUE)
191c2c66affSColin Finck CloseHandle(hCommandPipe);
192c2c66affSColin Finck
193c2c66affSColin Finck if (hOutputPipe != INVALID_HANDLE_VALUE)
194c2c66affSColin Finck CloseHandle(hOutputPipe);
195c2c66affSColin Finck
196c2c66affSColin Finck if (hFind != INVALID_HANDLE_VALUE)
197c2c66affSColin Finck FindClose(hFind);
198c2c66affSColin Finck
199c2c66affSColin Finck if (hService)
200c2c66affSColin Finck CloseServiceHandle(hService);
201c2c66affSColin Finck
202c2c66affSColin Finck if (hSC)
203c2c66affSColin Finck CloseServiceHandle(hSC);
204c2c66affSColin Finck
205c2c66affSColin Finck // If we successfully received test output through the named pipe, we have also output a summary line already.
206c2c66affSColin Finck // Prevent the testing framework from outputting another "0 tests executed" line in this case.
207c2c66affSColin Finck if (bSuccessful)
208c2c66affSColin Finck ExitProcess(0);
209c2c66affSColin Finck }
210c2c66affSColin Finck
START_TEST(fpEnumPrinters)211c2c66affSColin Finck START_TEST(fpEnumPrinters)
212c2c66affSColin Finck {
213*8a437ac5STimo Kreuzer #if defined(_M_AMD64)
214*8a437ac5STimo Kreuzer if (!winetest_interactive)
215*8a437ac5STimo Kreuzer {
216*8a437ac5STimo Kreuzer 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*8a437ac5STimo Kreuzer return;
218*8a437ac5STimo Kreuzer }
219*8a437ac5STimo Kreuzer #endif
220*8a437ac5STimo Kreuzer
221c2c66affSColin Finck _RunRemoteTest("fpEnumPrinters");
222c2c66affSColin Finck }
223c2c66affSColin Finck
START_TEST(fpGetPrintProcessorDirectory)224c2c66affSColin Finck START_TEST(fpGetPrintProcessorDirectory)
225c2c66affSColin Finck {
226*8a437ac5STimo Kreuzer #if defined(_M_AMD64)
227*8a437ac5STimo Kreuzer if (!winetest_interactive)
228*8a437ac5STimo Kreuzer {
229*8a437ac5STimo Kreuzer 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*8a437ac5STimo Kreuzer return;
231*8a437ac5STimo Kreuzer }
232*8a437ac5STimo Kreuzer #endif
233*8a437ac5STimo Kreuzer
234c2c66affSColin Finck _RunRemoteTest("fpGetPrintProcessorDirectory");
235c2c66affSColin Finck }
2363b93ba0fSColin Finck
START_TEST(fpSetJob)2373b93ba0fSColin Finck START_TEST(fpSetJob)
2383b93ba0fSColin Finck {
239*8a437ac5STimo Kreuzer #if defined(_M_AMD64)
240*8a437ac5STimo Kreuzer if (!winetest_interactive)
241*8a437ac5STimo Kreuzer {
242*8a437ac5STimo Kreuzer 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*8a437ac5STimo Kreuzer return;
244*8a437ac5STimo Kreuzer }
245*8a437ac5STimo Kreuzer #endif
246*8a437ac5STimo Kreuzer
2473b93ba0fSColin Finck _RunRemoteTest("fpSetJob");
2483b93ba0fSColin Finck }
249