From nobody Wed Apr 8 06:39:08 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 527CBC433FE for ; Sun, 23 Oct 2022 13:11:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230429AbiJWNLP (ORCPT ); Sun, 23 Oct 2022 09:11:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44688 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230457AbiJWNKx (ORCPT ); Sun, 23 Oct 2022 09:10:53 -0400 Received: from mail-pl1-x635.google.com (mail-pl1-x635.google.com [IPv6:2607:f8b0:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 516B554CA0; Sun, 23 Oct 2022 06:10:20 -0700 (PDT) Received: by mail-pl1-x635.google.com with SMTP id u6so6325573plq.12; Sun, 23 Oct 2022 06:10:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=y6vwtBgk1QR+Bc4S86nB20t7mIV4s/vDKmFGd1CmmDU=; b=ETvKLwIy4N3Yxy995W9XmRZYzueJOJVA9EnXQOdgXTDCOeNOpnlfYkiMBKkGYXJ80w g02UTuvMJuuwAaSXKEvBMru1/E/NbJIqwGLNS+aFOei82FgiYKfTMNDhP46DUe2KLqLD vhQPlbWtSQeAJCP09RD72kiYUxg+d77ARPhsxUEOyDga7qEDI6UMPANOpciil2sNhJ0O kHo27NMgrfomjxh8X1/4iXMIQpejwXGX+X4ATbWZbQ3XCFoxAa6iVgdwpdRM52seCT/W DMSsZ7XVhqfErTiv8VKFDko2oP/LPPoCESr9mcP2nI6VuNjQG2yilHE4e/+gYnwBQuvT 1z8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=y6vwtBgk1QR+Bc4S86nB20t7mIV4s/vDKmFGd1CmmDU=; b=gOSrmY0jtcLJxmIF+ntkBfDjSuA166hGI0jS7mia0B7UU9V8sjg+zB0hSk0H3WwOkx 6K6aDGhTutr0aIqW67lpDMsjC9DlixmkijLLafZFNq6dCNHXQIozn3i286vUJQ79jlhF XRo1OVzZhMWtUwfW+3B1JbjXKtMLUBB/uJ6332mdx2kCZDt8KQ8mDO4/WW2m+7/gTExN CXxvpmoVKrAfRHIRGdpCyx+Rt03QUAdDQ+DGpaUa1MuOMzKaBqJPirMdbP0ygjAOFJQk shsuN+eJK1kc1qQbyUDUm1IXtNsPEVluA/3t56uUqQ3nsUu7d0TAF7dOwVPf7Ton/8q6 tcVw== X-Gm-Message-State: ACrzQf3O6XWSTcqslqKioArRcHhK1/PphKMtFtrTFBee7jlc+lhdvjr7 aT1rvS7kzien8xR4ZLDBIRs2AemROqDfeA== X-Google-Smtp-Source: AMsMyM5Fcj2aCw3lqiw6gaHuH68OFT+QSgGv4mIWZTCw1nZnDUd3WyM4QDJr3oYKXsy8IhQXm5yxnw== X-Received: by 2002:a17:903:40d2:b0:186:6f1d:608c with SMTP id t18-20020a17090340d200b001866f1d608cmr16019240pld.52.1666530618258; Sun, 23 Oct 2022 06:10:18 -0700 (PDT) Received: from debian.. (subs02-180-214-232-1.three.co.id. [180.214.232.1]) by smtp.gmail.com with ESMTPSA id d5-20020a623605000000b0056b6c7a17c6sm3411713pfa.12.2022.10.23.06.10.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 23 Oct 2022 06:10:17 -0700 (PDT) From: Bagas Sanjaya To: linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com Cc: Jonathan Corbet , Brendan Higgins , David Gow , Lukas Bulwahn , Khalid Masum , Sadiya Kazi , Bagas Sanjaya Subject: [PATCH RESEND 7/7] Documentation: kunit: rewrite "Writing tests" Date: Sun, 23 Oct 2022 20:08:47 +0700 Message-Id: <20221023130846.63296-8-bagasdotme@gmail.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221023130846.63296-1-bagasdotme@gmail.com> References: <20221023130846.63296-1-bagasdotme@gmail.com> MIME-Version: 1.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=27186; i=bagasdotme@gmail.com; h=from:subject; bh=ZBMrD2zTAwPNW4BI+VkpvBw6PlGVuQCho52Kn8PDVZU=; b=owGbwMvMwCH2bWenZ2ig32LG02pJDMmhNndSW7ZLHjMW7bnTpL31SLnX0VW3BdbLumXaf10vpsvA X8HRUcrCIMbBICumyDIpka/p9C4jkQvtax1h5rAygQxh4OIUgIkc/MbIcPUkm9HcirkmXqeK+SQ78y fzLLE5fTKg/t2U6xWmRgxfJzH8lVU3X/2YyzhrdtvMtngdcamFre+XbXnJlf/z0cS+TvHpzAA= X-Developer-Key: i=bagasdotme@gmail.com; a=openpgp; fpr=701B806FDCA5D3A58FFB8F7D7C276C64A5E44A1D Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Rewrite the documentation for clarity. Major points: * Switch to third person point of view * Briefly describe code examples before giving them out * Use "base" and "derived" terminology on class inheritance Signed-off-by: Bagas Sanjaya --- Documentation/dev-tools/kunit/usage.rst | 322 +++++++++++++----------- 1 file changed, 173 insertions(+), 149 deletions(-) diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-to= ols/kunit/usage.rst index 2737863ef36532..e529da3fd1d32b 100644 --- a/Documentation/dev-tools/kunit/usage.rst +++ b/Documentation/dev-tools/kunit/usage.rst @@ -6,9 +6,11 @@ Writing Tests Test Cases ---------- =20 -The fundamental unit in KUnit is the test case. A test case is a function = with -the signature ``void (*)(struct kunit *test)``. It calls the function unde= r test -and then sets *expectations* for what should happen. For example: +The fundamental unit in KUnit is the test case. A test case in KUnit is a +function with the signature ``void (*)(struct kunit *test)``. It calls the +function under test and then sets *expectations* for what should happen. + +Below are the simplest examples: =20 .. code-block:: c =20 @@ -22,18 +24,17 @@ and then sets *expectations* for what should happen. Fo= r example: } =20 In the above example, ``example_test_success`` always passes because it do= es -nothing; no expectations are set, and therefore all expectations pass. On = the -other hand ``example_test_failure`` always fails because it calls ``KUNIT_= FAIL``, -which is a special expectation that logs a message and causes the test cas= e to -fail. +nothing (there are no expectations set). On the +other hand ``example_test_failure`` always fails because it calls +``KUNIT_FAIL``, which is a special function that logs the message string a= nd +signal the test as failed. =20 Expectations ~~~~~~~~~~~~ -An *expectation* specifies that we expect a piece of code to do something = in a -test. An expectation is called like a function. A test is made by setting -expectations about the behavior of a piece of code under test. When one or= more -expectations fail, the test case fails and information about the failure is -logged. For example: +An *expectation* specifies the expected behavior of tested function. It is +written as regular function call. A test is made by asserting one or more +expectations in the test case. When any of these is not satisfied, the test +case fails and information about the failure is logged. For example: =20 .. code-block:: c =20 @@ -43,18 +44,16 @@ logged. For example: KUNIT_EXPECT_EQ(test, 2, add(1, 1)); } =20 -In the above example, ``add_test_basic`` makes a number of assertions abou= t the -behavior of a function called ``add``. The first parameter is always of ty= pe +In the above example, ``add_test_basic`` tests a function called ``add()``. +The first parameter to ``KUNIT_EXPECT_EQ`` is always of type ``struct kunit *``, which contains information about the current test cont= ext. -The second parameter, in this case, is what the value is expected to be. T= he -last value is what the value actually is. If ``add`` passes all of these -expectations, the test case, ``add_test_basic`` will pass; if any one of t= hese -expectations fails, the test case will fail. +The second parameter is what the value is expected to be returned by the +function. The last is value returned by calling the tested function. =20 -A test case *fails* when any expectation is violated; however, the test wi= ll -continue to run, and try other expectations until the test case ends or is -otherwise terminated. This is as opposed to *assertions* which are discuss= ed -later. +Even though a single expectation is not satisfied, the test will +continue to run and try asserting the following expectations until either +the test case ends or is terminated. This is as opposed to *assertions* wh= ich +are discussed later. =20 To learn about more KUnit expectations, see Documentation/dev-tools/kunit/= api/test.rst. =20 @@ -62,9 +61,8 @@ To learn about more KUnit expectations, see Documentation= /dev-tools/kunit/api/te A single test case should be short, easy to understand, and focused on a single behavior. =20 -For example, if we want to rigorously test the ``add`` function above, cre= ate -additional tests cases which would test each property that an ``add`` func= tion -should have as shown below: +The example below extends ``add()`` tests with cases for negative values +and edge cases involving ``INT_MIN`` and ``INT_MAX`` constants: =20 .. code-block:: c =20 @@ -93,8 +91,10 @@ should have as shown below: Assertions ~~~~~~~~~~ =20 -An assertion is like an expectation, except that the assertion immediately -terminates the test case if the condition is not satisfied. For example: +An assertion is like an expectation, except that it immediately +terminates the test case if the condition is false. + +The following example demonstrate testing ``sort()`` function: =20 .. code-block:: c =20 @@ -112,21 +112,22 @@ terminates the test case if the condition is not sati= sfied. For example: KUNIT_EXPECT_LE(test, a[i], a[i + 1]); } =20 -In this example, the method under test should return pointer to a value. I= f the -pointer returns null or an errno, we want to stop the test since the follo= wing -expectation could crash the test case. `ASSERT_NOT_ERR_OR_NULL(...)` allow= s us -to bail out of the test case if the appropriate conditions are not satisfi= ed to -complete the test. +In this example, the method under test should return pointer to a value. If +it instead returns null pointer or errno, the test case should be bailed o= ut +with ``KUNIT_ASSERT_NOT_ERR_OR_NULL()`` since the following expectation th= at +asserts the array order could crash it. =20 Test Suites ~~~~~~~~~~~ =20 -We need many test cases covering all the unit's behaviors. It is common to= have -many similar tests. In order to reduce duplication in these closely related -tests, most unit testing frameworks (including KUnit) provide the concept = of a -*test suite*. A test suite is a collection of test cases for a unit of code -with optional setup and teardown functions that run before/after the whole -suite and/or every test case. For example: +It is common to have many similar tests cases in order to test the code +behavior. In order to reduce duplication in these closely related +tests, most unit testing frameworks (including KUnit) provide the *test su= ite* +concept. A test suite is a collection of test cases for a particularcode +with optional setup and cleanup functions that run before/after the whole +suite and/or every test case. + +Below is an example of writing a test suite: =20 .. code-block:: c =20 @@ -147,21 +148,21 @@ suite and/or every test case. For example: }; kunit_test_suite(example_test_suite); =20 -In the above example, the test suite ``example_test_suite`` would first run -``example_suite_init``, then run the test cases ``example_test_foo``, -``example_test_bar``, and ``example_test_baz``. Each would have -``example_test_init`` called immediately before it and ``example_test_exit= `` -called immediately after it. Finally, ``example_suite_exit`` would be call= ed -after everything else. ``kunit_test_suite(example_test_suite)`` registers = the -test suite with the KUnit test framework. +In the above example, running ``example_test_suite`` will initialize the +suite with ``example_suite_init``, then run three cases ``example_test_foo= ``, +``example_test_bar``, and ``example_test_baz``. Each case will be initiali= zed +with ``example_test_init`` and ``example_test_exit`` will take care of +case-specific cleanup after the case have been finished. Finally, +``example_suite_exit`` will do suite-wide cleanup, then finish the suite. +``kunit_test_suite(example_test_suite)`` registers the test suite with KUn= it. =20 .. note:: A test case will only run if it is associated with a test suite. =20 -``kunit_test_suite(...)`` is a macro which tells the linker to put the -specified test suite in a special linker section so that it can be run by = KUnit -either after ``late_init``, or when the test module is loaded (if the test= was -built as a module). +``kunit_test_suite()`` is a macro which tells the linker to put the specif= ied +test suite in a special linker section so that it can be run either after +``late_init``, or when the test module is loaded (if the test was built as +module). =20 For more information, see Documentation/dev-tools/kunit/api/test.rst. =20 @@ -170,25 +171,23 @@ For more information, see Documentation/dev-tools/kun= it/api/test.rst. Writing Tests For Other Architectures ------------------------------------- =20 -It is better to write tests that run on UML to tests that only run under a -particular architecture. It is better to write tests that run under QEMU or -another easy to obtain (and monetarily free) software environment to a spe= cific -piece of hardware. +In many cases, it is better to write tests that can be run on UML or QEMU +without requiring any specific architecture or hardware, since these can +be practically run by any machine that is capable of cross-compiling. =20 -Nevertheless, there are still valid reasons to write a test that is archit= ecture -or hardware specific. For example, we might want to test code that really -belongs in ``arch/some-arch/*``. Even so, try to write the test so that it= does -not depend on physical hardware. Some of our test cases may not need hardw= are, -only few tests actually require the hardware to test it. When hardware is = not -available, instead of disabling tests, we can skip them. +Nevertheless, there are still valid reasons to write architecture-specific +or hardware-specific tests. For example, you might want to test codes that +are in ``arch/some-arch/*``. Even so, try to write tests so that it does +not require specific hardware to run them. A test suite may contain +hardware-specific cases. These can be skipped if the hardware is not +available. =20 -Now that we have narrowed down exactly what bits are hardware specific, the -actual procedure for writing and running the tests is same as writing norm= al -KUnit tests. +Writing architecture-specific and hardware-specific tests is the same as +writing any other tests. =20 .. important:: - We may have to reset hardware state. If this is not possible, we may on= ly - be able to run one test case per invocation. + In some cases you need to reset hardware state after each test case, + otherwise only one case can be run per test suite. =20 .. TODO(brendanhiggins@google.com): Add an actual example of an architectu= re- dependent KUnit test. @@ -200,38 +199,41 @@ Isolating Behavior ------------------ =20 Unit testing limits the amount of code under test to a single unit. It con= trols -what code gets run when the unit under test calls a function. Where a func= tion -is exposed as part of an API such that the definition of that function can= be +what code gets run when the unit under test calls a function. When a funct= ion +is exposed as part of an API, the function definition can be changed without affecting the rest of the code base. In the kernel, this c= omes from two constructs: classes, which are structs that contain function poin= ters -provided by the implementer, and architecture-specific functions, which ha= ve -definitions selected at compile time. +provided by the implementer; and architecture-specific functions, which ha= ve +definitions determined at compile time. =20 Classes ~~~~~~~ =20 -Classes are not a construct that is built into the C programming language; -however, it is an easily derived concept. Accordingly, in most cases, every -project that does not use a standardized object oriented library (like GNO= ME's -GObject) has their own slightly different way of doing object oriented -programming; the Linux kernel is no exception. +The C programming language does not have the formal notion of class-based +object-oriented programming, hovewer the paradigm can be applied within +procedural framework. Accordingly, every project has their own slightly +different way of doing object oriented programming (such as GNOME's GObjec= t); +the Linux kernel is no exception. =20 The central concept in kernel object oriented programming is the class. In= the kernel, a *class* is a struct that contains function pointers. This create= s a contract between *implementers* and *users* since it forces them to use the -same function signature without having to call the function directly. To b= e a -class, the function pointers must specify that a pointer to the class, kno= wn as -a *class handle*, be one of the parameters. Thus the member functions (also -known as *methods*) have access to member variables (also known as *fields= *) -allowing the same implementation to have multiple *instances*. +same function signature without having to call the function directly. In o= rder +to be a class, the function pointers must specify that a pointer to the cl= ass +(known as a *class handle*) be one of the parameters; thus the member func= tions +(also known as *methods*) have access to member variables (also known as +*fields*), allowing the same implementation to have multiple *instances*. =20 -A class can be *overridden* by *child classes* by embedding the *parent cl= ass* -in the child class. Then when the child class *method* is called, the child -implementation knows that the pointer passed to it is of a parent contained -within the child. Thus, the child can compute the pointer to itself becaus= e the -pointer to the parent is always a fixed offset from the pointer to the chi= ld. -This offset is the offset of the parent contained in the child struct. For -example: +A class can be *overridden* by *derived classes* by embedding the *base cl= ass* +in the derived class. Then when the dervied class *method* is called, the +derived class implementation knows that the pointer passed to it is of a b= ase +class contained within the derived class. Thus, the child can compute the +pointer to itself because the pointer to the base class is always a fixed +offset from the pointer to the derived class. This offset is the offset of= the +base class contained in the struct of derived class. + +The example below defines a base class ``shape`` and derived class +``rectangle`` along with class implementations: =20 .. code-block:: c =20 @@ -259,8 +261,8 @@ example: self->width =3D width; } =20 -In this example, computing the pointer to the child from the pointer to the -parent is done by ``container_of``. +In this example, computing the pointer to ``rectangle`` from the pointer to +the ``shape`` is taken care of by ``container_of`` method. =20 Faking Classes ~~~~~~~~~~~~~~ @@ -269,11 +271,13 @@ In order to unit test a piece of code that calls a me= thod in a class, the behavior of the method must be controllable, otherwise the test ceases to = be a unit test and becomes an integration test. =20 -A fake class implements a piece of code that is different than what runs i= n a -production instance, but behaves identical from the standpoint of the call= ers. -This is done to replace a dependency that is hard to deal with, or is slow= . For -example, implementing a fake EEPROM that stores the "contents" in an -internal buffer. Assume we have a class that represents an EEPROM: +A fake class implements a piece of code that interfaces to actual code +used in production. This is done to replace code dependencies that is hard= to +deal with (expensive or impossible to duplicate), or is slow. + +The examples below shows how to test fake EEPROM implementation that stores +its contents in an internal buffer. Assume that there is ``eeprom`` class, +which is defined as: =20 .. code-block:: c =20 @@ -282,7 +286,8 @@ internal buffer. Assume we have a class that represents= an EEPROM: ssize_t (*write)(struct eeprom *this, size_t offset, const char *buffer,= size_t count); }; =20 -And we want to test code that buffers writes to the EEPROM: +Supposes that you want to test ``eeprom_buffer`` class, which writes the +contents to actual EEPROM: =20 .. code-block:: c =20 @@ -295,7 +300,8 @@ And we want to test code that buffers writes to the EEP= ROM: struct eeprom_buffer *new_eeprom_buffer(struct eeprom *eeprom); void destroy_eeprom_buffer(struct eeprom *eeprom); =20 -We can test this code by *faking out* the underlying EEPROM: +In order to test ``eeprom_buffer``, you need *faking out* the underlying +EEPROM with ``fake_eeprom``, which is derived from ``eeprom``: =20 .. code-block:: c =20 @@ -331,7 +337,7 @@ We can test this code by *faking out* the underlying EE= PROM: memset(this->contents, 0, FAKE_EEPROM_CONTENTS_SIZE); } =20 -We can now use it to test ``struct eeprom_buffer``: +You can now use it to test ``eeprom_buffer``: =20 .. code-block:: c =20 @@ -425,11 +431,12 @@ We can now use it to test ``struct eeprom_buffer``: Testing Against Multiple Inputs ------------------------------- =20 -Testing just a few inputs is not enough to ensure that the code works corr= ectly, -for example: testing a hash function. +Sometimes in order to correctly test the code, many inputs are required. In +such cases, you can write a helper macro or function. The helper can be +called for each test input. =20 -We can write a helper macro or function. The function is called for each i= nput. -For example, to test ``sha1sum(1)``, we can write: +The following example defines ``TEST_SHA1`` helper macro for testing +:manpage:`sha1sum(1)`. The macro is called on 2 test cases: =20 .. code-block:: c =20 @@ -444,12 +451,15 @@ For example, to test ``sha1sum(1)``, we can write: Note the use of the ``_MSG`` version of ``KUNIT_EXPECT_STREQ`` to print a = more detailed error and make the assertions clearer within the helper macros. =20 -The ``_MSG`` variants are useful when the same expectation is called multi= ple -times (in a loop or helper function) and thus the line number is not enoug= h to -identify what failed, as shown below. +The ``_MSG`` variants are useful when the same expectation is asserted +multiple times (in a loop or helper function), since the line number alone= is +not enough to identify the failure, as shown below. =20 -In complicated cases, we recommend using a *table-driven test* compared to= the -helper macro variation, for example: +In complicated cases, it is recommended to use *table-driven test* pattern +instead. + +The following example does the same test as above, but the test cases are +defined in an array of struct: =20 .. code-block:: c =20 @@ -478,18 +488,14 @@ helper macro variation, for example: } =20 =20 -There is more boilerplate code involved, but it can: +There are advantages of *table-driven tests*: =20 -* be more readable when there are multiple inputs/outputs (due to field na= mes). - - * For example, see ``fs/ext4/inode-test.c``. - -* reduce duplication if test cases are shared across multiple tests. - - * For example: if we want to test ``sha256sum``, we could add a ``sha256= `` - field and reuse ``cases``. - -* be converted to a "parameterized test". +* The test is more readable when there are many inputs and expected output= s. + See ``fs/ext4/inode-test.c`` for example. +* It can reduce duplication if test cases are shared across multiple tests. + For example, if you want to also test :manpage:`sha256sum(1)`, you can + simply add ``sha256`` field to ``cases``. +* The test can be turned into "parameterized test", see below subsection. =20 Parameterized Testing ~~~~~~~~~~~~~~~~~~~~~ @@ -497,8 +503,8 @@ Parameterized Testing The table-driven testing pattern is common enough that KUnit has special support for it. =20 -By reusing the same ``cases`` array from above, we can write the test as a -"parameterized test" with the following. +The following example does the same :manpage:`sha1sum(1)` test as above, +but is written using parameterized testing facilities: =20 .. code-block:: c =20 @@ -526,7 +532,7 @@ By reusing the same ``cases`` array from above, we can = write the test as a // Creates `sha1_gen_params()` to iterate over `cases`. KUNIT_ARRAY_PARAM(sha1, cases, case_to_desc); =20 - // Looks no different from a normal test. + // Looks no different from other tests. static void sha1_test(struct kunit *test) { // This function can just contain the body of the for-loop. @@ -539,7 +545,7 @@ By reusing the same ``cases`` array from above, we can = write the test as a "sha1sum(%s)", test_param->str); } =20 - // Instead of KUNIT_CASE, we use KUNIT_CASE_PARAM and pass in the + // Instead of KUNIT_CASE, use KUNIT_CASE_PARAM and pass in the // function declared by KUNIT_ARRAY_PARAM. static struct kunit_case sha1_test_cases[] =3D { KUNIT_CASE_PARAM(sha1_test, sha1_gen_params), @@ -549,9 +555,13 @@ By reusing the same ``cases`` array from above, we can= write the test as a Exiting Early on Failed Expectations ------------------------------------ =20 -We can use ``KUNIT_EXPECT_EQ`` to mark the test as failed and continue -execution. In some cases, it is unsafe to continue. We can use the -``KUNIT_ASSERT`` variant to exit on failure. +All the tests until now uses ``KUNIT_EXPECT`` macros to assert expectation= s. +In case of any of these are failed, the test continues. However, in some +cases, continuing tests is not possible or is unsafe. For these cases, +you can use ``KUNIT_ASSERT`` variant to exit early on failure. + +The example below tests allocating objects then play with them. Only if +the allocation succeed, these objects can be played with: =20 .. code-block:: c =20 @@ -567,12 +577,15 @@ execution. In some cases, it is unsafe to continue. = We can use the Allocating Memory ----------------- =20 -Where you might use ``kzalloc``, you can instead use ``kunit_kzalloc`` as = KUnit -will then ensure that the memory is freed once the test completes. +When ``kzalloc`` may be used to allocate memory, you can instead use +``kunit_kzalloc`` as KUnit will then ensure that the memory is freed once +the test completes. =20 -This is useful because it lets us use the ``KUNIT_ASSERT_EQ`` macros to ex= it -early from a test without having to worry about remembering to call ``kfre= e``. -For example: +This is useful because it lets you to use ``KUNIT_ASSERT_EQ`` macros to ex= it +early from a test without having to worry about remembering to freeing +memory with ``kfree``. + +The following example tests allocating buffer memory: =20 .. code-block:: c =20 @@ -589,9 +602,11 @@ For example: Testing Static Functions ------------------------ =20 -If we do not want to expose functions or variables for testing, one option= is to -conditionally ``#include`` the test file at the end of your .c file. For -example: +If you do not want to expose testing functions or variables unconditionall= y, +you can ``#include`` the test file inside ``#ifdef`` guard. + +In the example below, the test code in ``my_kunit_test.c`` is included and +compiled only if ``CONFIG_MY_KUNIT_TEST`` is enabled: =20 .. code-block:: c =20 @@ -606,7 +621,11 @@ example: Injecting Test-Only Code ------------------------ =20 -Similar to as shown above, we can add test-specific logic. For example: +You can also add test-only logic inside the guard. + +In the following example, prototype of ``test_only_hook()`` is defined with +``CONFIG_MY_KUNIT_TEST`` both enabled and disabled. In case the configurat= ion +is disabled, the hook is defined as empty function: =20 .. code-block:: c =20 @@ -619,19 +638,21 @@ Similar to as shown above, we can add test-specific l= ogic. For example: void test_only_hook(void) { } #endif =20 -This test-only code can be made more useful by accessing the current ``kun= it_test`` -as shown in next section: *Accessing The Current Test*. +This can be made more useful by accessing the current ``kunit_test`` +as shown in next section below. =20 Accessing The Current Test -------------------------- =20 -In some cases, we need to call test-only code from outside the test file. -For example, see example in section *Injecting Test-Only Code* or if -we are providing a fake implementation of an ops struct. Using -``kunit_test`` field in ``task_struct``, we can access it via -``current->kunit_test``. +In some cases, it is desired to call test-only code from outside the test +file. See example from previous section for how this can be done by +including the test file. =20 -The example below includes how to implement "mocking": +Another way is to provide the fake implementation of an ops struct. For +example, given ``kunit_test`` field in ``task_struct``, the field can be +accessed via ``current->kunit_test``. + +The example below shows how to implement "mocking" pattern: =20 .. code-block:: c =20 @@ -665,25 +686,28 @@ The example below includes how to implement "mocking": KUNIT_EXPECT_EQ(test, fake_foo(1), 42); } =20 -In this example, we are using the ``priv`` member of ``struct kunit`` as a= way +In this example, ``kunit->priv`` is used as a way of passing data to the test from the init function. In general ``priv`` is pointer that can be used for any user data. This is preferred over static variables, as it avoids concurrency issues. =20 -Had we wanted something more flexible, we could have used a named ``kunit_= resource``. -Each test can have multiple resources which have string names providing th= e same -flexibility as a ``priv`` member, but also, for example, allowing helper +In cases where you want something more flexible, ``kunit_resource`` can be +used instead. Each test can have multiple resources which have names provi= ding +the same flexibility as ``priv``, but also, for example, allowing helper functions to create resources without conflicting with each other. It is a= lso -possible to define a clean up function for each resource, making it easy to -avoid resource leaks. For more information, see Documentation/dev-tools/ku= nit/api/test.rst. +possible to define clean up routines for each resource, making it easy to +avoid leaking. For more information, see +Documentation/dev-tools/kunit/api/test.rst. =20 Failing The Current Test ------------------------ =20 -If we want to fail the current test, we can use ``kunit_fail_current_test(= fmt, args...)`` -which is defined in ```` and does not require pulling in= ````. -For example, we have an option to enable some extra debug checks on some d= ata -structures as shown below: +If you want to fail the current test with a meaningful reason, you can use +``kunit_fail_current_test()``, which is defined in ````. +It does not require pulling in ````. + +The following example have the extra validation over ``data`` struct, which +is only done when ``CONFIG_EXTRA_DEBUG_CHECKS`` is enabled: =20 .. code-block:: c =20 --=20 An old man doll... just what I always wanted! - Clara