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