| .. SPDX-License-Identifier: GPL-2.0 | |
| ======================== | |
| Function Redirection API | |
| ======================== | |
| Overview | |
| ======== | |
| When writing unit tests, it's important to be able to isolate the code being | |
| tested from other parts of the kernel. This ensures the reliability of the test | |
| (it won't be affected by external factors), reduces dependencies on specific | |
| hardware or config options (making the test easier to run), and protects the | |
| stability of the rest of the system (making it less likely for test-specific | |
| state to interfere with the rest of the system). | |
| While for some code (typically generic data structures, helpers, and other | |
| "pure functions") this is trivial, for others (like device drivers, | |
| filesystems, core subsystems) the code is heavily coupled with other parts of | |
| the kernel. | |
| This coupling is often due to global state in some way: be it a global list of | |
| devices, the filesystem, or some hardware state. Tests need to either carefully | |
| manage, isolate, and restore state, or they can avoid it altogether by | |
| replacing access to and mutation of this state with a "fake" or "mock" variant. | |
| By refactoring access to such state, such as by introducing a layer of | |
| indirection which can use or emulate a separate set of test state. However, | |
| such refactoring comes with its own costs (and undertaking significant | |
| refactoring before being able to write tests is suboptimal). | |
| A simpler way to intercept and replace some of the function calls is to use | |
| function redirection via static stubs. | |
| Static Stubs | |
| ============ | |
| Static stubs are a way of redirecting calls to one function (the "real" | |
| function) to another function (the "replacement" function). | |
| It works by adding a macro to the "real" function which checks to see if a test | |
| is running, and if a replacement function is available. If so, that function is | |
| called in place of the original. | |
| Using static stubs is pretty straightforward: | |
| 1. Add the KUNIT_STATIC_STUB_REDIRECT() macro to the start of the "real" | |
| function. | |
| This should be the first statement in the function, after any variable | |
| declarations. KUNIT_STATIC_STUB_REDIRECT() takes the name of the | |
| function, followed by all of the arguments passed to the real function. | |
| For example: | |
| .. code-block:: c | |
| void send_data_to_hardware(const char *str) | |
| { | |
| KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str); | |
| /* real implementation */ | |
| } | |
| 2. Write one or more replacement functions. | |
| These functions should have the same function signature as the real function. | |
| In the event they need to access or modify test-specific state, they can use | |
| kunit_get_current_test() to get a struct kunit pointer. This can then | |
| be passed to the expectation/assertion macros, or used to look up KUnit | |
| resources. | |
| For example: | |
| .. code-block:: c | |
| void fake_send_data_to_hardware(const char *str) | |
| { | |
| struct kunit *test = kunit_get_current_test(); | |
| KUNIT_EXPECT_STREQ(test, str, "Hello World!"); | |
| } | |
| 3. Activate the static stub from your test. | |
| From within a test, the redirection can be enabled with | |
| kunit_activate_static_stub(), which accepts a struct kunit pointer, | |
| the real function, and the replacement function. You can call this several | |
| times with different replacement functions to swap out implementations of the | |
| function. | |
| In our example, this would be | |
| .. code-block:: c | |
| kunit_activate_static_stub(test, | |
| send_data_to_hardware, | |
| fake_send_data_to_hardware); | |
| 4. Call (perhaps indirectly) the real function. | |
| Once the redirection is activated, any call to the real function will call | |
| the replacement function instead. Such calls may be buried deep in the | |
| implementation of another function, but must occur from the test's kthread. | |
| For example: | |
| .. code-block:: c | |
| send_data_to_hardware("Hello World!"); /* Succeeds */ | |
| send_data_to_hardware("Something else"); /* Fails the test. */ | |
| 5. (Optionally) disable the stub. | |
| When you no longer need it, disable the redirection (and hence resume the | |
| original behaviour of the 'real' function) using | |
| kunit_deactivate_static_stub(). Otherwise, it will be automatically disabled | |
| when the test exits. | |
| For example: | |
| .. code-block:: c | |
| kunit_deactivate_static_stub(test, send_data_to_hardware); | |
| It's also possible to use these replacement functions to test to see if a | |
| function is called at all, for example: | |
| .. code-block:: c | |
| void send_data_to_hardware(const char *str) | |
| { | |
| KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str); | |
| /* real implementation */ | |
| } | |
| /* In test file */ | |
| int times_called = 0; | |
| void fake_send_data_to_hardware(const char *str) | |
| { | |
| times_called++; | |
| } | |
| ... | |
| /* In the test case, redirect calls for the duration of the test */ | |
| kunit_activate_static_stub(test, send_data_to_hardware, fake_send_data_to_hardware); | |
| send_data_to_hardware("hello"); | |
| KUNIT_EXPECT_EQ(test, times_called, 1); | |
| /* Can also deactivate the stub early, if wanted */ | |
| kunit_deactivate_static_stub(test, send_data_to_hardware); | |
| send_data_to_hardware("hello again"); | |
| KUNIT_EXPECT_EQ(test, times_called, 1); | |
| API Reference | |
| ============= | |
| .. kernel-doc:: include/kunit/static_stub.h | |
| :internal: |