diff --git a/.github/workflows/cppcheck-differential.yaml b/.github/workflows/cppcheck-differential.yaml index d698e1bd..0a22f6a5 100644 --- a/.github/workflows/cppcheck-differential.yaml +++ b/.github/workflows/cppcheck-differential.yaml @@ -99,7 +99,7 @@ jobs: - name: Upload Cppcheck report if: ${{ steps.add-kmod.outputs.filtered-full-paths != '' }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: cppcheck-report path: cppcheck-report.txt diff --git a/.gitignore b/.gitignore index a36b4293..6c2c3bfb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ log *.log coverage_report_agnocastlib .vscode +agnocast_kmod_coverage_report diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e05cc1a..d817e25c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,3 +10,11 @@ repos: hooks: - id: markdownlint args: [-c, .markdownlint.yaml, --fix] + + - repo: local + hooks: + - id: kunit + name: Run KUnit tests + entry: scripts/run_kunit + language: system + types: [c] diff --git a/README.md b/README.md index cdd98f5a..27faab32 100644 --- a/README.md +++ b/README.md @@ -109,9 +109,24 @@ You can build, test and generate the coverage report by following: bash scripts/test_and_create_report ``` +## (For developer) Kernel Module Test + +A custom kernel with the following CONFIG enabled is required to run KUnit Test and obtain the coverage report (sample custom kernel is placed [here](https://drive.google.com/drive/folders/1sd8ROusgxhnEDOO0hbze3F5y47qtIdcM?usp=drive_link)). + +- `CONFIG_KUNIT=y` +- `CONFIG_GCOV_KERNEL=y` + +If booting with the custom kernel, the following script can be used to run unit tests on kernel modules and generate coverage reports. + +```bash +bash script/run_kunit +``` + +You can also use [pre-commit](#for-developer-setup-pre-commit). + ## (For developer) Setup pre-commit -The following command allows `clang-format` and `markdownlint` to be run before each commit. +The following command allows `clang-format`, `markdownlint`, and [KUNIT Test](./kmod/agnocast_kunit.c) to be run before each commit. ```bash bash scripts/setup diff --git a/kmod/Makefile b/kmod/Makefile index 2d1a086c..b514ded1 100644 --- a/kmod/Makefile +++ b/kmod/Makefile @@ -27,6 +27,14 @@ ifeq ($(shell expr $(KERNEL_MAJOR) \< 6 \| $(KERNEL_MAJOR) = 6 \& $(KERNEL_MINOR CFLAGS_agnocast.o += -Wno-declaration-after-statement endif +# For KUnit +ifeq ($(CONFIG_KUNIT),y) + obj-m += agnocast_kunit.o +endif +ifeq ($(CONFIG_GCOV_KERNEL),y) + CFLAGS_agnocast.o += -fprofile-arcs -ftest-coverage + CFLAGS_agnocast_kunit.o += -fprofile-arcs -ftest-coverage +endif all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules diff --git a/kmod/agnocast_kunit.c b/kmod/agnocast_kunit.c new file mode 100644 index 00000000..7cef1e48 --- /dev/null +++ b/kmod/agnocast_kunit.c @@ -0,0 +1,22 @@ +#include "agnocast.h" + +#include + +MODULE_LICENSE("Dual BSD/GPL"); + +static void agnocast_sample_test_case(struct kunit * test) +{ + KUNIT_EXPECT_EQ(test, 1 + 1, 2); +} + +struct kunit_case agnocast_test_cases[] = { + KUNIT_CASE(agnocast_sample_test_case), + {}, +}; + +struct kunit_suite agnocast_test_suite = { + .name = "agnocast_test_suite", + .test_cases = agnocast_test_cases, +}; + +kunit_test_suite(agnocast_test_suite); diff --git a/scripts/run_kunit b/scripts/run_kunit new file mode 100755 index 00000000..431dae98 --- /dev/null +++ b/scripts/run_kunit @@ -0,0 +1,39 @@ +#!/bin/bash + +### Validation +if [ -f "/boot/config-$(uname -r)" ]; then + CONFIG_FILE="/boot/config-$(uname -r)" +elif [ -f "/proc/config.gz" ]; then + zcat /proc/config.gz >/tmp/config-$(uname -r) + CONFIG_FILE="/tmp/config-$(uname -r)" +else + echo "Kernel config file not found!" + exit 1 +fi + +if ! grep -q "CONFIG_KUNIT=y" $CONFIG_FILE; then + echo "Skipping KUnit tests as CONFIG_KUNIT is not enabled." + exit 0 +fi + +### Run KUnit tests +AGNOCAST_DIR=$(realpath "$(dirname $(readlink -f $0))/..") + +AGNOCAST_KMOD_PATH=$AGNOCAST_DIR/kmod + +if [ -z "$AGNOCAST_KMOD_PATH" ]; then + echo "Usage: create_coverage_report " + exit 1 +fi + +cd $AGNOCAST_KMOD_PATH +make clean +make +sudo insmod $AGNOCAST_KMOD_PATH/agnocast_kunit.ko +sudo rmmod agnocast_kunit + +sudo lcov --capture --directory /sys/kernel/debug/gcov/$AGNOCAST_KMOD_PATH --output-file $AGNOCAST_DIR/coverage.info +sudo lcov --remove $AGNOCAST_DIR/coverage.info "*linux*" "*kunit*" --output-file $AGNOCAST_DIR/coverage_filtered.info +genhtml $AGNOCAST_DIR/coverage_filtered.info --output-directory $AGNOCAST_DIR/agnocast_kmod_coverage_report +rm -f $AGNOCAST_DIR/coverage.info $AGNOCAST_DIR/coverage_filtered.info $AGNOCAST_KMOD_PATH/*.gcno +echo "Please open agnocast_kmod_coverage_report/index.html in your web browser."