- Fix epic naming fail (DhcpEnabled -> EnableDHCP
[reactos.git] / rostests / drivers / kmtest / kmtest.c
index d30f348..3164688 100644 (file)
 #include <ddk/ntddk.h>
 #include "kmtest.h"
 
+#define NDEBUG
+#include <debug.h>
+
 LONG successes;
 LONG failures;
+LONG skipped;
 tls_data glob_data;
 
 /* PRIVATE FUNCTIONS ***********************************************************/
@@ -35,12 +39,61 @@ StartTest()
 {
     successes = 0;
     failures = 0;
+    skipped = 0;
 }
 
 VOID
-FinishTest(LPSTR TestName)
+FinishTest(HANDLE KeyHandle, LPWSTR TestName)
 {
-    DbgPrint("Test %s finished with %d succeses and %d failures\n", TestName, successes, failures);
+    WCHAR KeyName[100];
+    LONG total = successes + failures;
+    UNICODE_STRING KeyNameU;
+
+    wcscpy(KeyName, TestName);
+    wcscat(KeyName, L"SuccessCount");
+    RtlInitUnicodeString(&KeyNameU, KeyName);
+
+    ZwSetValueKey(KeyHandle,
+                  &KeyNameU,
+                  0,
+                  REG_DWORD,
+                  &successes,
+                  sizeof(ULONG));
+
+    wcscpy(KeyName, TestName);
+    wcscat(KeyName, L"FailureCount");
+    RtlInitUnicodeString(&KeyNameU, KeyName);
+
+    ZwSetValueKey(KeyHandle,
+                  &KeyNameU,
+                  0,
+                  REG_DWORD,
+                  &failures,
+                  sizeof(ULONG));
+
+    wcscpy(KeyName, TestName);
+    wcscat(KeyName, L"TotalCount");
+    RtlInitUnicodeString(&KeyNameU, KeyName);
+
+    ZwSetValueKey(KeyHandle,
+                  &KeyNameU,
+                  0,
+                  REG_DWORD,
+                  &total,
+                  sizeof(ULONG));
+
+    wcscpy(KeyName, TestName);
+    wcscat(KeyName, L"SkipCount");
+    RtlInitUnicodeString(&KeyNameU, KeyName);
+
+    ZwSetValueKey(KeyHandle,
+                  &KeyNameU,
+                  0,
+                  REG_DWORD,
+                  &skipped,
+                  sizeof(ULONG));
+
+    DbgPrint("%S: %d test executed (0 marked as todo, %d failures), %d skipped.\n", TestName, total, failures, skipped);
 }
 
 void kmtest_set_location(const char* file, int line)
@@ -100,13 +153,89 @@ int kmtest_ok(int condition, const char *msg, ... )
 
 /* PUBLIC FUNCTIONS ***********************************************************/
 
+PWCHAR CreateLowerDeviceRegistryKey(PUNICODE_STRING RegistryPath, PWCHAR NewDriver);
+
 /*
  * Test Declarations
  */
-VOID NtoskrnlIoTests();
-VOID NtoskrnlObTest();
-VOID NtoskrnlExecutiveTests();
-VOID NtoskrnlPoolsTest();
+VOID RegisterDI_Test(HANDLE KeyHandle);
+VOID NtoskrnlIoMdlTest(HANDLE KeyHandle);
+VOID NtoskrnlIoIrpTest(HANDLE KeyHandle);
+VOID NtoskrnlObTest(HANDLE KeyHandle);
+VOID ExTimerTest(HANDLE KeyHandle);
+VOID PoolsTest(HANDLE KeyHandle);
+VOID PoolsCorruption(HANDLE KeyHandle);
+VOID KeStallTest(HANDLE KeyHandle);
+VOID DriverObjectTest(PDRIVER_OBJECT, int);
+VOID DeviceCreateDeleteTest(PDRIVER_OBJECT);
+VOID DeviceObjectTest(PDEVICE_OBJECT);
+BOOLEAN ZwLoadTest(PDRIVER_OBJECT, PUNICODE_STRING, PWCHAR);
+BOOLEAN ZwUnloadTest(PDRIVER_OBJECT, PUNICODE_STRING, PWCHAR);
+BOOLEAN DetachDeviceTest(PDEVICE_OBJECT);
+BOOLEAN AttachDeviceTest(PDEVICE_OBJECT,  PWCHAR);
+VOID LowerDeviceKernelAPITest(PDEVICE_OBJECT, BOOLEAN);
+
+typedef enum {
+    TestStageExTimer = 0,
+    TestStageIoMdl,
+    TestStageIoDi,
+    TestStageIoIrp,
+    TestStageMmPoolTest,
+    TestStageMmPoolCorruption,
+    TestStageOb,
+    TestStageKeStall,
+    TestStageDrv,
+    TestStageMax
+} TEST_STAGE;
+
+/*
+ * KmtestDispatch
+ */
+NTSTATUS
+NTAPI
+KmtestDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    if (AttachDeviceObject)
+    {
+        IoSkipCurrentIrpStackLocation(Irp);
+        Status = IoCallDriver(AttachDeviceObject, Irp);
+        return Status;
+    }
+
+    Irp->IoStatus.Status = Status;
+    Irp->IoStatus.Information = 0;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+}
+
+/*
+ * KmtestCreateClose
+ */
+NTSTATUS
+NTAPI
+KmtestCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    if (AttachDeviceObject)
+    {
+        IoSkipCurrentIrpStackLocation(Irp);
+        Status = IoCallDriver(AttachDeviceObject, Irp);
+        return Status;
+    }
+
+    /* Do DriverObject Test with Driver Initialized */
+    DriverObjectTest(DeviceObject->DriverObject, 1);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    Irp->IoStatus.Information=0;
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return STATUS_SUCCESS;
+}
 
 /*
  * KmtestUnload
@@ -115,7 +244,165 @@ VOID
 NTAPI
 KmtestUnload(IN PDRIVER_OBJECT DriverObject)
 {
-    /* Nothing to do here */
+    UNICODE_STRING DosDeviceString;
+
+    if(AttachDeviceObject)
+    {
+        IoDetachDevice(AttachDeviceObject);
+    }
+
+    /* Do DriverObject Test for Unload */
+    DriverObjectTest(DriverObject, 2);
+
+    if (MainDeviceObject)
+    {
+        RtlInitUnicodeString(&DosDeviceString, L"\\DosDevices\\Kmtest");
+        IoDeleteSymbolicLink(&DosDeviceString);
+
+        IoDeleteDevice(MainDeviceObject);
+    }
+}
+
+static
+PKEY_VALUE_PARTIAL_INFORMATION
+NTAPI
+ReadRegistryValue(HANDLE KeyHandle, PWCHAR ValueName)
+{
+    NTSTATUS Status;
+    PKEY_VALUE_PARTIAL_INFORMATION InformationBuffer = NULL;
+    ULONG AllocatedLength = 0, RequiredLength = 0;
+    UNICODE_STRING ValueNameU;
+
+    RtlInitUnicodeString(&ValueNameU, ValueName);
+
+    Status = ZwQueryValueKey(KeyHandle,
+                             &ValueNameU,
+                             KeyValuePartialInformation,
+                             NULL,
+                             0,
+                             &RequiredLength);
+    if (Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW)
+    {
+        InformationBuffer = ExAllocatePool(PagedPool, RequiredLength);
+        AllocatedLength = RequiredLength;
+        if (!InformationBuffer) return NULL;
+
+        Status = ZwQueryValueKey(KeyHandle,
+                                 &ValueNameU,
+                                 KeyValuePartialInformation,
+                                 InformationBuffer,
+                                 AllocatedLength,
+                                 &RequiredLength);
+    }
+
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to read %S (0x%x)\n", ValueName, Status);
+        if (InformationBuffer != NULL)
+            ExFreePool(InformationBuffer);
+        return NULL;
+    }
+
+    return InformationBuffer;
+}
+
+static
+VOID
+RunKernelModeTest(PDRIVER_OBJECT DriverObject,
+                  PUNICODE_STRING RegistryPath,
+                  HANDLE KeyHandle,
+                  TEST_STAGE Stage)
+{
+    UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"CurrentStage");
+    PWCHAR LowerDriverRegPath;
+
+    DPRINT1("Running stage %d test...\n", Stage);
+
+    ZwSetValueKey(KeyHandle,
+                  &KeyName,
+                  0,
+                  REG_DWORD,
+                  &Stage,
+                  sizeof(ULONG));
+
+    switch (Stage)
+    {
+       case TestStageExTimer:
+         ExTimerTest(KeyHandle);
+         break;
+
+       case TestStageIoMdl:
+         NtoskrnlIoMdlTest(KeyHandle);
+         break;
+
+       case TestStageIoDi:
+         RegisterDI_Test(KeyHandle);
+         break;
+
+       case TestStageIoIrp:
+         NtoskrnlIoIrpTest(KeyHandle);
+         break;
+
+       case TestStageMmPoolTest:
+         PoolsTest(KeyHandle);
+         break;
+
+       case TestStageMmPoolCorruption:
+         PoolsCorruption(KeyHandle);
+         break;
+
+       case TestStageOb:
+         NtoskrnlObTest(KeyHandle);
+         break;
+
+       case TestStageKeStall:
+         KeStallTest(KeyHandle);
+         break;
+
+       case TestStageDrv:
+         /* Start the tests for the driver routines */
+         StartTest();
+
+         /* Do DriverObject Test for Driver Entry */
+         DriverObjectTest(DriverObject, 0);
+
+         /* Create and delete device, on return MainDeviceObject has been created */
+         DeviceCreateDeleteTest(DriverObject);
+
+         /* Make sure a device object was created */
+         if (MainDeviceObject)
+         {
+             LowerDriverRegPath = CreateLowerDeviceRegistryKey(RegistryPath, L"kmtestassist");
+
+             if (LowerDriverRegPath)
+             {
+                 /* Load driver test and load the lower driver */
+                 if (ZwLoadTest(DriverObject, RegistryPath, LowerDriverRegPath))
+                 {
+                     AttachDeviceTest(MainDeviceObject, L"kmtestassists");
+                     if (AttachDeviceObject)
+                     {
+                         LowerDeviceKernelAPITest(MainDeviceObject, FALSE);
+                     }
+
+                     /* Unload lower driver without detaching from its device */
+                     ZwUnloadTest(DriverObject, RegistryPath, LowerDriverRegPath);
+                     LowerDeviceKernelAPITest(MainDeviceObject, TRUE);
+                 }
+                 else
+                 {
+                     DbgPrint("Failed to load kmtestassist driver\n");
+                 }
+             }
+         }
+
+         FinishTest(KeyHandle, L"DriverTest");
+         break;
+
+       default:
+         ASSERT(FALSE);
+         break;
+     }
 }
 
 /*
@@ -126,15 +413,108 @@ NTAPI
 DriverEntry(PDRIVER_OBJECT DriverObject,
             PUNICODE_STRING RegistryPath)
 {
-    DbgPrint("\n===============================================\nKernel Mode Regression Test driver starting...\n");
+    int i;
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    UNICODE_STRING ParameterKeyName = RTL_CONSTANT_STRING(L"Parameters");
+    PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
+    PULONG KeyValue;
+    TEST_STAGE CurrentStage;
+    HANDLE DriverKeyHandle, ParameterKeyHandle;
+
+    DbgPrint("\n===============================================\n");
+    DbgPrint("Kernel Mode Regression Driver Test starting...\n");
+    DbgPrint("===============================================\n");
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               RegistryPath,
+                               OBJ_CASE_INSENSITIVE,
+                               0,
+                               NULL);
+
+    Status = ZwOpenKey(&DriverKeyHandle,
+                       KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS,
+                       &ObjectAttributes);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to open %wZ\n", RegistryPath);
+        return Status;
+    }
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &ParameterKeyName,
+                               OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
+                               DriverKeyHandle,
+                               NULL);
+    Status = ZwCreateKey(&ParameterKeyHandle,
+                         KEY_SET_VALUE | KEY_QUERY_VALUE,
+                         &ObjectAttributes,
+                         0,
+                         NULL,
+                         REG_OPTION_NON_VOLATILE,
+                         NULL);
+    ZwClose(DriverKeyHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Failed to create %wZ\\%wZ\n", RegistryPath, &ParameterKeyName);
+        return Status;
+    }
+
+    KeyInfo = ReadRegistryValue(ParameterKeyHandle, L"CurrentStage");
+    if (KeyInfo)
+    {
+        if (KeyInfo->DataLength != sizeof(ULONG))
+        {
+            DPRINT1("Invalid data length for CurrentStage: %d\n", KeyInfo->DataLength);
+            ExFreePool(KeyInfo);
+            return STATUS_UNSUCCESSFUL;
+        }
+
+        KeyValue = (PULONG)KeyInfo->Data;
+
+        if ((*KeyValue) + 1 < TestStageMax)
+        {
+            DPRINT1("Resuming testing after a crash at stage %d\n", (*KeyValue));
+
+            CurrentStage = (TEST_STAGE)((*KeyValue) + 1);
+        }
+        else
+        {
+            DPRINT1("Testing was completed on a previous boot\n");
+            ExFreePool(KeyInfo);
+            return STATUS_UNSUCCESSFUL;
+        }
+
+        ExFreePool(KeyInfo);
+    }
+    else
+    {
+        DPRINT1("Starting a fresh test\n");
+        CurrentStage = (TEST_STAGE)0;
+    }
+
+    /* Run the tests */
+    while (CurrentStage < TestStageMax)
+    {
+       RunKernelModeTest(DriverObject,
+                         RegistryPath,
+                         ParameterKeyHandle,
+                         CurrentStage);
+       CurrentStage++;
+    }
+
+    DPRINT1("Testing is complete!\n");
+    ZwClose(ParameterKeyHandle);
+
+    /* Set all MajorFunctions to NULL to verify that kernel fixes them */
+    for (i = 1; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+        DriverObject->MajorFunction[i] = NULL;
 
     /* Set necessary routines */
     DriverObject->DriverUnload = KmtestUnload;
-
-    NtoskrnlExecutiveTests();
-    NtoskrnlIoTests();
-    NtoskrnlObTest();
-    NtoskrnlPoolsTest();
+    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KmtestDispatch;
+    DriverObject->MajorFunction[IRP_MJ_CREATE] = KmtestCreateClose;
+    DriverObject->MajorFunction[IRP_MJ_CLOSE] = KmtestCreateClose;
 
     return STATUS_SUCCESS;
 }