| HOW TO RUN KUNIT TESTS IN ANDROID | |
| ================================= | |
| Prerequisites | |
| ------------- | |
| If you want to run a vendor module KUnit tests, please run the tests with a | |
| "no trim" kernel (e.g. add `--notrim` to bazel build command). | |
| Run tests on a physical or virtual device: | |
| ------------------------------------------ | |
| ``` | |
| $ kernel/tests/tools/run_test_only.sh -t kunit -s <serial_number> -td <test_dir> | |
| ``` | |
| test_dir is the same directory as specified when running: | |
| ``` | |
| $ tools/bazel run //common:kunit_tests_arm64 -- -v --destdir <test_dir> | |
| ``` | |
| Before the tests, you can use the following command to launch a virtual device: | |
| ``` | |
| $ kernel/tests/tools/launch_cvd.sh | |
| ``` | |
| After the tests, you can use the following command to remove the virtual device: | |
| ``` | |
| $ prebuilts/asuite/acloud/linux-x86/acloud delete | |
| ``` | |
| The following are command examples: | |
| * Build kernel and launch a virtual device from a specific platform build: | |
| ``` | |
| $ kernel/tests/tools/launch_cvd.sh -pb \ | |
| ab://aosp-main/aosp_cf_x86_64_phone-trunk_staging-userdebug/12505199 | |
| ``` | |
| * Run a specific test: | |
| ``` | |
| $ kernel/tests/tools/run_test_only.sh \ | |
| -t 'kunit soc-utils-test' -s <serial_number> | |
| ``` | |
| * Check other available options: | |
| ``` | |
| $ kernel/tests/tools/launch_cvd.sh -h | |
| $ kernel/tests/tools/run_test_only.sh -h | |
| ``` | |
| Load and run a test module on Android device manually | |
| ----------------------------------------------------- | |
| * Push the KUnit test framework module kunit.ko over to the device. For | |
| example: | |
| ``` | |
| $ adb push kunit.ko /data | |
| ``` | |
| * Load test module on device: | |
| ``` | |
| $ cd /data | |
| $ insmod kunit.ko enable=1 | |
| ``` | |
| If the kunit.ko has been installed already but without enable=1 passed, | |
| it needs to remove it first via the rmmod command, and install again | |
| via the insmod command | |
| * Push the KUnit test module over to the device. For example using adb: | |
| ``` | |
| $ adb push kunit-example-test.ko /data | |
| ``` | |
| * (Optional) - Mount debugfs on device: | |
| ``` | |
| $ mount -t debugfs debugfs /sys/kernel/debug | |
| ``` | |
| * Load test module on device: | |
| ``` | |
| $ cd /data | |
| $ insmod kunit-example-test.ko | |
| ``` | |
| * View test results | |
| * If debugfs is mounted: | |
| ``` | |
| $ cat /sys/kernel/debug/kunit/<test name>/results | |
| KTAP version 1 | |
| 1..1 | |
| KTAP version 1 | |
| # Subtest: example | |
| 1..4 | |
| # example_simple_test: initializing | |
| ok 1 example_simple_test | |
| .... | |
| ``` | |
| * Via dmesg (check before log cycles out): | |
| ``` | |
| $ dmesg | |
| .... | |
| [172434.032618] 1..1 | |
| [172434.032618] KTAP version 1 | |
| [172434.032618] # Subtest: example | |
| [172434.032618] 1..4 | |
| [172434.032618] # example_simple_test: initializing | |
| [172434.032618] | |
| [172434.032618] ok 1 example_simple_test | |
| .... | |
| ``` | |
| Run KUnit tests on Android Device via test automation infrastructure tradefed | |
| ----------------------------------------------------------------------------- | |
| * Build ACK KUnit tests and install (e.g. /tmp/kunit_tests): | |
| ``` | |
| $ tools/bazel run -- //common:kunit_tests_x86_64 -v --destdir /tmp/kunit_tests | |
| ``` | |
| Or | |
| ``` | |
| $ tools/bazel run -- //common:kunit_tests_arm64 -v --destdir /tmp/kunit_tests | |
| ``` | |
| * With device connected and accessible via adb run the tests: | |
| ``` | |
| $ prebuilts/tradefed/filegroups/tradefed/tradefed.sh run commandAndExit \ | |
| template/local_min --template:map test=suite/test_mapping_suite \ | |
| --include-filter kunit --tests-dir=/tmp/kunit_tests \ | |
| -s <your_device_serial_number> | |
| .... | |
| ======================================================= | |
| =============== Summary =============== | |
| Total Run time: 23s | |
| 1/1 modules completed | |
| Total Tests : 9 | |
| PASSED : 9 | |
| FAILED : 0 | |
| ============== End of Results ============== | |
| ============================================ | |
| .... | |
| ``` | |
| Troubleshooting | |
| --------------- | |
| 1. Test module fails to load. | |
| Check dmesg for load errors. If undefined symbol errors are shown, you're | |
| likely running with a trimmed kernel where the symbols are not available. | |
| Run with a "no trim" kernel. | |
| Check the test module dependency with `modinfo <module_name>.ko` on your | |
| local host machine or on the Android device with | |
| `adb shell modinfo <module_name>.ko`. | |
| All dependent modules need to be installed before the test module can be | |
| installed successfully. | |
| Check if the module is already installed with `adb shell lsmod`. The | |
| `adb shell rmmod` can be used to remove the already installed test module, | |
| and installing the test module again will trigger the test rerun. | |
| `adb shell lsmod` will also show the module dependency for your test module | |
| in the `Used by` column. You can not remove a module with `adb shell rmmod` | |
| if it is being used by another module. Other modules that are using it need | |
| to be removed first. | |
| 2. Test module loaded but no test results. | |
| Check dmesg for KUnit errors. | |
| ``` | |
| $ dmesg | grep kunit | |
| ``` | |
| If "kunit: disabled" is shown then kunit.ko is not installed with | |
| `enable=1`. | |
| If kunit.ko or \<module_name\>.ko fails to install, check for whether they | |
| are already installed with `adb shell lsmod`. | |
| HOW TO WRITE KUNIT TESTS | |
| ======================== | |
| If you want to | |
| just run the example test and skip through the walkthrough, it is sufficient to | |
| apply [patch1.txt](patch1.txt) and [patch2.txt](patch2.txt) and | |
| then [run the test](#run-tests-on-a-physical-or-virtual-device). | |
| Walkthrough | |
| ----------- | |
| This section follows the | |
| [Writing Your First Test](https://docs.kernel.org/dev-tools/kunit/start.html#writing-your-first-test) | |
| example from the KUnit docs. | |
| ### Create an example driver to test | |
| 1. Create the header file for the driver `common/drivers/misc/example.h`: | |
| ```c | |
| int misc_example_add(int left, int right); | |
| ``` | |
| 2. Define and export `misc_example_add` that will be tested later | |
| `common/drivers/misc/example.c`: | |
| ```c | |
| #include <linux/module.h> | |
| #include "example.h" | |
| int misc_example_add(int left, int right) | |
| { | |
| return left + right; | |
| } | |
| EXPORT_SYMBOL_GPL(misc_example_add); | |
| ``` | |
| 3. Add a kconfig option for the driver to `common/drivers/misc/Kconfig`: | |
| ``` | |
| config MISC_EXAMPLE | |
| bool "My example" | |
| ``` | |
| 4. Add the build rule to `common/drivers/misc/Makefile`: | |
| ```makefile | |
| obj-$(CONFIG_MISC_EXAMPLE) += example.o | |
| ``` | |
| ### Write the test case | |
| 1. Define the test functions and suite `common/drivers/misc/example_test.c`: | |
| ```c | |
| #include <kunit/test.h> | |
| #include "example.h" | |
| /* Define the test cases. */ | |
| static void misc_example_add_test_basic(struct kunit *test) | |
| { | |
| KUNIT_EXPECT_EQ(test, 1, misc_example_add(1, 0)); | |
| KUNIT_EXPECT_EQ(test, 2, misc_example_add(1, 1)); | |
| KUNIT_EXPECT_EQ(test, 0, misc_example_add(-1, 1)); | |
| KUNIT_EXPECT_EQ(test, INT_MAX, misc_example_add(0, INT_MAX)); | |
| KUNIT_EXPECT_EQ(test, -1, misc_example_add(INT_MAX, INT_MIN)); | |
| } | |
| static void misc_example_test_failure(struct kunit *test) | |
| { | |
| KUNIT_FAIL(test, "This test never passes."); | |
| } | |
| static struct kunit_case misc_example_test_cases[] = { | |
| KUNIT_CASE(misc_example_add_test_basic), | |
| KUNIT_CASE(misc_example_test_failure), | |
| {} | |
| }; | |
| static struct kunit_suite misc_example_test_suite = { | |
| .name = "misc-example", | |
| .test_cases = misc_example_test_cases, | |
| }; | |
| kunit_test_suite(misc_example_test_suite); | |
| MODULE_DESCRIPTION("KUnit test for misc_example_add"); | |
| MODULE_LICENSE("GPL"); | |
| ``` | |
| Consult the | |
| [KUnit usage guide](https://docs.kernel.org/dev-tools/kunit/usage.html#) and | |
| the | |
| [KUnit API reference](https://docs.kernel.org/dev-tools/kunit/api/index.html) | |
| for more in-depth guidance on the test API. | |
| 2. Create a kconfig option for the test `common/drivers/misc/Kconfig`: | |
| ``` | |
| config MISC_EXAMPLE_TEST | |
| tristate "Test for my example" if !KUNIT_ALL_TESTS | |
| depends on MISC_EXAMPLE && KUNIT | |
| default KUNIT_ALL_TESTS | |
| ``` | |
| 3. Add the build rule to the makefile `common/drivers/misc/Makefile`: | |
| ```makefile | |
| obj-$(CONFIG_MISC_EXAMPLE_TEST) += example_test.o | |
| ``` | |
| 4. Add the kconfig options for kunit.py `common/drivers/misc/.kunitconfig`: | |
| ```bash | |
| CONFIG_KUNIT=y | |
| CONFIG_MISC_EXAMPLE=y | |
| CONFIG_MISC_EXAMPLE_TEST=y | |
| ``` | |
| ### Run the test against User-mode Linux | |
| 1. The test can then be run against User-mode Linux using the kunit.py script: | |
| ``` | |
| $ cd $REPO/common | |
| $ tools/testing/kunit/kunit.py run --kunitconfig drivers/misc | |
| [01:21:36] Configuring KUnit Kernel ... | |
| Regenerating .config ... | |
| Populating config with: | |
| $ make ARCH=um O=.kunit olddefconfig | |
| [01:21:38] Building KUnit Kernel ... | |
| Populating config with: | |
| $ make ARCH=um O=.kunit olddefconfig | |
| Building with: | |
| $ make ARCH=um O=.kunit --jobs=96 | |
| [01:21:49] Starting KUnit Kernel (1/1)... | |
| [01:21:49] ============================================================ | |
| Running tests with: | |
| $ .kunit/linux kunit.enable=1 mem=1G console=tty kunit_shutdown=halt | |
| [01:22:01] ================ misc-example (2 subtests) ================= | |
| [01:22:01] [PASSED] misc_example_add_test_basic | |
| [01:22:01] # misc_example_test_failure: EXPECTATION FAILED at drivers/misc/example_test.c:17 | |
| [01:22:01] This test never passes. | |
| [01:22:01] [FAILED] misc_example_test_failure | |
| [01:22:01] # module: example_test | |
| [01:22:01] # misc-example: pass:1 fail:1 skip:0 total:2 | |
| [01:22:01] # Totals: pass:1 fail:1 skip:0 total:2 | |
| [01:22:01] ================== [FAILED] misc-example =================== | |
| [01:22:01] ============================================================ | |
| [01:22:01] Testing complete. Ran 2 tests: passed: 1, failed: 1 | |
| [01:22:01] Elapsed time: 24.906s total, 2.271s configuring, 10.889s building, 11.694s running | |
| ``` | |
| 2. `kunit.py` will create symlinks under the `.kunit` directory that interfere | |
| with Kleaf/Bazel. This directory is ignored by `.bazelignore`. | |
| ### Configure the test for Android | |
| Now that the example test is created, the test needs to be configured for | |
| Android. We will need to add build rules for Kleaf/Bazel and create a Tradefed | |
| test config. | |
| 1. Set `CONFIG_MISC_EXAMPLE=y` and `CONFIG_MISC_EXAMPLE_TEST=m` in | |
| [gki_defconfig](https://source.corp.google.com/h/android/kernel/superproject/+/common-android-mainline:common/arch/x86/configs/gki_defconfig). | |
| (This can be done using menuconfig; refer to the | |
| [Kleaf documentation](https://android.googlesource.com/kernel/build/+/refs/heads/main-kernel/kleaf/docs/kernel_config.md)): | |
| ``` | |
| $ cd $REPO | |
| $ tools/bazel run //common:kernel_x86_64_config -- menuconfig | |
| ``` | |
| 2. Add the test module to `_KUNIT_COMMON_MODULES_LIST` in `common/modules.bzl`: | |
| ```bazel | |
| _KUNIT_COMMON_MODULES_LIST = [ | |
| .... | |
| "drivers/misc/example_test.ko", | |
| .... | |
| ] | |
| ``` | |
| 3. Add the test under `KUnitModuleTest` in the tradefed configuration | |
| `common/tools/testing/kunit/android/tradefed_configs/config_x86_64.xml`: | |
| ```xml | |
| <test class="com.android.tradefed.testtype.binary.KUnitModuleTest" > | |
| .... | |
| <option name='binary' key='drivers/misc/example_test' value='/data/kunit/arm64/example_test.ko' /> | |
| .... | |
| </test> | |
| ``` |