| 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> |
| ``` |