6ab770948eda108cfcee258b5873b2beab6ee0b0
[reactos.git] / kmtests / kmtest / kmtest.c
1 /*
2 * PROJECT: ReactOS kernel-mode tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Kernel-Mode Test Suite Loader Application
5 * PROGRAMMER: Thomas Faber <thfabba@gmx.de>
6 */
7
8 #define UNICODE
9 #define WIN32_LEAN_AND_MEAN
10 #include <windows.h>
11 #include <strsafe.h>
12 #include <winioctl.h>
13
14 #include <assert.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17
18 #include "kmtest.h"
19 #include <kmt_public.h>
20 #define KMT_DEFINE_TEST_FUNCTIONS
21 #include <kmt_test.h>
22
23 #define SERVICE_NAME L"Kmtest"
24 #define SERVICE_PATH L"kmtest_drv.sys"
25 #define SERVICE_DESCRIPTION L"ReactOS Kernel-Mode Test Suite Driver"
26
27 #define RESULTBUFFER_SIZE (1024 * 1024)
28
29 typedef enum
30 {
31 KMT_DO_NOTHING,
32 KMT_LIST_TESTS,
33 KMT_LIST_ALL_TESTS,
34 KMT_RUN_TEST,
35 } KMT_OPERATION;
36
37 HANDLE KmtestHandle;
38 SC_HANDLE KmtestServiceHandle;
39 PCSTR ErrorFileAndLine = "No error";
40
41 static void OutputError(IN DWORD Error);
42 static DWORD ListTests(IN BOOLEAN IncludeHidden);
43 static PKMT_TESTFUNC FindTest(IN PCSTR TestName);
44 static DWORD OutputResult(IN PCSTR TestName);
45 static DWORD RunTest(IN PCSTR TestName);
46 int __cdecl main(int ArgCount, char **Arguments);
47
48 /**
49 * @name OutputError
50 *
51 * Output an error message to the console.
52 *
53 * @param Error
54 * Win32 error code
55 */
56 static
57 void
58 OutputError(
59 IN DWORD Error)
60 {
61 PSTR Message;
62 if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
63 NULL, Error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&Message, 0, NULL))
64 {
65 fprintf(stderr, "%s: Could not retrieve error message (error 0x%08lx). Original error: 0x%08lx\n",
66 ErrorFileAndLine, GetLastError(), Error);
67 return;
68 }
69
70 fprintf(stderr, "%s: error 0x%08lx: %s\n", ErrorFileAndLine, Error, Message);
71
72 LocalFree(Message);
73 }
74
75 /**
76 * @name CompareTestNames
77 *
78 * strcmp that skips a leading '-' on either string if present
79 *
80 * @param Str1
81 * @param Str2
82 * @return see strcmp
83 */
84 static
85 INT
86 CompareTestNames(
87 IN PCSTR Str1,
88 IN PCSTR Str2)
89 {
90 if (*Str1 == '-')
91 ++Str1;
92 if (*Str2 == '-')
93 ++Str2;
94 while (*Str1 && *Str1 == *Str2)
95 {
96 ++Str1;
97 ++Str2;
98 }
99 return *Str1 - *Str2;
100 }
101
102 /**
103 * @name ListTests
104 *
105 * Output the list of tests to the console.
106 * The list will comprise tests as listed by the driver
107 * in addition to user-mode tests in TestList.
108 *
109 * @param IncludeHidden
110 * TRUE to include "hidden" tests prefixed with a '-'
111 *
112 * @return Win32 error code
113 */
114 static
115 DWORD
116 ListTests(
117 IN BOOLEAN IncludeHidden)
118 {
119 DWORD Error = ERROR_SUCCESS;
120 CHAR Buffer[1024];
121 DWORD BytesRead;
122 PCSTR TestName = Buffer;
123 PCKMT_TEST TestEntry = TestList;
124 PCSTR NextTestName;
125
126 puts("Valid test names:");
127
128 // get test list from driver
129 if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_GET_TESTS, NULL, 0, Buffer, sizeof Buffer, &BytesRead, NULL))
130 error_goto(Error, cleanup);
131
132 // output test list plus user-mode tests
133 while (TestEntry->TestName || *TestName)
134 {
135 if (!TestEntry->TestName)
136 {
137 NextTestName = TestName;
138 TestName += strlen(TestName) + 1;
139 }
140 else if (!*TestName)
141 {
142 NextTestName = TestEntry->TestName;
143 ++TestEntry;
144 }
145 else
146 {
147 INT Result = CompareTestNames(TestEntry->TestName, TestName);
148
149 if (Result == 0)
150 {
151 NextTestName = TestEntry->TestName;
152 TestName += strlen(TestName) + 1;
153 ++TestEntry;
154 }
155 else if (Result < 0)
156 {
157 NextTestName = TestEntry->TestName;
158 ++TestEntry;
159 }
160 else
161 {
162 NextTestName = TestName;
163 TestName += strlen(TestName) + 1;
164 }
165 }
166
167 if (IncludeHidden && NextTestName[0] == '-')
168 ++NextTestName;
169
170 if (NextTestName[0] != '-')
171 printf(" %s\n", NextTestName);
172 }
173
174 cleanup:
175 return Error;
176 }
177
178 /**
179 * @name FindTest
180 *
181 * Find a test in TestList by name.
182 *
183 * @param TestName
184 * Name of the test to look for. Case sensitive
185 *
186 * @return pointer to test function, or NULL if not found
187 */
188 static
189 PKMT_TESTFUNC
190 FindTest(
191 IN PCSTR TestName)
192 {
193 PCKMT_TEST TestEntry = TestList;
194
195 for (TestEntry = TestList; TestEntry->TestName; ++TestEntry)
196 {
197 PCSTR TestEntryName = TestEntry->TestName;
198
199 // skip leading '-' if present
200 if (*TestEntryName == '-')
201 ++TestEntryName;
202
203 if (!lstrcmpA(TestEntryName, TestName))
204 break;
205 }
206
207 return TestEntry->TestFunction;
208 }
209
210 /**
211 * @name OutputResult
212 *
213 * Output the test results in ResultBuffer to the console.
214 *
215 * @param TestName
216 * Name of the test whose result is to be printed
217 *
218 * @return Win32 error code
219 */
220 static
221 DWORD
222 OutputResult(
223 IN PCSTR TestName)
224 {
225 DWORD Error = ERROR_SUCCESS;
226 DWORD BytesWritten;
227
228 KmtFinishTest(TestName);
229
230 if (!WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), ResultBuffer->LogBuffer, ResultBuffer->LogBufferLength, &BytesWritten, NULL))
231 error(Error);
232
233 return Error;
234 }
235
236 /**
237 * @name RunTest
238 *
239 * Run the named test and output its results.
240 *
241 * @param TestName
242 * Name of the test to run. Case sensitive
243 *
244 * @return Win32 error code
245 */
246 static
247 DWORD
248 RunTest(
249 IN PCSTR TestName)
250 {
251 DWORD Error = ERROR_SUCCESS;
252 PKMT_TESTFUNC TestFunction;
253 DWORD BytesRead;
254
255 assert(TestName != NULL);
256
257 if (!ResultBuffer)
258 {
259 ResultBuffer = KmtAllocateResultBuffer(RESULTBUFFER_SIZE);
260 if (!ResultBuffer)
261 error_goto(Error, cleanup);
262 if (!DeviceIoControl(KmtestHandle, IOCTL_KMTEST_SET_RESULTBUFFER, ResultBuffer, RESULTBUFFER_SIZE, NULL, 0, &BytesRead, NULL))
263 error_goto(Error, cleanup);
264 }
265
266 // check test list
267 TestFunction = FindTest(TestName);
268
269 if (TestFunction)
270 {
271 TestFunction();
272 goto cleanup;
273 }
274
275 // not found in user-mode test list, call driver
276 Error = KmtRunKernelTest(TestName);
277
278 cleanup:
279 if (!Error)
280 Error = OutputResult(TestName);
281
282 return Error;
283 }
284
285 /**
286 * @name main
287 *
288 * Program entry point
289 *
290 * @param ArgCount
291 * @param Arguments
292 *
293 * @return EXIT_SUCCESS on success, EXIT_FAILURE on failure
294 */
295 int
296 main(
297 int ArgCount,
298 char **Arguments)
299 {
300 INT Status = EXIT_SUCCESS;
301 DWORD Error = ERROR_SUCCESS;
302 PCSTR AppName = "kmtest.exe";
303 PCSTR TestName = NULL;
304 KMT_OPERATION Operation = KMT_DO_NOTHING;
305 BOOLEAN ShowHidden = FALSE;
306
307 Error = KmtServiceInit();
308 if (Error)
309 goto cleanup;
310
311 if (ArgCount >= 1)
312 AppName = Arguments[0];
313
314 if (ArgCount <= 1)
315 {
316 printf("Usage: %s <test_name> - run the specified test (creates/starts the driver(s) as appropriate)\n", AppName);
317 printf(" %s --list - list available tests\n", AppName);
318 printf(" %s --list-all - list available tests, including hidden\n", AppName);
319 printf(" %s <create|delete|start|stop> - manage the kmtest driver\n\n", AppName);
320 Operation = KMT_LIST_TESTS;
321 }
322 else
323 {
324 TestName = Arguments[1];
325 if (!lstrcmpA(TestName, "create"))
326 Error = KmtCreateService(SERVICE_NAME, SERVICE_PATH, SERVICE_DESCRIPTION, &KmtestServiceHandle);
327 else if (!lstrcmpA(TestName, "delete"))
328 Error = KmtDeleteService(SERVICE_NAME, &KmtestServiceHandle);
329 else if (!lstrcmpA(TestName, "start"))
330 Error = KmtStartService(SERVICE_NAME, &KmtestServiceHandle);
331 else if (!lstrcmpA(TestName, "stop"))
332 Error = KmtStopService(SERVICE_NAME, &KmtestServiceHandle);
333
334 else if (!lstrcmpA(TestName, "--list"))
335 Operation = KMT_LIST_TESTS;
336 else if (!lstrcmpA(TestName, "--list-all"))
337 Operation = KMT_LIST_ALL_TESTS;
338 else
339 Operation = KMT_RUN_TEST;
340 }
341
342 if (Operation)
343 {
344 Error = KmtCreateAndStartService(SERVICE_NAME, SERVICE_PATH, SERVICE_DESCRIPTION, &KmtestServiceHandle, FALSE);
345 if (Error)
346 goto cleanup;
347
348 KmtestHandle = CreateFile(KMTEST_DEVICE_PATH, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
349 if (KmtestHandle == INVALID_HANDLE_VALUE)
350 error_goto(Error, cleanup);
351
352 switch (Operation)
353 {
354 case KMT_LIST_ALL_TESTS:
355 ShowHidden = TRUE;
356 /* fall through */
357 case KMT_LIST_TESTS:
358 Error = ListTests(ShowHidden);
359 break;
360 case KMT_RUN_TEST:
361 Error = RunTest(TestName);
362 break;
363 default:
364 assert(FALSE);
365 }
366 }
367
368 cleanup:
369 if (KmtestHandle)
370 CloseHandle(KmtestHandle);
371
372 if (ResultBuffer)
373 KmtFreeResultBuffer(ResultBuffer);
374
375 KmtCloseService(&KmtestServiceHandle);
376
377 if (Error)
378 KmtServiceCleanup(TRUE);
379 else
380 Error = KmtServiceCleanup(FALSE);
381
382 if (Error)
383 {
384 OutputError(Error);
385
386 Status = EXIT_FAILURE;
387 }
388
389 return Status;
390 }