* Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <ctype.h>
#include <string.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <AutoDeleter.h>
#include <kernel.h>
#include <thread.h>
#include "TestContext.h"
#include "TestManager.h"
#include "TestOutput.h"
#include "lock/LockTestSuite.h"
int32 api_version = B_CUR_DRIVER_API_VERSION;
static const char* sDeviceNames[] = {
"kernel_unit_tests",
NULL
};
static const char* const kUsage =
"Usage:\n"
" help\n"
" Print usage info.\n"
"\n"
" list\n"
" Print available tests.\n"
"\n"
" run [ <options> ] [ <tests> ]\n"
" Run tests. The tests to be run can be given as arguments. When no "
"tests\n"
" are specified, all tests are run.\n"
"\n"
" Options:\n"
" -p - panic(), when a test check fails.\n"
" -q - Don't run any further tests after the first test failure.\n";
static TestManager* sTestManager = NULL;
static bool
parse_command_line(char* buffer, char**& _argv, int& _argc)
{
char* start = buffer;
char* out = buffer;
bool pendingArgument = false;
int argc = 0;
while (*start != '\0') {
if (isspace(*start)) {
if (pendingArgument) {
*out = '\0';
out++;
argc++;
pendingArgument = false;
}
start++;
continue;
}
pendingArgument = true;
if (*start == '"' || *start == '\'') {
char quote = *start;
start++;
while (*start != '\0' && *start != quote) {
if (*start == '\\' && quote == '"') {
start++;
if (*start == '\0')
break;
}
*out = *start;
start++;
out++;
}
if (*start != '\0')
start++;
} else {
if (*start == '\\') {
start++;
if (start == '\0')
break;
}
*out = *start;
start++;
out++;
}
}
if (pendingArgument) {
*out = '\0';
argc++;
}
char** argv = new(std::nothrow) char*[argc + 1];
if (argv == NULL)
return false;
start = buffer;
for (int i = 0; i < argc; i++) {
argv[i] = start;
start += strlen(start) + 1;
}
argv[argc] = NULL;
_argv = argv;
_argc = argc;
return true;
}
static status_t
device_open(const char* name, uint32 openMode, void** _cookie)
{
*_cookie = NULL;
return B_OK;
}
static status_t
device_close(void* cookie)
{
return B_OK;
}
static status_t
device_free(void* cookie)
{
return B_OK;
}
static status_t
device_control(void* cookie, uint32 op, void* arg, size_t length)
{
return B_BAD_VALUE;
}
static status_t
device_read(void* cookie, off_t position, void* data, size_t* numBytes)
{
*numBytes = 0;
return B_OK;
}
static status_t
device_write(void* cookie, off_t position, const void* data, size_t* numBytes)
{
if (position != 0)
return B_BAD_VALUE;
char* buffer = (char*)malloc(*numBytes + 1);
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter bufferDeleter(buffer);
Thread* thread = thread_get_current_thread();
if ((thread->flags & THREAD_FLAGS_SYSCALL) != 0) {
if (!IS_USER_ADDRESS(data)
|| user_memcpy(buffer, data, *numBytes) != B_OK) {
return B_BAD_ADDRESS;
}
} else
memcpy(buffer, data, *numBytes);
buffer[*numBytes] = '\0';
TestOutput* output = new(std::nothrow) DebugTestOutput;
if (output == NULL)
return B_NO_MEMORY;
ObjectDeleter<TestOutput> outputDeleter(output);
char** argv;
int argc;
if (!parse_command_line(buffer, argv, argc))
return B_NO_MEMORY;
ArrayDeleter<char*> argvDeleter(argv);
if (argc == 0)
return B_BAD_VALUE;
if (strcmp(argv[0], "help") == 0) {
const char* usage = kUsage;
while (*usage != '\0') {
const char* lineEnd = strchr(usage, '\n');
if (lineEnd != NULL)
lineEnd++;
else
lineEnd = usage + strlen(usage);
output->Print("%.*s", (int)(lineEnd - usage), usage);
usage = lineEnd;
}
} else if (strcmp(argv[0], "list") == 0) {
sTestManager->ListTests(*output);
} else if (strcmp(argv[0], "run") == 0) {
TestOptions options;
int argi = 1;
while (argi < argc) {
const char* arg = argv[argi];
if (*arg == '-') {
for (arg++; *arg != '\0'; arg++) {
switch (*arg) {
case 'p':
options.panicOnFailure = true;
break;
case 'q':
options.quitAfterFailure = true;
break;
default:
output->Print("Invalid option: \"-%c\"\n", *arg);
return B_BAD_VALUE;
}
}
argi++;
} else
break;
}
GlobalTestContext globalContext(*output, options);
sTestManager->RunTests(globalContext, argv + argi, argc - argi);
} else {
output->Print("Invalid command \"%s\"!\n", argv[0]);
return B_BAD_VALUE;
}
return B_OK;
}
static device_hooks sDeviceHooks = {
device_open,
device_close,
device_free,
device_control,
device_read,
device_write
};
status_t
init_hardware(void)
{
return B_OK;
}
status_t
init_driver(void)
{
sTestManager = new(std::nothrow) TestManager;
if (sTestManager == NULL)
return B_NO_MEMORY;
sTestManager->AddTest(create_lock_test_suite());
return B_OK;
}
void
uninit_driver(void)
{
delete sTestManager;
}
const char**
publish_devices(void)
{
return sDeviceNames;
}
device_hooks*
find_device(const char* name)
{
return strcmp(name, sDeviceNames[0]) == 0 ? &sDeviceHooks : NULL;
}