[PATCH v3 07/13] test: add basic barebox self-test infrastructure

Ahmad Fatoum a.fatoum at pengutronix.de
Wed Jun 2 07:34:47 PDT 2021


Self tests is code written to run within barebox to exercise
functionality. They offer flexibility to test specific units of barebox
instead of the program as a whole. Add a very simple infrastructure
for registering and executing self-tests. This is based on the Linux
kselftest modules. We don't utilize modules for this, however, because
we only have module support on ARM, but we need a generic solution.

Selftests can be enabled individually and even tested without shell
support to allow tests to happen for size-restricted barebox images
as well.

Acked-by: Rouven Czerwinski <r.czerwinski at pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 Kconfig             |  1 +
 Makefile            |  2 +-
 commands/Makefile   |  1 +
 commands/selftest.c | 88 +++++++++++++++++++++++++++++++++++++++++++++
 common/startup.c    |  4 +++
 include/bselftest.h | 74 ++++++++++++++++++++++++++++++++++++++
 test/Kconfig        |  8 +++++
 test/Makefile       |  1 +
 test/self/Kconfig   | 33 +++++++++++++++++
 test/self/Makefile  |  3 ++
 test/self/core.c    | 22 ++++++++++++
 11 files changed, 236 insertions(+), 1 deletion(-)
 create mode 100644 commands/selftest.c
 create mode 100644 include/bselftest.h
 create mode 100644 test/Kconfig
 create mode 100644 test/Makefile
 create mode 100644 test/self/Kconfig
 create mode 100644 test/self/Makefile
 create mode 100644 test/self/core.c

diff --git a/Kconfig b/Kconfig
index 29c32463fb43..7c4cf36881b4 100644
--- a/Kconfig
+++ b/Kconfig
@@ -15,3 +15,4 @@ source "lib/Kconfig"
 source "crypto/Kconfig"
 source "firmware/Kconfig"
 source "scripts/Kconfig"
+source "test/Kconfig"
diff --git a/Makefile b/Makefile
index a1411386f1b3..e856d8672b71 100644
--- a/Makefile
+++ b/Makefile
@@ -581,7 +581,7 @@ endif
 include $(srctree)/scripts/Makefile.lib
 
 # Objects we will link into barebox / subdirs we need to visit
-common-y		:= common/ drivers/ commands/ lib/ crypto/ net/ fs/ firmware/
+common-y		:= common/ drivers/ commands/ lib/ crypto/ net/ fs/ firmware/ test/
 
 include $(srctree)/arch/$(SRCARCH)/Makefile
 
diff --git a/commands/Makefile b/commands/Makefile
index 447349fd157d..4b45d266fd56 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -130,5 +130,6 @@ obj-$(CONFIG_CMD_SEED)		+= seed.o
 obj-$(CONFIG_CMD_IP_ROUTE_GET)  += ip-route-get.o
 obj-$(CONFIG_CMD_BTHREAD)	+= bthread.o
 obj-$(CONFIG_CMD_UBSAN)		+= ubsan.o
+obj-$(CONFIG_CMD_SELFTEST)	+= selftest.o
 
 UBSAN_SANITIZE_ubsan.o := y
diff --git a/commands/selftest.c b/commands/selftest.c
new file mode 100644
index 000000000000..a10f1467fece
--- /dev/null
+++ b/commands/selftest.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#define pr_fmt(fmt) "selftest: " fmt
+
+#include <common.h>
+#include <command.h>
+#include <getopt.h>
+#include <bselftest.h>
+#include <complete.h>
+
+static int run_selftest(const char *match, bool list)
+{
+	struct selftest *test;
+	int matches = 0;
+	int err = 0;
+
+	list_for_each_entry(test, &selftests, list) {
+		if (list) {
+			printf("%s\n", test->name);
+			matches++;
+			continue;
+		}
+
+		if (match && strcmp(test->name, match))
+			continue;
+
+		err |= test->func();
+		matches++;
+	}
+
+	if (!matches) {
+		if (match) {
+			printf("No tests matching '%s' found.\n", match);
+			return -EINVAL;
+		}
+
+		printf("No tests found.\n");
+	}
+
+	return err;
+}
+
+static int do_selftest(int argc, char *argv[])
+{
+	bool list = false;
+	int i, err = 0;
+	int opt;
+
+	while((opt = getopt(argc, argv, "l")) > 0) {
+		switch(opt) {
+		case 'l':
+			list = true;
+			break;
+		default:
+			return COMMAND_ERROR_USAGE;
+		}
+	}
+
+	if (optind == argc) {
+		err = run_selftest(NULL, list);
+	} else {
+		for (i = optind; i < argc; i++) {
+			err = run_selftest(argv[i], list);
+			if (err)
+				goto out;
+		}
+	}
+
+out:
+	return err ? COMMAND_ERROR : COMMAND_SUCCESS;
+}
+
+BAREBOX_CMD_HELP_START(selftest)
+BAREBOX_CMD_HELP_TEXT("Run enabled barebox self-tests")
+BAREBOX_CMD_HELP_TEXT("If run without arguments, all tests are run")
+BAREBOX_CMD_HELP_TEXT("")
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-l",     "list available tests")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(selftest)
+	.cmd		= do_selftest,
+	BAREBOX_CMD_DESC("Run selftests")
+	BAREBOX_CMD_OPTS("[-l] [tests..]")
+	BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+	BAREBOX_CMD_COMPLETE(empty_complete)
+	BAREBOX_CMD_HELP(cmd_selftest_help)
+BAREBOX_CMD_END
diff --git a/common/startup.c b/common/startup.c
index 080feebf05fd..d170cb8a7c5a 100644
--- a/common/startup.c
+++ b/common/startup.c
@@ -37,6 +37,7 @@
 #include <linux/ctype.h>
 #include <watchdog.h>
 #include <glob.h>
+#include <bselftest.h>
 
 extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[],
 		  __barebox_initcalls_end[];
@@ -417,6 +418,9 @@ void __noreturn start_barebox(void)
 
 	pr_debug("initcalls done\n");
 
+	if (IS_ENABLED(CONFIG_SELFTEST_AUTORUN))
+		selftests_run();
+
 	if (barebox_main)
 		barebox_main();
 
diff --git a/include/bselftest.h b/include/bselftest.h
new file mode 100644
index 000000000000..21eeba0526ef
--- /dev/null
+++ b/include/bselftest.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef __BSELFTEST_H
+#define __BSELFTEST_H
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <init.h>
+
+enum bselftest_group {
+	BSELFTEST_core
+};
+
+struct selftest {
+	enum bselftest_group group;
+	const char *name;
+	int (*func)(void);
+	struct list_head list;
+};
+
+static inline int selftest_report(unsigned int total_tests, unsigned int failed_tests,
+				  unsigned int skipped_tests)
+{
+	if (failed_tests == 0) {
+		if (skipped_tests) {
+			pr_info("skipped %u tests\n", skipped_tests);
+			pr_info("remaining %u tests passed\n", total_tests);
+		} else
+			pr_info("all %u tests passed\n", total_tests);
+	} else
+		pr_err("failed %u out of %u tests\n", failed_tests, total_tests);
+
+	return failed_tests ? -EINVAL : 0;
+}
+
+extern struct list_head selftests;
+
+#define BSELFTEST_GLOBALS()			\
+static unsigned int total_tests __initdata;	\
+static unsigned int failed_tests __initdata;	\
+static unsigned int skipped_tests __initdata
+
+#ifdef CONFIG_SELFTEST
+#define __bselftest_initcall(func) late_initcall(func)
+void selftests_run(void);
+#else
+#define __bselftest_initcall(func)
+static inline void selftests_run(void)
+{
+}
+#endif
+
+#define bselftest(_group, _func)				\
+	static int __bselftest_##_func(void)			\
+	{							\
+		total_tests = failed_tests = skipped_tests = 0;	\
+		_func();					\
+		return selftest_report(total_tests,		\
+				failed_tests,			\
+				skipped_tests);			\
+	}							\
+	static __maybe_unused					\
+	int __init _func##_bselftest_register(void)		\
+	{							\
+		static struct selftest this = {			\
+			.group = BSELFTEST_##_group,		\
+			.name = KBUILD_MODNAME,			\
+			.func = __bselftest_##_func,		\
+		};						\
+		list_add_tail(&this.list, &selftests);		\
+		return 0;					\
+	}							\
+	__bselftest_initcall(_func##_bselftest_register);
+
+#endif
diff --git a/test/Kconfig b/test/Kconfig
new file mode 100644
index 000000000000..eece702e68aa
--- /dev/null
+++ b/test/Kconfig
@@ -0,0 +1,8 @@
+menuconfig TEST
+	bool "Testing"
+
+if TEST
+
+source "test/self/Kconfig"
+
+endif
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 000000000000..1b9eb2171a82
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1 @@
+obj-y += self/
diff --git a/test/self/Kconfig b/test/self/Kconfig
new file mode 100644
index 000000000000..720abeffc51b
--- /dev/null
+++ b/test/self/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config SELFTEST
+	bool "Self-tests"
+	help
+	  Configures support for in-barebox testing
+
+if SELFTEST
+
+config CMD_SELFTEST
+	bool "selftest command"
+	depends on COMMAND_SUPPORT
+	help
+	  Command to run enabled barebox self-tests.
+	  If run without arguments, all tests are run
+
+	  Usage: selftest [-l] [tests...]
+
+	  Options:
+	    -l     list available tests
+
+config SELFTEST_AUTORUN
+	bool "Run self-tests on startup"
+	help
+	  Self tests are run automatically after initcalls are done,
+	  but before barebox_main (shell or board-specific startup).
+
+config SELFTEST_ENABLE_ALL
+	bool "Enable all self-tests"
+	help
+	  Selects all self-tests compatible with current configuration
+
+endif
diff --git a/test/self/Makefile b/test/self/Makefile
new file mode 100644
index 000000000000..78f738c8e210
--- /dev/null
+++ b/test/self/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_SELFTEST) += core.o
diff --git a/test/self/core.c b/test/self/core.c
new file mode 100644
index 000000000000..5a309fc8f18d
--- /dev/null
+++ b/test/self/core.c
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#define pr_fmt(fmt) "bselftest: " fmt
+
+#include <common.h>
+#include <bselftest.h>
+
+LIST_HEAD(selftests);
+
+int selftests_run(void)
+{
+	struct selftest *test;
+	int err = 0;
+
+	pr_notice("Configured tests will run now\n");
+
+	list_for_each_entry(test, &selftests, list)
+		err |= test->func();
+
+	if (err)
+		pr_err("Some selftests failed\n");
+}
-- 
2.29.2




More information about the barebox mailing list