Kernel-mode C++ unit testing framework in BDD-style

KmTest

Kernel-mode C++ unit testing framework in BDD-style Build status

Introduction

There is a lack of unit testing frameworks that work in OS kernel. This library closes that gap and is targeted for windows driver developers.

Features

  • designed for testing kernel-mode code
  • header-only
  • easy to use
  • BDD-style approach for writing unit tests (as well as a traditional one)
  • code sharing between steps in scenario

Requirements

  • Windows XP and higher
  • Visual Studio 2010 and higher

Usage

Creating a test project

Create an empty driver project and do the following:

  • add a path to kmtest/inlcude into the project include paths
  • add #include into your new cpp/h files (if you have precompiled headers it is a good place to add this include there)

This is a sample precompiled header:

#pragma once
#include <ntddk.h>
#include <kmtest/kmtest.h>

Now you can start writing tests.

Note: DriverEntry is automatically created by the library, so you don't need to write it.

Writing tests

You can write tests cases in 2 styles:

  • BDD-style (using GIVEN-WHEN-THEN clauses)
  • traditional

BDD-style test

BDD-style tests requires more efforts in writing but they are superior in maintaining than traditional tests. The basic test structure is shown below (for more advanced usage read about code sharing):

SCENARIO("Addition operation")
{
    GIVEN("x = 2")
    {
        int x = 2;

        WHEN("y = 3")
        {
            int y = 3;

            THEN("the sum will be 5")
            {
                REQUIRE(Calculator::add(x, y) == 5);
            }
        }
    }
}

Where:

  • SCENARIO,GIVEN,WHEN,THEN are used to describe the test
  • REQUIRE is used for assertions (can be placed in any block)

Code sharing

A great feature of BDD-style tests is that a SCENARIO can have several GIVEN clauses, a GIVEN can have several WHEN clauses, a WHEN can have several THEN clauses. KmTest framework will run all combinations as independed test cases. The sample below will produce 2 test cases (2+3=5 and 2+0=2):

SCENARIO("Addition operation")
{
    GIVEN("x = 2")
    {
        int x = 2;

        WHEN("y = 3")
        {
            int y = 3;

            THEN("the sum will be 5")
            {
                REQUIRE(Calculator::add(x, y) == 5);
            }
        }

        WHEN("y = 0")
        {
            int y = 0;

            THEN("the sum will be 2")
            {
                REQUIRE(Calculator::add(x, y) == 2);
            }
        }
    }
}    

That's not all. Setup/cleanup code can be shared as well. It is demonstrated by the following example:

<== Here you can write a shared setup code for SCENARIO. GIVEN("x = 2") { int x = 2; // <== Here you can write a shared setup code for GIVEN. WHEN("y = 3") { int y = 3; // <== Here you can write a shared setup code for WHEN. THEN("the sum will be 5") { REQUIRE(Calculator::add(x, y) == 5); } // <== Here you can write a shared cleanup code for WHEN. } // <== Here you can write a shared cleanup code for GIVEN. } // <== Here you can write a shared cleanup code for SCENARIO. }">
SCENARIO("Addition operation")
{
    // <== Here you can write a shared setup code for SCENARIO.

    GIVEN("x = 2")
    {
        int x = 2; // <== Here you can write a shared setup code for GIVEN.

        WHEN("y = 3")
        {
            int y = 3; // <== Here you can write a shared setup code for WHEN.

            THEN("the sum will be 5")
            {
                REQUIRE(Calculator::add(x, y) == 5);
            }

            // <== Here you can write a shared cleanup code for WHEN.
        }

        // <== Here you can write a shared cleanup code for GIVEN.
    }

    // <== Here you can write a shared cleanup code for SCENARIO.
}

Traditional test

A traditional test is shown below and is represented by a BDD-style test without GIVEN-WHEN-THEN clauses:

// A minimal scenario for those who do not want to write GIVEN-WHEN-THEN clauses.
SCENARIO("Multiplication operation")
{
    REQUIRE(6 == Calculator::mul(2, 3));
    REQUIRE(-30 == Calculator::mul(-10, 3));
    REQUIRE(6 == Calculator::mul(-2, -3));
    REQUIRE(0 == Calculator::mul(0, 3));
}

Where:

  • SCENARIO is used to describe the test
  • REQUIRE is used for assertions

Running tests

Running KmTest based tests means starting a driver. It is highly recommended to do this inside a virtual machine. Any assertion failure will trigger a kernel debugger breakpoint or a BSOD if there is no debugger.

Refer to samples/CalcTest/CalcTest.cmd for how to start a driver from the command line.

Test output

KmTest writes messages to the debug output. It can be viewed by WinDbg, DbgView or similar tools. A sample test output is demonstrated below:

**************************************************
* KMTEST BEGIN
**************************************************
--------------------------------------------------
SCENARIO: Addition operation
--------------------------------------------------
GIVEN: x = 2
  WHEN: y = 3
    THEN: the sum will be 5
GIVEN: x = 2
  WHEN: y = 0
    THEN: the sum will be 2
GIVEN: x = 2
  WHEN: y = -2
    THEN: the sum will be 0
GIVEN: x = -2
  WHEN: y = 3
    THEN: the sum will be 1
GIVEN: x = -2
  WHEN: y = -1
    THEN: the sum will be -3
 
ASSERTIONS PASSED: 5
 
--------------------------------------------------
SCENARIO: Multiplication operation
--------------------------------------------------
 
ASSERTIONS PASSED: 4
 
--------------------------------------------------
SCENARIO: Subtraction operation
--------------------------------------------------
GIVEN: x = 8
  WHEN: y = 3
    THEN: the difference will be 5
GIVEN: x = 8
  WHEN: y = 0
    THEN: the difference will be 8
GIVEN: x = 8
  WHEN: y = -2
    THEN: the difference will be 10
GIVEN: x = -3
  WHEN: y = 2
    THEN: the difference will be -5
GIVEN: x = -3
  WHEN: y = -1
    THEN: the difference will be -2
 
ASSERTIONS PASSED: 5
 
**************************************************
* KMTEST END (scenarios: 3, assertions: 14)
**************************************************

Samples

There is a samples folder that demonstrates usage of KmTest unit testing framework. To compile it you need Visual Studio 2015 and WDK10.

License

KmTest is licensed under the MPL version 2.0. You can freely use it in your commercial or opensource software.

Acknowledgment

Thanks to Phil Nash and his Catch C++ test framework for BDD-style inspiration.

Version history

Version 0.9.0 (18 Jan 2017)

  • Initial public release
Similar Resources

The fastest feature-rich C++11/14/17/20 single-header testing framework

The fastest feature-rich C++11/14/17/20 single-header testing framework

master branch Windows All dev branch Windows All doctest is a new C++ testing framework but is by far the fastest both in compile times (by orders of

Sep 26, 2022

Googletest - Google Testing and Mocking Framework

GoogleTest OSS Builds Status Announcements Release 1.10.x Release 1.10.x is now available. Coming Soon Post 1.10.x googletest will follow Abseil Live

Sep 24, 2022

C++ xUnit-like testing framework without macros

tst C++ testing framework. Installation, documentation, tutorials See WiKi. Features xUnit-like concepts minimal use of preprocessor macros declarativ

Sep 26, 2022

c++ testing framework

iutest iutest - iris unit test framework Welcome to the iutest iutest is framework for writing C++ tests. Features An XUnit test framework. Header onl

Sep 12, 2022

Simple C testing framework

MrTest Simple C testing framework Usage Copy the mrtest.c and mrtest.h file into your project. In order to use the mrtest main: create a .c file that

Jul 20, 2022

xtest is a C++ testing framework inspired by googletest.

xtest is a C++ testing framework inspired by googletest.

xtest C++ testing framework inspired by googletest Explore the docs » Wiki · Report Bug · Request Feature Contents xtest Commence Prerequisites Ubuntu

Jul 9, 2022

A minimal testing framework for C/C++

A minimal testing framework for C/C++

mtest About mtest is a minimal testing framework for C++. Requirements Windows or UNIX-like host A compiler supporting C++11 Usage To include mtest in

Jan 10, 2022

The fastest feature-rich C++11/14/17/20 single-header testing framework

The fastest feature-rich C++11/14/17/20 single-header testing framework

master branch dev branch doctest is a new C++ testing framework but is by far the fastest both in compile times (by orders of magnitude) and runtime c

Sep 29, 2022

A dynamic mock tool for C/C++ unit test on Linux&MacOS X86_64

lmock 接口 替换一个函数,修改机器指令,用新函数替换旧函数,支持全局函数(包括第三方和系统函数)、成员函数(包括静态和虚函数)

Sep 20, 2022
Comments
  • Cannot convert const char [] to PVOID

    Cannot convert const char [] to PVOID

    So, I was trying out the unit test framework with some simple tests but I immediately ran into the following compiler error:

    'void RtlAssert(PVOID,PVOID,ULONG,PSTR)': cannot convert argument 1 from 'const char [12]' to 'PVOID'

    This compiler error was generated from the following:

    #include <ntddk.h>
    #include "kmtest/kmtest.h"
    #include "kmd_utility.hpp"
    
    SCENARIO("Minimum operation")
    {
        int x{ 2 };
        int y{ 3 };
        int result{ (kmd::min)(x, y) };
        REQUIRE(2 == result);
    }
    

    The same errors occurs when passing the contents of the __FILE__ macro to RtlAssert.

  • warning C4127: conditional expression is constant

    warning C4127: conditional expression is constant

    So this particular warning can become an issue when compiling with /WX and /W4. The following can trigger this warning:

    SCENARIO("Minimum operation")
    {
        const int x{ 2 };
        const int y{ 3 };
        REQUIRE(x < y);
    }
    

    I believe re-writing the REQUIRE macro to the following would correct the issue:

    #define REQUIRE(expression) \
    do { \
        ++assertions; \
        const auto result__ = (expression); \
        if (!result__) { \
            KMTEST_ASSERT(const_cast<char*>(#expression), const_cast<char*>(__FILE__), __LINE__, nullptr); \
            ++failures; \
        } \
    } while (0)
    

    It looks like the C4127 was being generated when evaluating the constant expression in the if statement of the original REQUIRE macro.

    Maybe some more testing is needed in order to fully confirm this is the case for different compiler versions. Currently I've only tested this with VS2019 version 16.11.13 and VS2022 version 17.1.4.

The C Unit Testing Library on GitHub is a library designed for easy unit testing in C

The C Unit Testing Library on GitHub is a library designed for easy unit testing in C. It was written by Brennan Hurst for the purpose of providing a J-Unit-like testing framework within C for personal projects.

Oct 11, 2021
A modern, C++-native, header-only, test framework for unit-tests, TDD and BDD - using C++11, C++14, C++17 and later (or C++03 on the Catch1.x branch)
A modern, C++-native, header-only, test framework for unit-tests, TDD and BDD - using C++11, C++14, C++17 and later (or C++03 on the Catch1.x branch)

Catch2 v3 is being developed! You are on the devel branch, where the next major version, v3, of Catch2 is being developed. As it is a significant rewo

Sep 26, 2022
A modern, C++11-native, single-file header-only, tiny framework for unit-tests, TDD and BDD (includes C++98 variant)

lest – lest errors escape testing This tiny C++11 test framework is based on ideas and examples by Kevlin Henney [1,2] and on ideas found in the CATCH

Sep 19, 2022
UT: C++20 μ(micro)/Unit Testing Framework
UT: C++20 μ(micro)/Unit Testing Framework

"If you liked it then you "should have put a"_test on it", Beyonce rule UT / μt | Motivation | Quick Start | Overview | Tutorial | Examples | User Gui

Sep 28, 2022
A complete unit testing framework in a header

liblittletest A complete unit testing framework in a header liblittletest is an easy to use all-in-an-header testing framework; all you have to do in

Nov 11, 2021
Modern c++17 unit testing framework on Microsoft Windows, Apple macOS, Linux, iOS and android.
Modern c++17 unit testing framework on Microsoft Windows, Apple macOS, Linux, iOS and android.

tunit Modern c++17 unit testing framework on Windows, macOS, Linux, iOS and android. Continuous Integration build status Operating system Status Windo

Apr 5, 2022
A micro unit-testing library for C/C++

µ-test A micro unit testing framework for C/C++ to get you up and running with unit-testing ASAP (even without libc). Usage Simply include the C and h

Dec 8, 2021
testing joystick under Linux environment, support monitoring disconnection state and auto recovery mode

qjoystick This qjoystick class is rewritten based on the library: https://github.com/drewnoakes/joystick. Please look at this library if you want to s

Oct 30, 2021
Various Framework to do Unit Test in C++
Various Framework to do Unit Test in C++

Unit Test in C++ There are many frameworks to performs unit test in C++, we will present the most popular ones and show how to use them. The testing f

Nov 18, 2021
Upp11 - C++11 lightweight single header unit test framework

upp11 Lightweight C++11 single header unit test framework To use framework: Copy upp11.h in you project dir. Create unit test source files or modify e

Apr 4, 2019