1 /*
2 * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <ctype.h>
8 #include <string.h>
9
10 #include <Drivers.h>
11 #include <KernelExport.h>
12
13 #include <AutoDeleter.h>
14
15 #include <kernel.h>
16 #include <thread.h>
17
18 #include "TestContext.h"
19 #include "TestManager.h"
20 #include "TestOutput.h"
21
22 #include "lock/LockTestSuite.h"
23
24
25 int32 api_version = B_CUR_DRIVER_API_VERSION;
26
27 static const char* sDeviceNames[] = {
28 "kernel_unit_tests",
29 NULL
30 };
31
32 static const char* const kUsage =
33 "Usage:\n"
34 " help\n"
35 " Print usage info.\n"
36 "\n"
37 " list\n"
38 " Print available tests.\n"
39 "\n"
40 " run [ <options> ] [ <tests> ]\n"
41 " Run tests. The tests to be run can be given as arguments. When no "
42 "tests\n"
43 " are specified, all tests are run.\n"
44 "\n"
45 " Options:\n"
46 " -p - panic(), when a test check fails.\n"
47 " -q - Don't run any further tests after the first test failure.\n";
48
49 static TestManager* sTestManager = NULL;
50
51
52 // #pragma mark -
53
54
55 static bool
parse_command_line(char * buffer,char ** & _argv,int & _argc)56 parse_command_line(char* buffer, char**& _argv, int& _argc)
57 {
58 // Process the argument string. We split at whitespace, heeding quotes and
59 // escaped characters. The processed arguments are written to the given
60 // buffer, separated by single null chars.
61 char* start = buffer;
62 char* out = buffer;
63 bool pendingArgument = false;
64 int argc = 0;
65 while (*start != '\0') {
66 // ignore whitespace
67 if (isspace(*start)) {
68 if (pendingArgument) {
69 *out = '\0';
70 out++;
71 argc++;
72 pendingArgument = false;
73 }
74 start++;
75 continue;
76 }
77
78 pendingArgument = true;
79
80 if (*start == '"' || *start == '\'') {
81 // quoted text -- continue until closing quote
82 char quote = *start;
83 start++;
84 while (*start != '\0' && *start != quote) {
85 if (*start == '\\' && quote == '"') {
86 start++;
87 if (*start == '\0')
88 break;
89 }
90 *out = *start;
91 start++;
92 out++;
93 }
94
95 if (*start != '\0')
96 start++;
97 } else {
98 // unquoted text
99 if (*start == '\\') {
100 // escaped char
101 start++;
102 if (start == '\0')
103 break;
104 }
105
106 *out = *start;
107 start++;
108 out++;
109 }
110 }
111
112 if (pendingArgument) {
113 *out = '\0';
114 argc++;
115 }
116
117 // allocate argument vector
118 char** argv = new(std::nothrow) char*[argc + 1];
119 if (argv == NULL)
120 return false;
121
122 // fill vector
123 start = buffer;
124 for (int i = 0; i < argc; i++) {
125 argv[i] = start;
126 start += strlen(start) + 1;
127 }
128 argv[argc] = NULL;
129
130 _argv = argv;
131 _argc = argc;
132 return true;
133 }
134
135
136 // #pragma mark - device hooks
137
138
139 static status_t
device_open(const char * name,uint32 openMode,void ** _cookie)140 device_open(const char* name, uint32 openMode, void** _cookie)
141 {
142 *_cookie = NULL;
143 return B_OK;
144 }
145
146
147 static status_t
device_close(void * cookie)148 device_close(void* cookie)
149 {
150 return B_OK;
151 }
152
153
154 static status_t
device_free(void * cookie)155 device_free(void* cookie)
156 {
157 return B_OK;
158 }
159
160
161 static status_t
device_control(void * cookie,uint32 op,void * arg,size_t length)162 device_control(void* cookie, uint32 op, void* arg, size_t length)
163 {
164 return B_BAD_VALUE;
165 }
166
167
168 static status_t
device_read(void * cookie,off_t position,void * data,size_t * numBytes)169 device_read(void* cookie, off_t position, void* data, size_t* numBytes)
170 {
171 *numBytes = 0;
172 return B_OK;
173 }
174
175
176 static status_t
device_write(void * cookie,off_t position,const void * data,size_t * numBytes)177 device_write(void* cookie, off_t position, const void* data, size_t* numBytes)
178 {
179 if (position != 0)
180 return B_BAD_VALUE;
181
182 // copy data to stack buffer
183 char* buffer = (char*)malloc(*numBytes + 1);
184 if (buffer == NULL)
185 return B_NO_MEMORY;
186 MemoryDeleter bufferDeleter(buffer);
187
188 Thread* thread = thread_get_current_thread();
189 if ((thread->flags & THREAD_FLAGS_SYSCALL) != 0) {
190 if (!IS_USER_ADDRESS(data)
191 || user_memcpy(buffer, data, *numBytes) != B_OK) {
192 return B_BAD_ADDRESS;
193 }
194 } else
195 memcpy(buffer, data, *numBytes);
196
197 buffer[*numBytes] = '\0';
198
199 // create an output
200 TestOutput* output = new(std::nothrow) DebugTestOutput;
201 if (output == NULL)
202 return B_NO_MEMORY;
203 ObjectDeleter<TestOutput> outputDeleter(output);
204
205 // parse arguments
206 char** argv;
207 int argc;
208 if (!parse_command_line(buffer, argv, argc))
209 return B_NO_MEMORY;
210 ArrayDeleter<char*> argvDeleter(argv);
211
212 if (argc == 0)
213 return B_BAD_VALUE;
214
215 // execute command
216 if (strcmp(argv[0], "help") == 0) {
217 // print usage -- print individual lines to avoid dprintf() buffer
218 // overflows
219 const char* usage = kUsage;
220 while (*usage != '\0') {
221 const char* lineEnd = strchr(usage, '\n');
222 if (lineEnd != NULL)
223 lineEnd++;
224 else
225 lineEnd = usage + strlen(usage);
226
227 output->Print("%.*s", (int)(lineEnd - usage), usage);
228 usage = lineEnd;
229 }
230 } else if (strcmp(argv[0], "list") == 0) {
231 sTestManager->ListTests(*output);
232 } else if (strcmp(argv[0], "run") == 0) {
233 // parse options
234 TestOptions options;
235 int argi = 1;
236 while (argi < argc) {
237 const char* arg = argv[argi];
238 if (*arg == '-') {
239 for (arg++; *arg != '\0'; arg++) {
240 switch (*arg) {
241 case 'p':
242 options.panicOnFailure = true;
243 break;
244 case 'q':
245 options.quitAfterFailure = true;
246 break;
247 default:
248 output->Print("Invalid option: \"-%c\"\n", *arg);
249 return B_BAD_VALUE;
250 }
251 }
252
253 argi++;
254 } else
255 break;
256 }
257
258 GlobalTestContext globalContext(*output, options);
259 sTestManager->RunTests(globalContext, argv + argi, argc - argi);
260 } else {
261 output->Print("Invalid command \"%s\"!\n", argv[0]);
262 return B_BAD_VALUE;
263 }
264
265 return B_OK;
266 }
267
268
269 // #pragma mark - driver interface
270
271
272 static device_hooks sDeviceHooks = {
273 device_open,
274 device_close,
275 device_free,
276 device_control,
277 device_read,
278 device_write
279 };
280
281
282 status_t
init_hardware(void)283 init_hardware(void)
284 {
285 return B_OK;
286 }
287
288
289 status_t
init_driver(void)290 init_driver(void)
291 {
292 sTestManager = new(std::nothrow) TestManager;
293 if (sTestManager == NULL)
294 return B_NO_MEMORY;
295
296 // register test suites
297 sTestManager->AddTest(create_lock_test_suite());
298
299 return B_OK;
300 }
301
302
303 void
uninit_driver(void)304 uninit_driver(void)
305 {
306 delete sTestManager;
307 }
308
309
310 const char**
publish_devices(void)311 publish_devices(void)
312 {
313 return sDeviceNames;
314 }
315
316
317 device_hooks*
find_device(const char * name)318 find_device(const char* name)
319 {
320 return strcmp(name, sDeviceNames[0]) == 0 ? &sDeviceHooks : NULL;
321 }
322