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