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