On Thu, Dec 08, 2022 at 02:18:41PM +0800, David Gow wrote: > From: Sadiya Kazi <sadiyakazi@xxxxxxxxxx> > > 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. s/Added/Add > diff --git a/Documentation/dev-tools/kunit/api/functionredirection.rst b/Documentation/dev-tools/kunit/api/functionredirection.rst > new file mode 100644 > index 000000000000..fc7644dfea65 > --- /dev/null > +++ b/Documentation/dev-tools/kunit/api/functionredirection.rst > @@ -0,0 +1,162 @@ > +.. 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). Test reliability is test independence, right? > + > +While for some code (typically generic data structures, helpers, and toher > +"pure function") this is trivial, for others (like device drivers, filesystems, > +core subsystems) the code is heavily coupled with other parts of the kernel. > + > +This often involves global state in some way: be it global lists of devices, > +the filesystem, or hardware state, this needs to be either carefully managed, > +isolated, and restored, or avoided altogether by replacing access to and > +mutation of this state with a "fake" or "mock" variant. "... or hardware state; this needs ..." > + > +This can be done by refactoring the code to abstract out access to such state, > +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 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, the redirection can be disabled (and hence the > + original behaviour of the 'real' function resumed) using > + kunit_deactivate_static_stub(). If the stub is not manually deactivated, it > + will nevertheless be disabled when the test finishes. > + > + 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) > + { > + /* fake implementation */ > + 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: > diff --git a/Documentation/dev-tools/kunit/api/index.rst b/Documentation/dev-tools/kunit/api/index.rst > index 45ce04823f9f..2d8f756aab56 100644 > --- a/Documentation/dev-tools/kunit/api/index.rst > +++ b/Documentation/dev-tools/kunit/api/index.rst > @@ -4,17 +4,24 @@ > API Reference > ============= > .. toctree:: > + :hidden: > > test > resource > + functionredirection > > -This section documents the KUnit kernel testing API. It is divided into the > + > +This page documents the KUnit kernel testing API. It is divided into the > following sections: > > Documentation/dev-tools/kunit/api/test.rst > > - - documents all of the standard testing API > + - Documents all of the standard testing API > > Documentation/dev-tools/kunit/api/resource.rst > > - - documents the KUnit resource API > + - Documents the KUnit resource API > + > +Documentation/dev-tools/kunit/api/functionredirection.rst > + > + - Documents the KUnit Function Redirection API Otherwise LGTM. -- An old man doll... just what I always wanted! - Clara
Attachment:
signature.asc
Description: PGP signature