Documentation: kunit: adds complete documentation for KUnit - Added intro and usage guide for KUnit - Added API reference Change-Id: I9f0969f328a2a812677b2c5c4a3c69a049bbb551 Signed-off-by: Felix Guo <felixguoxiuping@gmail.com> Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
diff --git a/Documentation/index.rst b/Documentation/index.rst index 5db7e87..33b1c4c 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst
@@ -68,6 +68,7 @@ kernel-hacking/index trace/index maintainer/index + test/index Kernel API documentation ------------------------
diff --git a/Documentation/test/api/class-and-function-mocking.rst b/Documentation/test/api/class-and-function-mocking.rst new file mode 100644 index 0000000..013004f --- /dev/null +++ b/Documentation/test/api/class-and-function-mocking.rst
@@ -0,0 +1,66 @@ +========================== +Class and Function Mocking +========================== + +This file documents class and function mocking features. + +.. note:: + If possible, prefer class mocking over arbitrary function mocking. Class + mocking has a much more limited scope and provides more control. + This file documents class mocking and most mocking features that do not + depend on function or platform mocking. + +Readability Macros +------------------ +When defining and declaring mock stubs, use these readability macros. + +.. code-block:: c + + #define CLASS(struct_name) struct_name + #define HANDLE_INDEX(index) index + #define METHOD(method_name) method_name + #define RETURNS(return_type) return_type + #define PARAMS(...) __VA_ARGS__ + +Consider a ``struct Foo`` with a member function +``int add(struct Foo*, int a, int b);`` + +When generating a mock stub with :c:func:`DEFINE_STRUCT_CLASS_MOCK`, which +takes a method name, struct name, return type, and method parameters, the +arguments should be passed in with the readability macros. + +.. code-block:: c + + DEFINE_STRUCT_CLASS_MOCK( + METHOD(add), + CLASS(Foo), + RETURNS(int), + PARAMS(struct Foo *, int, int) + ); + +For a more detailed example of this, take a look at the example in +:doc:`../start` + +These macros should only be used in the context of the mock stub generators. + + +Built in Matchers +----------------- + +.. kernel-doc:: include/test/mock.h + :doc: Built In Matchers + +Mock Returns +------------ +These functions can be used to specify a value to be returned (``ret``) when a +mocked function is intercepted via :c:func:`EXPECT_CALL`. + +.. code-block:: c + + struct mock_action *int_return(struct test *test, int ret); + struct mock_action *u32_return(struct test *test, u32 ret); + +API +--- +.. kernel-doc:: include/test/mock.h + :internal:
diff --git a/Documentation/test/api/index.rst b/Documentation/test/api/index.rst new file mode 100644 index 0000000..a032f94 --- /dev/null +++ b/Documentation/test/api/index.rst
@@ -0,0 +1,19 @@ +============= +API Reference +============= +.. toctree:: + + test + class-and-function-mocking + platform-mocking + +This section documents the KUnit kernel testing API. It is divided into 3 +sections: + +================================= ============================================== +:doc:`test` documents all of the standard testing API + excluding mocking or mocking related features. +:doc:`class-and-function-mocking` documents class and function mocking features. +:doc:`platform-mocking` documents mocking libraries that mock out + platform specific features. +================================= ==============================================
diff --git a/Documentation/test/api/platform-mocking.rst b/Documentation/test/api/platform-mocking.rst new file mode 100644 index 0000000..219f9e1 --- /dev/null +++ b/Documentation/test/api/platform-mocking.rst
@@ -0,0 +1,34 @@ +================ +Platform Mocking +================ + +This file documents *platform mocking*, mocking libraries that mock out platform +specific features and aid in writing mocks for platform drivers and other low +level kernel code. + +Enable Platform Mocking +----------------------- +``CONFIG_PLATFORM_MOCK`` needs to be added to the .config (or kunitconfig) to +enable platform mocking. + +Mocked IO Functions +------------------- +The following functions have been mocked for convenience. + +.. code-block:: c + + u8 readb(const volatile void __iomem *); + u16 readw(const volatile void __iomem *); + u32 readl(const volatile void __iomem *); + u64 readq(const volatile void __iomem *); + void writeb(u8, const volatile void __iomem *); + void writew(u16, const volatile void __iomem *); + void writel(u32, const volatile void __iomem *); + void writeq(u64, const volatile void __iomem *); + +.. note:: These functions do not have any non-mocked behaviour in UML. + +API +--- +.. kernel-doc:: include/linux/platform_device_mock.h + :internal: \ No newline at end of file
diff --git a/Documentation/test/api/test.rst b/Documentation/test/api/test.rst new file mode 100644 index 0000000..b3ef4d1 --- /dev/null +++ b/Documentation/test/api/test.rst
@@ -0,0 +1,13 @@ +======== +Test API +======== + +This file documents all of the standard testing API excluding mocking or mocking +related features. + +.. kernel-doc:: include/test/test.h + :internal: + +.. kernel-doc:: include/test/test-stream.h + :internal: +
diff --git a/Documentation/test/index.rst b/Documentation/test/index.rst new file mode 100644 index 0000000..4ef0a8d --- /dev/null +++ b/Documentation/test/index.rst
@@ -0,0 +1,81 @@ +========================================= +KUnit - Unit Testing for the Linux Kernel +========================================= + +.. toctree:: + :maxdepth: 2 + + start + usage + api/index + +What is KUnit? +============== + +KUnit is a lightweight unit testing and mocking framework for the Linux kernel. +These tests are able to be run locally on a developer’s workstation without a VM +or special hardware. + +KUnit is heavily inspired by JUnit, Python's ``unittest.mock``, and +Googletest/Googlemock for C++. They have the same structure for defining test +suites and test cases. KUnit defines a way to mock out C style classes and +functions and create expectations on methods called within the code under test. + +Get started now: :doc:`start` + +Why KUnit? +========== + +Aside from KUnit there is no true unit testing framework for the Linux kernel. +Autotest and kselftest are sometimes cited as unit testing frameworks; however, +they are not by most reasonable definitions of unit tests. + +A unit test is supposed to test a single unit of code in isolation, hence the +name. A unit test should be the finest granularity of testing and as such should +allow all possible code paths to be tested in the code under test; this is only +possible if the code under test is very small and does not have any external +dependencies outside of the test's control like hardware. + +Outside of KUnit, there are no testing frameworks currently +available for the kernel that do not require installing the kernel on a test +machine or in a VM and all require tests to be written in userspace running on +the kernel; this is true for Autotest, and kselftest, disqualifying +any of them from being considered unit testing frameworks. + +KUnit addresses the problem of being able to run tests without needing a virtual +machine or actual hardware with User Mode Linux. User Mode Linux is a Linux +architecture, like ARM or x86; however, unlike other architectures it compiles +to a standalone program that can be run like any other program directly inside +of a host operating system; to be clear, it does not require any virtualization +support; it is just a regular program. + +KUnit is fast. Excluding build time, from invocation to completion KUnit can run +several dozen tests in only 10 to 20 seconds; this might not sound like a big +deal to some people, but having such fast and easy to run tests fundamentally +changes the way you go about testing and even writing code in the first place. +Linus himself said in his `git talk at Google +<https://gist.github.com/lorn/1272686/revisions#diff-53c65572127855f1b003db4064a94573R874>`_: + + "... a lot of people seem to think that performance is about doing the + same thing, just doing it faster, and that is not true. That is not what + performance is all about. If you can do something really fast, really + well, people will start using it differently." + +In this context Linus was talking about branching and merging, +but this point also applies to testing. If your tests are slow, unreliable, are +difficult to write, and require a special setup or special hardware to run, +then you wait a lot longer to write tests, and you wait a lot longer to run +tests; this means that tests are likely to break, unlikely to test a lot of +things, and are unlikely to be rerun once they pass. If your tests are really +fast, you run them all the time, everytime you make a change, and everytime +someone sends you some code. Why trust that someone ran all their tests +correctly on every change when you can just run them yourself in less time than +it takes to read his / her test log? + +How do I use it? +=================== + +* :doc:`start` - for new users of KUnit +* :doc:`usage` - for a more detailed explanation of KUnit features +* :doc:`api/index` - for the list of KUnit APIs used for testing +
diff --git a/Documentation/test/start.rst b/Documentation/test/start.rst new file mode 100644 index 0000000..fe3fdee --- /dev/null +++ b/Documentation/test/start.rst
@@ -0,0 +1,234 @@ +=============== +Getting Started +=============== + +Installing dependencies +======================= +KUnit has the same dependencies as the Linux kernel. As long as you can build +the kernel, you can run KUnit. + +KUnit Wrapper +============= +Included with KUnit is a simple Python wrapper that helps format the output to +easily use and read KUnit output. It handles building and running the kernel, as +well as formatting the output. + +The wrapper can be run with: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py + +Creating a kunitconfig +====================== +The Python script is a thin wrapper around Kbuild as such, it needs to be +configured with a ``kunitconfig`` file. This file essentially contains the +regular Kernel config, with the specific test targets as well. + +.. TODO(brendanhiggins@google.com): I guess we need to release a kunitconfig + when we release KUnit. We need to create a repo and then link it here. + +.. code-block:: bash + + PLACEHOLDER_COMMAND_TO_GET_RELEASED_KUNITCONFIG + +You may want to add kunitconfig to your local gitignore. + +Verifying KUnit Works +------------------------- + +To make sure that everything is set up correctly, simply invoke the Python +wrapper from your kernel repo: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py + +.. note:: + You may want to run ``make mrproper`` first. + +If everything worked correctly, you should see the following: + +.. code-block:: bash + + Generating .config ... + Building KUnit Kernel ... + Starting KUnit Kernel ... + +followed by a list of tests that are run. All of them should be passing. + +.. note:: + Because it is building a lot of sources for the first time, the ``Building + kunit kernel`` step may take a while. + +Writing your first test +========================== + +In your kernel repo let's add some code that we can test. Create a file +``drivers/misc/example.h`` with the contents: + +.. code-block:: c + + struct misc_example { + int (*misc_example_foo)(struct misc_example *, int); + }; + + int misc_example_bar(struct misc_example *example); + +create a file ``drivers/misc/example.c``: + +.. code-block:: c + + #include <linux/errno.h> + + #include "example.h" + + int misc_example_bar(struct misc_example *example) + { + if (example->misc_example_foo(example, 5)) + return -EIO; + else + return 0; + } + +Now add the following lines to ``drivers/misc/Kconfig``: + +.. code-block:: kconfig + + config MISC_EXAMPLE + bool "My example" + +and the following lines to ``drivers/misc/Makefile``: + +.. code-block:: make + + obj-$(CONFIG_MISC_EXAMPLE) += example.o + +Now we are ready to write the test. The test will be in +``drivers/misc/example-test.c``: + +.. code-block:: c + + #include <linux/test.h> + #include <linux/mock.h> + #include "example.h" + + /* Define the mock. */ + + DECLARE_STRUCT_CLASS_MOCK_PREREQS(misc_example); + + DEFINE_STRUCT_CLASS_MOCK(METHOD(misc_example_foo), CLASS(misc_example), + RETURNS(int), + PARAMS(struct misc_example *, int)); + + static int misc_example_init(struct MOCK(misc_example) *mock_example) + { + struct misc_example *example = mock_get_trgt(mock_example); + + example->misc_example_foo = misc_example_foo; + return 0; + } + + DEFINE_STRUCT_CLASS_MOCK_INIT(misc_example, misc_example_init); + + /* Define the test cases. */ + + static void misc_example_bar_test_success(struct test *test) + { + struct MOCK(misc_example) *mock_example = test->priv; + struct misc_example *example = mock_get_trgt(mock_example); + struct mock_expectation *handle; + + handle = EXPECT_CALL(misc_example_foo(mock_get_ctrl(mock_example), + int_eq(test, 5))); + handle->action = int_return(test, 0); + + EXPECT_EQ(test, 0, misc_example_bar(example)); + } + + static void misc_example_bar_test_failure(struct test *test) + { + struct MOCK(misc_example) *mock_example = test->priv; + struct misc_example *example = mock_get_trgt(mock_example); + struct mock_expectation *handle; + + handle = EXPECT_CALL(misc_example_foo(mock_get_ctrl(mock_example), + int_eq(test, 5))); + handle->action = int_return(test, -EINVAL); + + EXPECT_EQ(test, -EINVAL, misc_example_bar(example)); + } + + static int misc_example_test_init(struct test *test) + { + test->priv = CONSTRUCT_MOCK(misc_example, test); + if (!test->priv) + return -EINVAL; + + return 0; + } + + static void misc_example_test_exit(struct test *test) + { + } + + static struct test_case misc_example_test_cases[] = { + TEST_CASE(misc_example_bar_test_success), + TEST_CASE(misc_example_bar_test_failure), + {}, + }; + + static struct test_module misc_example_test_module = { + .name = "misc-example", + .init = misc_example_test_init, + .exit = misc_example_test_exit, + .test_cases = misc_example_test_cases, + }; + module_test(misc_example_test_module); + +Now add the following to ``drivers/misc/Kconfig``: + +.. code-block:: kconfig + + config MISC_EXAMPLE_TEST + bool "Test for my example" + depends on MISC_EXAMPLE && TEST + +and the following to ``drivers/misc/Makefile``: + +.. code-block:: make + + obj-$(CONFIG_MISC_EXAMPLE_TEST) += example-test.o + +Now add it to your ``kunitconfig``: + +.. code-block:: none + + CONFIG_MISC_EXAMPLE=y + CONFIG_MISC_EXAMPLE_TEST=y + +Now you can run the test: + +.. code-block:: bash + + ./tools/testing/kunit/kunit.py + +You should see the following failure: + +.. code-block:: none + + ... + kunit misc-example: misc_example_bar_test_success passed + kunit misc-example: EXPECTATION FAILED at drivers/misc/example-test.c:48 + Expected -22 == misc_example_bar(example), but + -22 == -22 + misc_example_bar(example) == -5 + kunit misc-example: misc_example_bar_test_failure failed + kunit misc-example: one or more tests failed + +Congrats! You just wrote your first KUnit test! + +Next Steps +============= +* Check out the :doc:`usage` page for a more + in-depth explanation of KUnit. \ No newline at end of file
diff --git a/Documentation/test/usage.rst b/Documentation/test/usage.rst new file mode 100644 index 0000000..f833b84c --- /dev/null +++ b/Documentation/test/usage.rst
@@ -0,0 +1,644 @@ +============= +Using KUnit +============= + +The purpose of this document is to describe what KUnit is, how it works, how it +is intended to be used, and all the concepts and terminology that are needed to +understand it. This guide assumes a working knowledge of the Linux kernel and +some basic knowledge of testing. + +For a high level introduction to KUnit, including setting up KUnit for your +project, see :doc:`start`. + +Organization of this document +================================= + +This document is organized into two main sections: Testing and Mocking. The +first covers what a unit test is and how to use KUnit to write them. The second +covers how to use KUnit to mock out dependencies and make it possible to unit +test code that was otherwise un-unit-testable. + +Testing +========== + +What is KUnit? +------------------ + +"K" is short for "kernel" so "KUnit" is the "(Linux) Kernel Unit Testing +Framework." KUnit is intended first and foremost for writing unit tests; it is +general enough that it can be used to write integration tests; however, this is +a secondary goal. KUnit has no ambition of being the only testing framework for +the kernel; for example, it does not intend to be an end-to-end testing +framework. + +What is Unit Testing? +------------------------- + +A *unit test* is a test that tests code at the smallest possible scope, a +*unit* of code. For the C programming language, defining a unit is easy: it is +the compilation unit; however, that does not mean compilation units cannot be +too large and should not be broken down to get more coverage or just because +they are hard to reason about. + +A unit test should test all the publicly exposed functions in a unit; so that is +all the functions that are exported in either a *class* (defined below) or all +functions which are **not** static. + +Writing Tests +------------- + +Test Cases +~~~~~~~~~~ + +The fundamental unit in KUnit is the test case. A test case is a function with +the signature ``void (*)(struct test *test)``. It calls a function to be tested +and then sets *expectations* for what should happen. For example: + +.. code-block:: c + + void example_test_success(struct test *test) + { + } + + void example_test_failure(struct test *test) + { + FAIL(test, "This test never passes."); + } + +In the above example ``example_test_success`` always passes because it does +nothing; no expectations are set, so all expectations pass. On the other hand +``example_test_failure`` always fails because it calls ``FAIL``, which is a +special expectation that logs a message and causes the test case to fail. + +Preferably, a single test case will be pretty short, it should be pretty easy to +understand, and it should really only try to test at most a handful of very +closely relating things. + +Expectations +~~~~~~~~~~~~ +An *expectation* is a way to make an assertion about what a piece of code should +do in a test. An expectation is called like a function. A test is made by +setting expectations about the behavior of a piece of code under test; when one +or more of the expectations fail, the test case fails and information about the +failure is logged. For example: + +.. code-block:: c + + void add_test_basic(struct test *test) + { + EXPECT_EQ(test, 1, add(1, 0)); + EXPECT_EQ(test, 2, add(1, 1)); + EXPECT_EQ(test, 0, add(-1, 1)); + EXPECT_EQ(test, INT_MAX, add(0, INT_MAX)); + EXPECT_EQ(test, -1, add(INT_MAX, INT_MIN)); + } + +In the above example ``add_test_basic`` makes a number of assertions about the +behavior of a function called ``add``; the first parameter is always of type +``struct test *``, which contains information about the current test context; the +second parameter, in this case, is what the value is expected to be; the last +value is what the value actually is. If ``add`` passes all of these expectations, +the test case, ``add_test_basic`` will pass; if any one of these expectations +fail, the test case will fail. + +To learn about more expectations supported by KUnit, see :doc:`api/test`. + +Assertions +~~~~~~~~~~ + +KUnit also has the concept of an *assertion*. An assertion is just like an +expectation except the assertion immediately terminates the test case if it is +not satisfied. + +For example: + +.. code-block:: c + + static void mock_test_do_expect_default_return(struct test *test) + { + struct mock_test_context *ctx = test->priv; + struct mock *mock = ctx->mock; + int param0 = 5, param1 = -5; + const char *two_param_types[] = {"int", "int"}; + const void *two_params[] = {¶m0, ¶m1}; + const void *ret; + + ret = mock->do_expect(mock, + "test_printk", test_printk, + two_param_types, two_params, + ARRAY_SIZE(two_params)); + ASSERT_NOT_ERR_OR_NULL(test, ret); + EXPECT_EQ(test, -4, *((int *) ret)); + } + +In this example, the method under test should return a pointer to a value, so if +the pointer returned by the method is null or an errno, we don't want to bother +continuing the test since the following expectation could crash the test case +if the pointer is null. `ASSERT_NOT_ERR_OR_NULL(...)` allows us to bail out of +the test case if the appropriate conditions have not been satisfied to complete +the test. + +Modules / Test Suites +~~~~~~~~~~~~~~~~~~~~~ + +Because test cases are supposed to test just a single function and then only +test a set of closely related concepts about that function, multiple test cases +are usually needed to fully test a unit. This is where the concept of a *test +suite* or a *module* comes in; it is just a collection of test cases for a unit +of code. In addition to specifying a set of test cases, a test suite also +supports specifying setup and tear down functions which allow functions to be +specified to run before and after each test case, respectively; this is useful +when the procedure to setup a unit for testing requires several steps and needs +to be done in every test case. + +Example: + +.. code-block:: c + + static struct test_case example_test_cases[] = { + TEST_CASE(example_test_foo), + TEST_CASE(example_test_bar), + TEST_CASE(example_test_baz), + {}, + }; + + static struct test_module example_test_module[] = { + .name = "example", + .init = example_test_init, + .exit = example_test_exit, + .test_cases = example_test_cases, + }; + module_test(example_test_module); + +In the above example the test suite, ``example_test_module``, would run the test +cases ``example_test_foo``, ``example_test_bar``, and ``example_test_baz``, each +would have ``example_test_init`` called immediately before it and would have +``example_test_exit`` called immediately after it. +``module_test(example_test_module)`` registers the test suite with the KUnit +test framework. + +.. note:: + A test case will only be run if it is associated with a test suite. + +For a more information on these types of things see the :doc:`api/test`. + +Mocking +======= + +The most important aspect of unit testing that other forms of testing do not +provide is the ability to limit the amount of code under test to a single unit. +In practice, this is only possible by being able to control what code gets run +when the unit under test calls a function and this is usually accomplished +through some sort of indirection where a function is exposed as part of an API +such that the definition of that function can be changed without affecting the +rest of the code base. In the kernel this primarily comes from two constructs, +classes, structs that contain function pointers that are provided by the +implementer, and architecture specific functions which have definitions selected +at compile time. + +Classes +------- + +Classes are not a construct that is built into the C programming language; +however, it is an easily derived concept. Accordingly, pretty much every project +that does not use a standardized object oriented library (like GNOME's GObject) +has their own slightly different way of doing object oriented programming; the +Linux kernel is no exception. + +The central concept in the kernel object oriented programming is the class. In +the kernel, a *class* is a struct that contains function pointers. This creates +a contract between *implementers* and *users* since it forces them to use the +same function signature without having to call the function directly. In order +for it to truly be a class, the function pointers must specify that a pointer to +the class, known as a *class handle*, be one of the parameters; this makes it +possible for the member functions (also known as *methods*) to have access to +member variables (more commonly known as *fields*) allowing the same +implementation to have multiple *instances*. + +Typically a class can be *overridden* by *child classes* by embedding the +*parent class* in the child class. Then when a method provided by the child +class is called, the child implementation knows that the pointer passed to it is +of a parent contained within the child; because of this, the child can compute +the pointer to itself because the pointer to the parent is always a fixed offset +from the pointer to the child; this offset is the offset of the parent contained +in the child struct. For example: + +.. code-block:: c + + struct shape { + int (*area)(struct shape *this); + }; + + struct rectangle { + struct shape parent; + int length; + int width; + }; + + int rectangle_area(struct shape *this) + { + struct rectangle *self = container_of(this, struct shape, parent); + + return self->length * self->width; + }; + + void rectangle_new(struct rectangle *self, int length, int width) + { + self->parent.area = rectangle_area; + self->length = length; + self->width = width; + } + +In this example (as in most kernel code) the operation of computing the pointer +to the child from the pointer to the parent is done by ``container_of``. + +Mocking Classes +~~~~~~~~~~~~~~~ + +In order to unit test a piece of code that calls a method in a class, the +behavior of the method must be controllable, otherwise the test ceases to be a +unit test and becomes an integration test. KUnit allows classes to be *mocked* +which means that it generates subclasses whose behavior can be specified in a +test case. KUnit accomplishes this with two sets of macros: the mock generation +macros and the ``EXPECT_CALL`` macro. + +For example, let's say you have a file named ``drivers/foo/example.c`` and it +contains the following code you would like to test: + +.. code-block:: c + + int example_bar(struct example *example) + { + if (example->foo(example, 5)) + return -EIO; + else + return 0; + } + +For the purposes of this example, assume ``struct example`` is defined as such: + +.. code-block:: c + + struct example { + int (*foo)(struct example *, int); + }; + +You would create a test file named ``drivers/foo/example-test.c`` and it would +contain the following code: + +.. code-block:: c + + /* Define the mock. */ + + DECLARE_STRUCT_CLASS_MOCK_PREREQS(example); + + DEFINE_STRUCT_CLASS_MOCK(METHOD(foo), CLASS(example), + RETURNS(int), + PARAMS(struct example *, int)); + + static int example_init(struct MOCK(example) *mock_example) + { + struct example *example = mock_get_trgt(mock_example); + + example->foo = foo; + return 0; + } + + DEFINE_STRUCT_CLASS_MOCK_INIT(example, example_init); + + /* Define the test cases. */ + + static void foo_example_test_success(struct test *test) + { + struct MOCK(example) *mock_example = test->priv; + struct example *example = mock_get_trgt(mock_example); + struct mock_expectation *handle; + + handle = EXPECT_CALL(foo(mock_get_ctrl(mock_example), int_eq(test, 5))); + handle->action = int_return(test, 0); + + EXPECT_EQ(test, 0, example_bar(example)); + } + + static void foo_example_test_failure(struct test *test) + { + struct MOCK(example) *mock_example = test->priv; + struct example *example = mock_get_trgt(mock_example); + struct mock_expectation *handle; + + handle = EXPECT_CALL(foo(mock_get_ctrl(mock_example), int_eq(test, 5))); + handle->action = int_return(test, -EINVAL); + EXPECT_EQ(test, -EINVAL, example_bar(example)); + } + + static int example_test_init(struct test *test) + { + test->priv = CONSTRUCT_MOCK(example, test); + if (!test->priv) + return -EINVAL; + + return 0; + } + + static void example_test_exit(struct test *test) + { + } + + static struct test_case foo_example_test_cases[] = { + TEST_CASE(foo_example_test_success), + TEST_CASE(foo_example_test_failure), + {}, + }; + + static struct test_module foo_example_test_module = { + .name = "example", + .init = example_test_init, + .exit = example_test_exit, + .test_cases = foo_example_test_cases, + }; + module_test(foo_example_test_module); + +``foo_example_test_success`` uses the mock allocated in init. It asserts that +``example.foo`` will get called with ``5`` as a parameter with the ``int_eq`` +parameter matcher. ``EXPECT_CALL`` the returns a handle that a user can use to +specify additional behavior on the mock; it must always specify a return value +using an *action*. Finally, it calls the function under test. + +For more information on class mocking see :doc:`api/class-and-function-mocking`. + +Mocking Arbitrary Functions +--------------------------- + +.. important:: + Always prefer class mocking over arbitrary function mocking where possible. + Class mocking has a much more limited scope and provides more control. + +Sometimes it is necessary to mock a function that does not use any class style +indirection. First and foremost, if you encounter this in your own code, please +rewrite it so that uses class style indirection discussed above, but if this is +in some code that is outside of your control you may use KUnit's function +mocking features. + +KUnit provides macros to allow arbitrary functions to be overridden so that the +original definition is replaced with a mock stub. For most functions, all you +have to do is label the function ``__mockable``: + +.. code-block:: c + + int __mockable example(int arg) {...} + +If a function is ``__mockable`` and a mock is defined: + +.. code-block:: c + + DEFINE_FUNCTION_MOCK(example, RETURNS(int), PARAMS(int)); + +When the function is called, the mock stub will actually be called. + +.. note:: + There is no performance penalty or potential side effects from doing this. + When not compiling for testing, ``__mockable`` compiles away. + +.. note:: + ``__mockable`` does not work on inlined functions. + +Redirect-mockable +~~~~~~~~~~~~~~~~~ + +Sometimes it is desirable to have a mock function that delegates to the original +definition in some or all circumstances. This is possible by making the function +*redirect-mockable*: + +.. code-block:: c + + DEFINE_REDIRECT_MOCKABLE(i2c_add_adapter, RETURNS(int), PARAMS(struct i2c_adapter *)); + int REAL_ID(i2c_add_adapter)(struct i2c_adapter *adapter) + { + ... + } + +This allows the function to be overridden by a mock as with ``__mockable``; +however, it associates the original definition of the function with alternate +symbol that KUnit can still reference. This makes it possible to mock the +function and then have the mock delegate to the original function definition +with the ``INVOKE_REAL(...)`` action: + +.. code-block:: c + + static int aspeed_i2c_test_init(struct test *test) + { + struct mock_param_capturer *adap_capturer; + struct mock_expectation *handle; + struct aspeed_i2c_test *ctx; + int ret; + + ctx = test_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + test->priv = ctx; + + handle = EXPECT_CALL( + i2c_add_adapter(capturer_to_matcher(adap_capturer))); + handle->action = INVOKE_REAL(test, i2c_add_adapter); + ret = of_fake_probe_platform_by_name(test, + "aspeed-i2c-bus", + "test-i2c-bus"); + if (ret < 0) + return ret; + + ASSERT_PARAM_CAPTURED(test, adap_capturer); + ctx->adap = mock_capturer_get(adap_capturer, struct i2c_adapter *); + + return 0; + } + +For more information on function mocking see +:doc:`api/class-and-function-mocking`. + +Platform Mocking +---------------- +The Linux kernel generally forbids normal code from accessing architecture +specific features. Instead, low level hardware features are usually abstracted +so that architecture specific code can live in the ``arch/`` directory and all +other code relies on APIs exposed by it. + +KUnit provides a mock architecture that currently allows mocking basic IO memory +accessors and in the future will provide even more. A major use case for +platform mocking is unit testing platform drivers, so KUnit also provides +helpers for this as well. + +In order to use platform mocking, ``CONFIG_PLATFORM_MOCK`` must be enabled in +your ``kunitconfig``. + +For more information on platform mocking see :doc:`api/platform-mocking`. + +Method Call Expectations +======================== +Once we have classes and methods mocked, we can place more advanced +expectations. Previously, we could only place expectations on simple return +values. With the :c:func:`EXPECT_CALL` macro, which allows you to make +assertions that a certain mocked function is called with specific arguments +given some code to be run. + +Basic Usage +----------- +Imagine we had some kind of dependency like this: + +.. code-block:: c + + struct Printer { + void (*print)(int arg); + }; + + // Printer's print + void printer_print(int arg) + { + do_something_to_print_to_screen(arg); + } + + struct Foo { + struct Printer *internal_printer; + void (*print_add_two)(struct Foo*, int); + }; + + // Foo's print_add_two: + void foo_print_add_two(struct Foo *this, int arg) + { + internal_printer->print(arg + 2); + } + +and we wanted to test ``struct Foo``'s behaviour, that ``foo->print_add_two`` +actually adds 2 to the argument passed. To properly unit test this, we create +mocks for all of ``struct Foo``'s dependencies, like ``struct Printer``. +We first setup stubs for ``MOCK(Printer)`` and its ``print`` function. + +In the real code, we'd assign a real ``struct Printer`` to the +``internal_printer`` variable in our ``struct Foo`` object, but in the +test, we'd construct a ``struct Foo`` with our ``MOCK(Printer)``. + +Finally, we can place expectations on the ``MOCK(Printer)``. + +For example: + +.. code-block:: c + + static int test_foo_add_two(struct test *test) + { + struct MOCK(Printer) *mock_printer = get_mocked_printer(); + struct Foo *foo = initialize_foo(mock_printer); + + // print() is a mocked method stub + EXPECT_CALL(print(any(test), int_eq(12))); + + foo->print_add_two(foo, 10); + } + +Here, we expect that the printer's print function will be called (by default, +once), and that it will be called with the argument ``12``. Once we've placed +expectations, we can call the function we want to test to see that it behaves +as we expected. + +Matchers +-------- +Above, we see ``any`` and ``int_eq``, which are matchers. A matcher simply +asserts that the argument passed to that function call fulfills some condition. +In this case, ``any()`` matches any argument, and ``int_eq(12)`` asserts that +the argument passed to that function must equal 12. If we had called: +``foo->print_add_two(foo, 9)`` instead, the expectation would not have been +fulfilled. There are a variety of built-in matchers: +:doc:`api/class-and-function-mocking` has a section about these matchers. + +.. note:: + :c:func:`EXPECT_CALL` only works with mocked functions and methods. + Matchers may only be used within the function inside the + :c:func:`EXPECT_CALL`. + +Additional :c:func:`EXPECT_CALL` Properties +------------------------------------------- + +The return value of :c:func:`EXPECT_CALL` is a +:c:func:`struct mock_expectation`. We can capture the value and add extra +properties to it as defined by the :c:func:`struct mock_expectation` interface. + +Times Called +~~~~~~~~~~~~ +In the previous example, if we wanted assert that the method is never called, +we could write: + +.. code-block:: c + + ... + struct mock_expectation* handle = EXPECT_CALL(...); + handle->min_calls_expected = 0; + handle->max_calls_expected = 0; + ... + +Both those fields are set to 1 by default and can be changed to assert a range +of times that the method or function is called. + +Mocked Actions +~~~~~~~~~~~~~~ +Because ``mock_printer`` is a mock, it doesn't actually perform any task. If +the function had some side effect that ``struct Foo`` requires to have been +done, such as modifying some state, we could mock that as well. + +Each expectation has an associated :c:func:`struct mock_action` which can be +set with ``handle->action``. By default, there are two actions that mock return +values. Those can also be found in :doc:`api/class-and-function-mocking`. + +Custom actions can be defined by simply creating a :c:func:`struct mock_action` +and assigning the appropriate function to ``do_action``. Mocked actions have +access to the parameters passed to the mocked function, as well as have the +ability to change / set the return value. + + +The Nice, the Strict, and the Naggy +=================================== +KUnit has three different mock types that can be set on a mocked class: nice +mocks, strict mocks, and naggy mocks. These are set via the corresponding macros +:c:func:`NICE_MOCK`, :c:func:`STRICT_MOCK`, and :c:func:`NAGGY_MOCK`, with naggy +mocks being the default. + +The type of mock simply dictates the behaviour the mock exhibits when +expectations are placed on it. + ++-----------------------+------------+--------------------+--------------------+ +| | **Nice** | **Naggy (default)**| **Strict** | ++-----------------------+------------+--------------------+--------------------+ +| Method called with no | Do nothing | Prints warning for | Fails test, prints | +| expectations on it | | uninteresting call | warning | +| | | | uninteresting call | ++-----------------------+------------+--------------------+--------------------+ +| Method called with no | Fails test, prints warnings, prints tried | +| matching expectations | expectations | +| on it | | ++-----------------------+------------------------------------------------------+ +| Test ends with an | Fail test, print warning | +| unfulfilled | | +| expectation | | ++-----------------------+------------------------------------------------------+ + +These macros take a ``MOCK(struct_name)`` and so should be used when retrieving +the mocked object. Following the example in :doc:`start`, there was this test +case: + +.. code-block:: c + + static void misc_example_bar_test_success(struct test *test) + { + struct MOCK(misc_example) *mock_example = test->priv; + struct misc_example *example = mock_get_trgt(mock_example); + struct mock_expectation *handle; + + handle = EXPECT_CALL(misc_example_foo(mock_get_ctrl(mock_example), + int_eq(test, 5))); + handle->action = int_return(test, 0); + + EXPECT_EQ(test, 0, misc_example_bar(example)); + } + +If we wanted ``mock_example`` to be a nice mock instead, we would simply write: + +.. code-block:: c + + struct MOCK(misc_example) *mock_example = NICE_MOCK(test->priv); \ No newline at end of file