reactos/rostests/kmtests/include/kmt_test.h
Thomas Faber e6c56afe8c [KMTESTS]
- Add KmtGetSystemRoutineAddress and use it to avoid importing functions that are not available in some versions of Windows. This allows running kmtests on Windows XP SP3 (and probably Server 2003 SP0/SP2). Note that failures on XP are still not valid bugs, this is purely for convenience!  Dedicated to Jérôme.
ROSTESTS-150

svn path=/trunk/; revision=66281
2015-02-15 10:23:21 +00:00

465 lines
17 KiB
C

/*
* PROJECT: ReactOS kernel-mode tests
* LICENSE: GPLv2+ - See COPYING in the top level directory
* PURPOSE: Kernel-Mode Test Suite test framework declarations
* PROGRAMMER: Thomas Faber <thomas.faber@reactos.org>
*/
/* Inspired by Wine C unit tests, Copyright (C) 2002 Alexandre Julliard
* Inspired by ReactOS kernel-mode regression tests,
* Copyright (C) Aleksey Bragin, Filip Navara
*/
#ifndef _KMTEST_TEST_H_
#define _KMTEST_TEST_H_
#include <kmt_platform.h>
typedef VOID KMT_TESTFUNC(VOID);
typedef KMT_TESTFUNC *PKMT_TESTFUNC;
typedef struct
{
const char *TestName;
KMT_TESTFUNC *TestFunction;
} KMT_TEST, *PKMT_TEST;
typedef const KMT_TEST CKMT_TEST, *PCKMT_TEST;
extern const KMT_TEST TestList[];
typedef struct
{
volatile LONG Successes;
volatile LONG Failures;
volatile LONG Skipped;
volatile LONG LogBufferLength;
LONG LogBufferMaxLength;
CHAR LogBuffer[ANYSIZE_ARRAY];
} KMT_RESULTBUFFER, *PKMT_RESULTBUFFER;
#ifndef KMT_STANDALONE_DRIVER
/* usermode call-back mechanism */
/* list of supported operations */
typedef enum _KMT_CALLBACK_INFORMATION_CLASS
{
QueryVirtualMemory
} KMT_CALLBACK_INFORMATION_CLASS, *PKMT_CALLBACK_INFORMATION_CLASS;
/* TODO: "response" is a little generic */
typedef union _KMT_RESPONSE
{
MEMORY_BASIC_INFORMATION MemInfo;
} KMT_RESPONSE, *PKMT_RESPONSE;
/* this struct is sent from driver to usermode */
typedef struct _KMT_CALLBACK_REQUEST_PACKET
{
ULONG RequestId;
KMT_CALLBACK_INFORMATION_CLASS OperationClass;
PVOID Parameters;
} KMT_CALLBACK_REQUEST_PACKET, *PKMT_CALLBACK_REQUEST_PACKET;
PKMT_RESPONSE KmtUserModeCallback(KMT_CALLBACK_INFORMATION_CLASS Operation, PVOID Parameters);
VOID KmtFreeCallbackResponse(PKMT_RESPONSE Response);
//macro to simplify using the mechanism
#define Test_NtQueryVirtualMemory(BaseAddress, Size, AllocationType, ProtectionType) \
do { \
PKMT_RESPONSE NtQueryTest = KmtUserModeCallback(QueryVirtualMemory, BaseAddress); \
if (NtQueryTest != NULL) \
{ \
ok_eq_hex(NtQueryTest->MemInfo.Protect, ProtectionType); \
ok_eq_hex(NtQueryTest->MemInfo.State, AllocationType); \
ok_eq_size(NtQueryTest->MemInfo.RegionSize, Size); \
KmtFreeCallbackResponse(NtQueryTest); \
} \
} while (0) \
#endif
#ifdef KMT_STANDALONE_DRIVER
#define KMT_KERNEL_MODE
typedef NTSTATUS (KMT_IRP_HANDLER)(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PIO_STACK_LOCATION IoStackLocation);
typedef KMT_IRP_HANDLER *PKMT_IRP_HANDLER;
NTSTATUS KmtRegisterIrpHandler(IN UCHAR MajorFunction, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_IRP_HANDLER IrpHandler);
NTSTATUS KmtUnregisterIrpHandler(IN UCHAR MajorFunction, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_IRP_HANDLER IrpHandler);
typedef NTSTATUS (KMT_MESSAGE_HANDLER)(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG ControlCode,
IN PVOID Buffer OPTIONAL,
IN SIZE_T InLength,
IN OUT PSIZE_T OutLength);
typedef KMT_MESSAGE_HANDLER *PKMT_MESSAGE_HANDLER;
NTSTATUS KmtRegisterMessageHandler(IN ULONG ControlCode OPTIONAL, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_MESSAGE_HANDLER MessageHandler);
NTSTATUS KmtUnregisterMessageHandler(IN ULONG ControlCode OPTIONAL, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PKMT_MESSAGE_HANDLER MessageHandler);
typedef enum
{
TESTENTRY_NO_CREATE_DEVICE = 1,
TESTENTRY_NO_REGISTER_DISPATCH = 2,
TESTENTRY_NO_REGISTER_UNLOAD = 4,
TESTENTRY_NO_EXCLUSIVE_DEVICE = 8,
} KMT_TESTENTRY_FLAGS;
NTSTATUS TestEntry(IN PDRIVER_OBJECT DriverObject, IN PCUNICODE_STRING RegistryPath, OUT PCWSTR *DeviceName, IN OUT INT *Flags);
VOID TestUnload(IN PDRIVER_OBJECT DriverObject);
#endif /* defined KMT_STANDALONE_DRIVER */
#ifdef KMT_KERNEL_MODE
/* Device Extension layout */
typedef struct
{
PKMT_RESULTBUFFER ResultBuffer;
PMDL Mdl;
} KMT_DEVICE_EXTENSION, *PKMT_DEVICE_EXTENSION;
extern BOOLEAN KmtIsCheckedBuild;
extern BOOLEAN KmtIsMultiProcessorBuild;
extern PCSTR KmtMajorFunctionNames[];
extern PDRIVER_OBJECT KmtDriverObject;
VOID KmtSetIrql(IN KIRQL NewIrql);
BOOLEAN KmtAreInterruptsEnabled(VOID);
ULONG KmtGetPoolTag(PVOID Memory);
USHORT KmtGetPoolType(PVOID Memory);
PVOID KmtGetSystemRoutineAddress(IN PCWSTR RoutineName);
PKTHREAD KmtStartThread(IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext OPTIONAL);
VOID KmtFinishThread(IN PKTHREAD Thread OPTIONAL, IN PKEVENT Event OPTIONAL);
#elif defined KMT_USER_MODE
DWORD KmtRunKernelTest(IN PCSTR TestName);
VOID KmtLoadDriver(IN PCWSTR ServiceName, IN BOOLEAN RestartIfRunning);
VOID KmtUnloadDriver(VOID);
VOID KmtOpenDriver(VOID);
VOID KmtCloseDriver(VOID);
DWORD KmtSendToDriver(IN DWORD ControlCode);
DWORD KmtSendStringToDriver(IN DWORD ControlCode, IN PCSTR String);
DWORD KmtSendWStringToDriver(IN DWORD ControlCode, IN PCWSTR String);
DWORD KmtSendBufferToDriver(IN DWORD ControlCode, IN OUT PVOID Buffer OPTIONAL, IN DWORD InLength, IN OUT PDWORD OutLength);
#else /* if !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
#error either KMT_KERNEL_MODE or KMT_USER_MODE must be defined
#endif /* !defined KMT_KERNEL_MODE && !defined KMT_USER_MODE */
extern PKMT_RESULTBUFFER ResultBuffer;
#ifdef __GNUC__
/* TODO: GCC doesn't understand %wZ :( */
#define KMT_FORMAT(type, fmt, first) /*__attribute__((__format__(type, fmt, first)))*/
#elif !defined __GNUC__
#define KMT_FORMAT(type, fmt, first)
#endif /* !defined __GNUC__ */
#define START_TEST(name) VOID Test_##name(VOID)
#ifndef KMT_STRINGIZE
#define KMT_STRINGIZE(x) #x
#endif /* !defined KMT_STRINGIZE */
#define ok(test, ...) ok_(test, __FILE__, __LINE__, __VA_ARGS__)
#define trace(...) trace_( __FILE__, __LINE__, __VA_ARGS__)
#define skip(test, ...) skip_(test, __FILE__, __LINE__, __VA_ARGS__)
#define ok_(test, file, line, ...) KmtOk(test, file ":" KMT_STRINGIZE(line), __VA_ARGS__)
#define trace_(file, line, ...) KmtTrace( file ":" KMT_STRINGIZE(line), __VA_ARGS__)
#define skip_(test, file, line, ...) KmtSkip(test, file ":" KMT_STRINGIZE(line), __VA_ARGS__)
BOOLEAN KmtVOk(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments) KMT_FORMAT(ms_printf, 3, 0);
BOOLEAN KmtOk(INT Condition, PCSTR FileAndLine, PCSTR Format, ...) KMT_FORMAT(ms_printf, 3, 4);
VOID KmtVTrace(PCSTR FileAndLine, PCSTR Format, va_list Arguments) KMT_FORMAT(ms_printf, 2, 0);
VOID KmtTrace(PCSTR FileAndLine, PCSTR Format, ...) KMT_FORMAT(ms_printf, 2, 3);
BOOLEAN KmtVSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments) KMT_FORMAT(ms_printf, 3, 0);
BOOLEAN KmtSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, ...) KMT_FORMAT(ms_printf, 3, 4);
PVOID KmtAllocateGuarded(SIZE_T SizeRequested);
VOID KmtFreeGuarded(PVOID Pointer);
#ifdef KMT_KERNEL_MODE
#define ok_irql(irql) ok(KeGetCurrentIrql() == irql, "IRQL is %d, expected %d\n", KeGetCurrentIrql(), irql)
#endif /* defined KMT_KERNEL_MODE */
#define ok_eq_print(value, expected, spec) ok((value) == (expected), #value " = " spec ", expected " spec "\n", value, expected)
#define ok_eq_pointer(value, expected) ok_eq_print(value, expected, "%p")
#define ok_eq_int(value, expected) ok_eq_print(value, expected, "%d")
#define ok_eq_uint(value, expected) ok_eq_print(value, expected, "%u")
#define ok_eq_long(value, expected) ok_eq_print(value, expected, "%ld")
#define ok_eq_ulong(value, expected) ok_eq_print(value, expected, "%lu")
#define ok_eq_longlong(value, expected) ok_eq_print(value, expected, "%I64d")
#define ok_eq_ulonglong(value, expected) ok_eq_print(value, expected, "%I64u")
#define ok_eq_char(value, expected) ok_eq_print(value, expected, "%c")
#define ok_eq_wchar(value, expected) ok_eq_print(value, expected, "%C")
#ifndef _WIN64
#define ok_eq_size(value, expected) ok_eq_print(value, (SIZE_T)(expected), "%lu")
#define ok_eq_longptr(value, expected) ok_eq_print(value, (LONG_PTR)(expected), "%ld")
#define ok_eq_ulongptr(value, expected) ok_eq_print(value, (ULONG_PTR)(expected), "%lu")
#elif defined _WIN64
#define ok_eq_size(value, expected) ok_eq_print(value, (SIZE_T)(expected), "%I64u")
#define ok_eq_longptr(value, expected) ok_eq_print(value, (LONG_PTR)(expected), "%I64d")
#define ok_eq_ulongptr(value, expected) ok_eq_print(value, (ULONG_PTR)(expected), "%I64u")
#endif /* defined _WIN64 */
#define ok_eq_hex(value, expected) ok_eq_print(value, expected, "0x%08lx")
#define ok_bool_true(value, desc) ok((value) == TRUE, desc " FALSE, expected TRUE\n")
#define ok_bool_false(value, desc) ok((value) == FALSE, desc " TRUE, expected FALSE\n")
#define ok_eq_bool(value, expected) ok((value) == (expected), #value " = %s, expected %s\n", \
(value) ? "TRUE" : "FALSE", \
(expected) ? "TRUE" : "FALSE")
#define ok_eq_str(value, expected) ok(!strcmp(value, expected), #value " = \"%s\", expected \"%s\"\n", value, expected)
#define ok_eq_wstr(value, expected) ok(!wcscmp(value, expected), #value " = \"%ls\", expected \"%ls\"\n", value, expected)
#define ok_eq_tag(value, expected) ok_eq_print(value, expected, "0x%08lx")
#define KMT_MAKE_CODE(ControlCode) CTL_CODE(FILE_DEVICE_UNKNOWN, \
0xC00 + (ControlCode), \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#define MICROSECOND 10
#define MILLISECOND (1000 * MICROSECOND)
#define SECOND (1000 * MILLISECOND)
/* See apitests/include/apitest.h */
#define KmtInvalidPointer ((PVOID)0x5555555555555555ULL)
#define KmtStartSeh() \
{ \
NTSTATUS ExceptionStatus = STATUS_SUCCESS; \
_SEH2_TRY \
{
#define KmtEndSeh(ExpectedStatus) \
} \
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) \
{ \
ExceptionStatus = _SEH2_GetExceptionCode(); \
} \
_SEH2_END; \
ok_eq_hex(ExceptionStatus, (ExpectedStatus)); \
}
#if defined KMT_DEFINE_TEST_FUNCTIONS
#if defined KMT_KERNEL_MODE
#include "kmt_test_kernel.h"
#elif defined KMT_USER_MODE
#include "kmt_test_user.h"
#endif /* defined KMT_USER_MODE */
PKMT_RESULTBUFFER ResultBuffer = NULL;
static VOID KmtAddToLogBuffer(PKMT_RESULTBUFFER Buffer, PCSTR String, SIZE_T Length)
{
LONG OldLength;
LONG NewLength;
if (!Buffer)
return;
do
{
OldLength = Buffer->LogBufferLength;
NewLength = OldLength + (ULONG)Length;
if (NewLength > Buffer->LogBufferMaxLength)
return;
} while (InterlockedCompareExchange(&Buffer->LogBufferLength, NewLength, OldLength) != OldLength);
memcpy(&Buffer->LogBuffer[OldLength], String, Length);
}
KMT_FORMAT(ms_printf, 5, 0)
static SIZE_T KmtXVSNPrintF(PSTR Buffer, SIZE_T BufferMaxLength, PCSTR FileAndLine, PCSTR Prepend, PCSTR Format, va_list Arguments)
{
SIZE_T BufferLength = 0;
SIZE_T Length;
if (FileAndLine)
{
PCSTR Slash;
Slash = strrchr(FileAndLine, '\\');
if (Slash)
FileAndLine = Slash + 1;
Slash = strrchr(FileAndLine, '/');
if (Slash)
FileAndLine = Slash + 1;
Length = min(BufferMaxLength, strlen(FileAndLine));
memcpy(Buffer, FileAndLine, Length);
Buffer += Length;
BufferLength += Length;
BufferMaxLength -= Length;
}
if (Prepend)
{
Length = min(BufferMaxLength, strlen(Prepend));
memcpy(Buffer, Prepend, Length);
Buffer += Length;
BufferLength += Length;
BufferMaxLength -= Length;
}
if (Format)
{
Length = KmtVSNPrintF(Buffer, BufferMaxLength, Format, Arguments);
/* vsnprintf can return more than maxLength, we don't want to do that */
BufferLength += min(Length, BufferMaxLength);
}
return BufferLength;
}
KMT_FORMAT(ms_printf, 5, 6)
static SIZE_T KmtXSNPrintF(PSTR Buffer, SIZE_T BufferMaxLength, PCSTR FileAndLine, PCSTR Prepend, PCSTR Format, ...)
{
SIZE_T BufferLength;
va_list Arguments;
va_start(Arguments, Format);
BufferLength = KmtXVSNPrintF(Buffer, BufferMaxLength, FileAndLine, Prepend, Format, Arguments);
va_end(Arguments);
return BufferLength;
}
VOID KmtFinishTest(PCSTR TestName)
{
CHAR MessageBuffer[512];
SIZE_T MessageLength;
if (!ResultBuffer)
return;
MessageLength = KmtXSNPrintF(MessageBuffer, sizeof MessageBuffer, NULL, NULL,
"%s: %ld tests executed (0 marked as todo, %ld failures), %ld skipped.\n",
TestName,
ResultBuffer->Successes + ResultBuffer->Failures,
ResultBuffer->Failures,
ResultBuffer->Skipped);
KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
}
BOOLEAN KmtVOk(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments)
{
CHAR MessageBuffer[512];
SIZE_T MessageLength;
if (!ResultBuffer)
return Condition != 0;
if (Condition)
{
InterlockedIncrement(&ResultBuffer->Successes);
if (0/*KmtReportSuccess*/)
{
MessageLength = KmtXSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": Test succeeded\n", NULL);
KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
}
}
else
{
InterlockedIncrement(&ResultBuffer->Failures);
MessageLength = KmtXVSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": Test failed: ", Format, Arguments);
KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
}
return Condition != 0;
}
BOOLEAN KmtOk(INT Condition, PCSTR FileAndLine, PCSTR Format, ...)
{
BOOLEAN Ret;
va_list Arguments;
va_start(Arguments, Format);
Ret = KmtVOk(Condition, FileAndLine, Format, Arguments);
va_end(Arguments);
return Ret;
}
VOID KmtVTrace(PCSTR FileAndLine, PCSTR Format, va_list Arguments)
{
CHAR MessageBuffer[512];
SIZE_T MessageLength;
MessageLength = KmtXVSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": ", Format, Arguments);
KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
}
VOID KmtTrace(PCSTR FileAndLine, PCSTR Format, ...)
{
va_list Arguments;
va_start(Arguments, Format);
KmtVTrace(FileAndLine, Format, Arguments);
va_end(Arguments);
}
BOOLEAN KmtVSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, va_list Arguments)
{
CHAR MessageBuffer[512];
SIZE_T MessageLength;
if (!ResultBuffer)
return !Condition;
if (!Condition)
{
InterlockedIncrement(&ResultBuffer->Skipped);
MessageLength = KmtXVSNPrintF(MessageBuffer, sizeof MessageBuffer, FileAndLine, ": Tests skipped: ", Format, Arguments);
KmtAddToLogBuffer(ResultBuffer, MessageBuffer, MessageLength);
}
return !Condition;
}
BOOLEAN KmtSkip(INT Condition, PCSTR FileAndLine, PCSTR Format, ...)
{
BOOLEAN Ret;
va_list Arguments;
va_start(Arguments, Format);
Ret = KmtVSkip(Condition, FileAndLine, Format, Arguments);
va_end(Arguments);
return Ret;
}
PVOID KmtAllocateGuarded(SIZE_T SizeRequested)
{
NTSTATUS Status;
SIZE_T Size = PAGE_ROUND_UP(SizeRequested + PAGE_SIZE);
PVOID VirtualMemory = NULL;
PCHAR StartOfBuffer;
Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &VirtualMemory, 0, &Size, MEM_RESERVE, PAGE_NOACCESS);
if (!NT_SUCCESS(Status))
return NULL;
Size -= PAGE_SIZE;
Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &VirtualMemory, 0, &Size, MEM_COMMIT, PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
Size = 0;
Status = ZwFreeVirtualMemory(ZwCurrentProcess(), &VirtualMemory, &Size, MEM_RELEASE);
ok_eq_hex(Status, STATUS_SUCCESS);
return NULL;
}
StartOfBuffer = VirtualMemory;
StartOfBuffer += Size - SizeRequested;
return StartOfBuffer;
}
VOID KmtFreeGuarded(PVOID Pointer)
{
NTSTATUS Status;
PVOID VirtualMemory = (PVOID)PAGE_ROUND_DOWN((SIZE_T)Pointer);
SIZE_T Size = 0;
Status = ZwFreeVirtualMemory(ZwCurrentProcess(), &VirtualMemory, &Size, MEM_RELEASE);
ok_eq_hex(Status, STATUS_SUCCESS);
}
#endif /* defined KMT_DEFINE_TEST_FUNCTIONS */
#endif /* !defined _KMTEST_TEST_H_ */