Skip to content

Commit 9ecc9cd

Browse files
sadiyakazi1shuahkh
authored andcommitted
Documentation: Add Function Redirection API docs
Added a new page (functionredirection.rst) that describes the Function Redirection (static stubbing) API. This page will be expanded if we add, for example, ftrace-based stubbing. In addition, 1. Updated the api/index.rst page to create an entry for function redirection api 2. Updated the toctree to be hidden, reducing redundancy on the generated page. Signed-off-by: Sadiya Kazi <[email protected]> Co-developed-by: Daniel Latypov <[email protected]> Signed-off-by: Daniel Latypov <[email protected]> Co-developed-by: David Gow <[email protected]> Signed-off-by: David Gow <[email protected]> Reviewed-by: Brendan Higgins <[email protected]> Signed-off-by: Shuah Khan <[email protected]>
1 parent e047c5e commit 9ecc9cd

File tree

2 files changed

+172
-3
lines changed

2 files changed

+172
-3
lines changed
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
========================
4+
Function Redirection API
5+
========================
6+
7+
Overview
8+
========
9+
10+
When writing unit tests, it's important to be able to isolate the code being
11+
tested from other parts of the kernel. This ensures the reliability of the test
12+
(it won't be affected by external factors), reduces dependencies on specific
13+
hardware or config options (making the test easier to run), and protects the
14+
stability of the rest of the system (making it less likely for test-specific
15+
state to interfere with the rest of the system).
16+
17+
While for some code (typically generic data structures, helpers, and other
18+
"pure functions") this is trivial, for others (like device drivers,
19+
filesystems, core subsystems) the code is heavily coupled with other parts of
20+
the kernel.
21+
22+
This coupling is often due to global state in some way: be it a global list of
23+
devices, the filesystem, or some hardware state. Tests need to either carefully
24+
manage, isolate, and restore state, or they can avoid it altogether by
25+
replacing access to and mutation of this state with a "fake" or "mock" variant.
26+
27+
By refactoring access to such state, such as by introducing a layer of
28+
indirection which can use or emulate a separate set of test state. However,
29+
such refactoring comes with its own costs (and undertaking significant
30+
refactoring before being able to write tests is suboptimal).
31+
32+
A simpler way to intercept and replace some of the function calls is to use
33+
function redirection via static stubs.
34+
35+
36+
Static Stubs
37+
============
38+
39+
Static stubs are a way of redirecting calls to one function (the "real"
40+
function) to another function (the "replacement" function).
41+
42+
It works by adding a macro to the "real" function which checks to see if a test
43+
is running, and if a replacement function is available. If so, that function is
44+
called in place of the original.
45+
46+
Using static stubs is pretty straightforward:
47+
48+
1. Add the KUNIT_STATIC_STUB_REDIRECT() macro to the start of the "real"
49+
function.
50+
51+
This should be the first statement in the function, after any variable
52+
declarations. KUNIT_STATIC_STUB_REDIRECT() takes the name of the
53+
function, followed by all of the arguments passed to the real function.
54+
55+
For example:
56+
57+
.. code-block:: c
58+
59+
void send_data_to_hardware(const char *str)
60+
{
61+
KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str);
62+
/* real implementation */
63+
}
64+
65+
2. Write one or more replacement functions.
66+
67+
These functions should have the same function signature as the real function.
68+
In the event they need to access or modify test-specific state, they can use
69+
kunit_get_current_test() to get a struct kunit pointer. This can then
70+
be passed to the expectation/assertion macros, or used to look up KUnit
71+
resources.
72+
73+
For example:
74+
75+
.. code-block:: c
76+
77+
void fake_send_data_to_hardware(const char *str)
78+
{
79+
struct kunit *test = kunit_get_current_test();
80+
KUNIT_EXPECT_STREQ(test, str, "Hello World!");
81+
}
82+
83+
3. Activate the static stub from your test.
84+
85+
From within a test, the redirection can be enabled with
86+
kunit_activate_static_stub(), which accepts a struct kunit pointer,
87+
the real function, and the replacement function. You can call this several
88+
times with different replacement functions to swap out implementations of the
89+
function.
90+
91+
In our example, this would be
92+
93+
.. code-block:: c
94+
95+
kunit_activate_static_stub(test,
96+
send_data_to_hardware,
97+
fake_send_data_to_hardware);
98+
99+
4. Call (perhaps indirectly) the real function.
100+
101+
Once the redirection is activated, any call to the real function will call
102+
the replacement function instead. Such calls may be buried deep in the
103+
implementation of another function, but must occur from the test's kthread.
104+
105+
For example:
106+
107+
.. code-block:: c
108+
109+
send_data_to_hardware("Hello World!"); /* Succeeds */
110+
send_data_to_hardware("Something else"); /* Fails the test. */
111+
112+
5. (Optionally) disable the stub.
113+
114+
When you no longer need it, disable the redirection (and hence resume the
115+
original behaviour of the 'real' function) using
116+
kunit_deactivate_static_stub(). Otherwise, it will be automatically disabled
117+
when the test exits.
118+
119+
For example:
120+
121+
.. code-block:: c
122+
123+
kunit_deactivate_static_stub(test, send_data_to_hardware);
124+
125+
126+
It's also possible to use these replacement functions to test to see if a
127+
function is called at all, for example:
128+
129+
.. code-block:: c
130+
131+
void send_data_to_hardware(const char *str)
132+
{
133+
KUNIT_STATIC_STUB_REDIRECT(send_data_to_hardware, str);
134+
/* real implementation */
135+
}
136+
137+
/* In test file */
138+
int times_called = 0;
139+
void fake_send_data_to_hardware(const char *str)
140+
{
141+
times_called++;
142+
}
143+
...
144+
/* In the test case, redirect calls for the duration of the test */
145+
kunit_activate_static_stub(test, send_data_to_hardware, fake_send_data_to_hardware);
146+
147+
send_data_to_hardware("hello");
148+
KUNIT_EXPECT_EQ(test, times_called, 1);
149+
150+
/* Can also deactivate the stub early, if wanted */
151+
kunit_deactivate_static_stub(test, send_data_to_hardware);
152+
153+
send_data_to_hardware("hello again");
154+
KUNIT_EXPECT_EQ(test, times_called, 1);
155+
156+
157+
158+
API Reference
159+
=============
160+
161+
.. kernel-doc:: include/kunit/static_stub.h
162+
:internal:

Documentation/dev-tools/kunit/api/index.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@
44
API Reference
55
=============
66
.. toctree::
7+
:hidden:
78

89
test
910
resource
11+
functionredirection
1012

11-
This section documents the KUnit kernel testing API. It is divided into the
13+
14+
This page documents the KUnit kernel testing API. It is divided into the
1215
following sections:
1316

1417
Documentation/dev-tools/kunit/api/test.rst
1518

16-
- documents all of the standard testing API
19+
- Documents all of the standard testing API
1720

1821
Documentation/dev-tools/kunit/api/resource.rst
1922

20-
- documents the KUnit resource API
23+
- Documents the KUnit resource API
24+
25+
Documentation/dev-tools/kunit/api/functionredirection.rst
26+
27+
- Documents the KUnit Function Redirection API

0 commit comments

Comments
 (0)