162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/reboot.h>
462306a36Sopenharmony_ci#include <kunit/test.h>
562306a36Sopenharmony_ci#include <kunit/attributes.h>
662306a36Sopenharmony_ci#include <linux/glob.h>
762306a36Sopenharmony_ci#include <linux/moduleparam.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci * These symbols point to the .kunit_test_suites section and are defined in
1162306a36Sopenharmony_ci * include/asm-generic/vmlinux.lds.h, and consequently must be extern.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ciextern struct kunit_suite * const __kunit_suites_start[];
1462306a36Sopenharmony_ciextern struct kunit_suite * const __kunit_suites_end[];
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic char *action_param;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cimodule_param_named(action, action_param, charp, 0400);
1962306a36Sopenharmony_ciMODULE_PARM_DESC(action,
2062306a36Sopenharmony_ci		 "Changes KUnit executor behavior, valid values are:\n"
2162306a36Sopenharmony_ci		 "<none>: run the tests like normal\n"
2262306a36Sopenharmony_ci		 "'list' to list test names instead of running them.\n"
2362306a36Sopenharmony_ci		 "'list_attr' to list test names and attributes instead of running them.\n");
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciconst char *kunit_action(void)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	return action_param;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic char *filter_glob_param;
3162306a36Sopenharmony_cistatic char *filter_param;
3262306a36Sopenharmony_cistatic char *filter_action_param;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cimodule_param_named(filter_glob, filter_glob_param, charp, 0400);
3562306a36Sopenharmony_ciMODULE_PARM_DESC(filter_glob,
3662306a36Sopenharmony_ci		"Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test");
3762306a36Sopenharmony_cimodule_param_named(filter, filter_param, charp, 0400);
3862306a36Sopenharmony_ciMODULE_PARM_DESC(filter,
3962306a36Sopenharmony_ci		"Filter which KUnit test suites/tests run at boot-time using attributes, e.g. speed>slow");
4062306a36Sopenharmony_cimodule_param_named(filter_action, filter_action_param, charp, 0400);
4162306a36Sopenharmony_ciMODULE_PARM_DESC(filter_action,
4262306a36Sopenharmony_ci		"Changes behavior of filtered tests using attributes, valid values are:\n"
4362306a36Sopenharmony_ci		"<none>: do not run filtered tests as normal\n"
4462306a36Sopenharmony_ci		"'skip': skip all filtered tests instead so tests will appear in output\n");
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ciconst char *kunit_filter_glob(void)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	return filter_glob_param;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cichar *kunit_filter(void)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return filter_param;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cichar *kunit_filter_action(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return filter_action_param;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */
6262306a36Sopenharmony_cistruct kunit_glob_filter {
6362306a36Sopenharmony_ci	char *suite_glob;
6462306a36Sopenharmony_ci	char *test_glob;
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */
6862306a36Sopenharmony_cistatic int kunit_parse_glob_filter(struct kunit_glob_filter *parsed,
6962306a36Sopenharmony_ci				    const char *filter_glob)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	const int len = strlen(filter_glob);
7262306a36Sopenharmony_ci	const char *period = strchr(filter_glob, '.');
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (!period) {
7562306a36Sopenharmony_ci		parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL);
7662306a36Sopenharmony_ci		if (!parsed->suite_glob)
7762306a36Sopenharmony_ci			return -ENOMEM;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		parsed->test_glob = NULL;
8062306a36Sopenharmony_ci		strcpy(parsed->suite_glob, filter_glob);
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL);
8562306a36Sopenharmony_ci	if (!parsed->suite_glob)
8662306a36Sopenharmony_ci		return -ENOMEM;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL);
8962306a36Sopenharmony_ci	if (!parsed->test_glob) {
9062306a36Sopenharmony_ci		kfree(parsed->suite_glob);
9162306a36Sopenharmony_ci		return -ENOMEM;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	strncpy(parsed->suite_glob, filter_glob, period - filter_glob);
9562306a36Sopenharmony_ci	strncpy(parsed->test_glob, period + 1, len - (period - filter_glob));
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* Create a copy of suite with only tests that match test_glob. */
10162306a36Sopenharmony_cistatic struct kunit_suite *
10262306a36Sopenharmony_cikunit_filter_glob_tests(const struct kunit_suite *const suite, const char *test_glob)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	int n = 0;
10562306a36Sopenharmony_ci	struct kunit_case *filtered, *test_case;
10662306a36Sopenharmony_ci	struct kunit_suite *copy;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	kunit_suite_for_each_test_case(suite, test_case) {
10962306a36Sopenharmony_ci		if (!test_glob || glob_match(test_glob, test_case->name))
11062306a36Sopenharmony_ci			++n;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (n == 0)
11462306a36Sopenharmony_ci		return NULL;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL);
11762306a36Sopenharmony_ci	if (!copy)
11862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
12162306a36Sopenharmony_ci	if (!filtered) {
12262306a36Sopenharmony_ci		kfree(copy);
12362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	n = 0;
12762306a36Sopenharmony_ci	kunit_suite_for_each_test_case(suite, test_case) {
12862306a36Sopenharmony_ci		if (!test_glob || glob_match(test_glob, test_case->name))
12962306a36Sopenharmony_ci			filtered[n++] = *test_case;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	copy->test_cases = filtered;
13362306a36Sopenharmony_ci	return copy;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_civoid kunit_free_suite_set(struct kunit_suite_set suite_set)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct kunit_suite * const *suites;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	for (suites = suite_set.start; suites < suite_set.end; suites++) {
14162306a36Sopenharmony_ci		kfree((*suites)->test_cases);
14262306a36Sopenharmony_ci		kfree(*suites);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci	kfree(suite_set.start);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * Filter and reallocate test suites. Must return the filtered test suites set
14962306a36Sopenharmony_ci * allocated at a valid virtual address or NULL in case of error.
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_cistruct kunit_suite_set
15262306a36Sopenharmony_cikunit_filter_suites(const struct kunit_suite_set *suite_set,
15362306a36Sopenharmony_ci		    const char *filter_glob,
15462306a36Sopenharmony_ci		    char *filters,
15562306a36Sopenharmony_ci		    char *filter_action,
15662306a36Sopenharmony_ci		    int *err)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	int i, j, k;
15962306a36Sopenharmony_ci	int filter_count = 0;
16062306a36Sopenharmony_ci	struct kunit_suite **copy, **copy_start, *filtered_suite, *new_filtered_suite;
16162306a36Sopenharmony_ci	struct kunit_suite_set filtered = {NULL, NULL};
16262306a36Sopenharmony_ci	struct kunit_glob_filter parsed_glob;
16362306a36Sopenharmony_ci	struct kunit_attr_filter *parsed_filters = NULL;
16462306a36Sopenharmony_ci	struct kunit_suite * const *suites;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	const size_t max = suite_set->end - suite_set->start;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	copy = kcalloc(max, sizeof(*filtered.start), GFP_KERNEL);
16962306a36Sopenharmony_ci	if (!copy) { /* won't be able to run anything, return an empty set */
17062306a36Sopenharmony_ci		return filtered;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	copy_start = copy;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (filter_glob) {
17562306a36Sopenharmony_ci		*err = kunit_parse_glob_filter(&parsed_glob, filter_glob);
17662306a36Sopenharmony_ci		if (*err)
17762306a36Sopenharmony_ci			goto free_copy;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Parse attribute filters */
18162306a36Sopenharmony_ci	if (filters) {
18262306a36Sopenharmony_ci		filter_count = kunit_get_filter_count(filters);
18362306a36Sopenharmony_ci		parsed_filters = kcalloc(filter_count, sizeof(*parsed_filters), GFP_KERNEL);
18462306a36Sopenharmony_ci		if (!parsed_filters) {
18562306a36Sopenharmony_ci			*err = -ENOMEM;
18662306a36Sopenharmony_ci			goto free_parsed_glob;
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci		for (j = 0; j < filter_count; j++)
18962306a36Sopenharmony_ci			parsed_filters[j] = kunit_next_attr_filter(&filters, err);
19062306a36Sopenharmony_ci		if (*err)
19162306a36Sopenharmony_ci			goto free_parsed_filters;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	for (i = 0; &suite_set->start[i] != suite_set->end; i++) {
19562306a36Sopenharmony_ci		filtered_suite = suite_set->start[i];
19662306a36Sopenharmony_ci		if (filter_glob) {
19762306a36Sopenharmony_ci			if (!glob_match(parsed_glob.suite_glob, filtered_suite->name))
19862306a36Sopenharmony_ci				continue;
19962306a36Sopenharmony_ci			filtered_suite = kunit_filter_glob_tests(filtered_suite,
20062306a36Sopenharmony_ci					parsed_glob.test_glob);
20162306a36Sopenharmony_ci			if (IS_ERR(filtered_suite)) {
20262306a36Sopenharmony_ci				*err = PTR_ERR(filtered_suite);
20362306a36Sopenharmony_ci				goto free_filtered_suite;
20462306a36Sopenharmony_ci			}
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci		if (filter_count > 0 && parsed_filters != NULL) {
20762306a36Sopenharmony_ci			for (k = 0; k < filter_count; k++) {
20862306a36Sopenharmony_ci				new_filtered_suite = kunit_filter_attr_tests(filtered_suite,
20962306a36Sopenharmony_ci						parsed_filters[k], filter_action, err);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci				/* Free previous copy of suite */
21262306a36Sopenharmony_ci				if (k > 0 || filter_glob) {
21362306a36Sopenharmony_ci					kfree(filtered_suite->test_cases);
21462306a36Sopenharmony_ci					kfree(filtered_suite);
21562306a36Sopenharmony_ci				}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci				filtered_suite = new_filtered_suite;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci				if (*err)
22062306a36Sopenharmony_ci					goto free_filtered_suite;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci				if (IS_ERR(filtered_suite)) {
22362306a36Sopenharmony_ci					*err = PTR_ERR(filtered_suite);
22462306a36Sopenharmony_ci					goto free_filtered_suite;
22562306a36Sopenharmony_ci				}
22662306a36Sopenharmony_ci				if (!filtered_suite)
22762306a36Sopenharmony_ci					break;
22862306a36Sopenharmony_ci			}
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		if (!filtered_suite)
23262306a36Sopenharmony_ci			continue;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		*copy++ = filtered_suite;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci	filtered.start = copy_start;
23762306a36Sopenharmony_ci	filtered.end = copy;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cifree_filtered_suite:
24062306a36Sopenharmony_ci	if (*err) {
24162306a36Sopenharmony_ci		for (suites = copy_start; suites < copy; suites++) {
24262306a36Sopenharmony_ci			kfree((*suites)->test_cases);
24362306a36Sopenharmony_ci			kfree(*suites);
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cifree_parsed_filters:
24862306a36Sopenharmony_ci	if (filter_count)
24962306a36Sopenharmony_ci		kfree(parsed_filters);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cifree_parsed_glob:
25262306a36Sopenharmony_ci	if (filter_glob) {
25362306a36Sopenharmony_ci		kfree(parsed_glob.suite_glob);
25462306a36Sopenharmony_ci		kfree(parsed_glob.test_glob);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cifree_copy:
25862306a36Sopenharmony_ci	if (*err)
25962306a36Sopenharmony_ci		kfree(copy_start);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	return filtered;
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_civoid kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	size_t num_suites = suite_set->end - suite_set->start;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (builtin || num_suites) {
26962306a36Sopenharmony_ci		pr_info("KTAP version 1\n");
27062306a36Sopenharmony_ci		pr_info("1..%zu\n", num_suites);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	__kunit_test_suites_init(suite_set->start, num_suites);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_civoid kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct kunit_suite * const *suites;
27962306a36Sopenharmony_ci	struct kunit_case *test_case;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Hack: print a ktap header so kunit.py can find the start of KUnit output. */
28262306a36Sopenharmony_ci	pr_info("KTAP version 1\n");
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	for (suites = suite_set->start; suites < suite_set->end; suites++) {
28562306a36Sopenharmony_ci		/* Print suite name and suite attributes */
28662306a36Sopenharmony_ci		pr_info("%s\n", (*suites)->name);
28762306a36Sopenharmony_ci		if (include_attr)
28862306a36Sopenharmony_ci			kunit_print_attr((void *)(*suites), false, 0);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		/* Print test case name and attributes in suite */
29162306a36Sopenharmony_ci		kunit_suite_for_each_test_case((*suites), test_case) {
29262306a36Sopenharmony_ci			pr_info("%s.%s\n", (*suites)->name, test_case->name);
29362306a36Sopenharmony_ci			if (include_attr)
29462306a36Sopenharmony_ci				kunit_print_attr((void *)test_case, true, 0);
29562306a36Sopenharmony_ci		}
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_KUNIT)
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic char *kunit_shutdown;
30262306a36Sopenharmony_cicore_param(kunit_shutdown, kunit_shutdown, charp, 0644);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic void kunit_handle_shutdown(void)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	if (!kunit_shutdown)
30762306a36Sopenharmony_ci		return;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!strcmp(kunit_shutdown, "poweroff"))
31062306a36Sopenharmony_ci		kernel_power_off();
31162306a36Sopenharmony_ci	else if (!strcmp(kunit_shutdown, "halt"))
31262306a36Sopenharmony_ci		kernel_halt();
31362306a36Sopenharmony_ci	else if (!strcmp(kunit_shutdown, "reboot"))
31462306a36Sopenharmony_ci		kernel_restart(NULL);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciint kunit_run_all_tests(void)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct kunit_suite_set suite_set = {
32162306a36Sopenharmony_ci		__kunit_suites_start, __kunit_suites_end,
32262306a36Sopenharmony_ci	};
32362306a36Sopenharmony_ci	int err = 0;
32462306a36Sopenharmony_ci	if (!kunit_enabled()) {
32562306a36Sopenharmony_ci		pr_info("kunit: disabled\n");
32662306a36Sopenharmony_ci		goto out;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (filter_glob_param || filter_param) {
33062306a36Sopenharmony_ci		suite_set = kunit_filter_suites(&suite_set, filter_glob_param,
33162306a36Sopenharmony_ci				filter_param, filter_action_param, &err);
33262306a36Sopenharmony_ci		if (err) {
33362306a36Sopenharmony_ci			pr_err("kunit executor: error filtering suites: %d\n", err);
33462306a36Sopenharmony_ci			goto out;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (!action_param)
33962306a36Sopenharmony_ci		kunit_exec_run_tests(&suite_set, true);
34062306a36Sopenharmony_ci	else if (strcmp(action_param, "list") == 0)
34162306a36Sopenharmony_ci		kunit_exec_list_tests(&suite_set, false);
34262306a36Sopenharmony_ci	else if (strcmp(action_param, "list_attr") == 0)
34362306a36Sopenharmony_ci		kunit_exec_list_tests(&suite_set, true);
34462306a36Sopenharmony_ci	else
34562306a36Sopenharmony_ci		pr_err("kunit executor: unknown action '%s'\n", action_param);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (filter_glob_param || filter_param) { /* a copy was made of each suite */
34862306a36Sopenharmony_ci		kunit_free_suite_set(suite_set);
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ciout:
35262306a36Sopenharmony_ci	kunit_handle_shutdown();
35362306a36Sopenharmony_ci	return err;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci#if IS_BUILTIN(CONFIG_KUNIT_TEST)
35762306a36Sopenharmony_ci#include "executor_test.c"
35862306a36Sopenharmony_ci#endif
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci#endif /* IS_BUILTIN(CONFIG_KUNIT) */
361