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