xref: /haiku/src/tests/system/kernel/unit/kernel_unit_tests.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
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
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
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
148 device_close(void* cookie)
149 {
150 	return B_OK;
151 }
152 
153 
154 static status_t
155 device_free(void* cookie)
156 {
157 	return B_OK;
158 }
159 
160 
161 static status_t
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
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
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
283 init_hardware(void)
284 {
285 	return B_OK;
286 }
287 
288 
289 status_t
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
304 uninit_driver(void)
305 {
306 	delete sTestManager;
307 }
308 
309 
310 const char**
311 publish_devices(void)
312 {
313 	return sDeviceNames;
314 }
315 
316 
317 device_hooks*
318 find_device(const char* name)
319 {
320 	return strcmp(name, sDeviceNames[0]) == 0 ? &sDeviceHooks : NULL;
321 }
322