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