xref: /linux/tools/testing/selftests/sched_ext/runner.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1*a5db7817SDavid Vernet /* SPDX-License-Identifier: GPL-2.0 */
2*a5db7817SDavid Vernet /*
3*a5db7817SDavid Vernet  * Copyright (c) 2024 Meta Platforms, Inc. and affiliates.
4*a5db7817SDavid Vernet  * Copyright (c) 2024 David Vernet <dvernet@meta.com>
5*a5db7817SDavid Vernet  * Copyright (c) 2024 Tejun Heo <tj@kernel.org>
6*a5db7817SDavid Vernet  */
7*a5db7817SDavid Vernet #include <stdio.h>
8*a5db7817SDavid Vernet #include <unistd.h>
9*a5db7817SDavid Vernet #include <signal.h>
10*a5db7817SDavid Vernet #include <libgen.h>
11*a5db7817SDavid Vernet #include <bpf/bpf.h>
12*a5db7817SDavid Vernet #include "scx_test.h"
13*a5db7817SDavid Vernet 
14*a5db7817SDavid Vernet const char help_fmt[] =
15*a5db7817SDavid Vernet "The runner for sched_ext tests.\n"
16*a5db7817SDavid Vernet "\n"
17*a5db7817SDavid Vernet "The runner is statically linked against all testcases, and runs them all serially.\n"
18*a5db7817SDavid Vernet "It's required for the testcases to be serial, as only a single host-wide sched_ext\n"
19*a5db7817SDavid Vernet "scheduler may be loaded at any given time."
20*a5db7817SDavid Vernet "\n"
21*a5db7817SDavid Vernet "Usage: %s [-t TEST] [-h]\n"
22*a5db7817SDavid Vernet "\n"
23*a5db7817SDavid Vernet "  -t TEST       Only run tests whose name includes this string\n"
24*a5db7817SDavid Vernet "  -s            Include print output for skipped tests\n"
25*a5db7817SDavid Vernet "  -q            Don't print the test descriptions during run\n"
26*a5db7817SDavid Vernet "  -h            Display this help and exit\n";
27*a5db7817SDavid Vernet 
28*a5db7817SDavid Vernet static volatile int exit_req;
29*a5db7817SDavid Vernet static bool quiet, print_skipped;
30*a5db7817SDavid Vernet 
31*a5db7817SDavid Vernet #define MAX_SCX_TESTS 2048
32*a5db7817SDavid Vernet 
33*a5db7817SDavid Vernet static struct scx_test __scx_tests[MAX_SCX_TESTS];
34*a5db7817SDavid Vernet static unsigned __scx_num_tests = 0;
35*a5db7817SDavid Vernet 
sigint_handler(int simple)36*a5db7817SDavid Vernet static void sigint_handler(int simple)
37*a5db7817SDavid Vernet {
38*a5db7817SDavid Vernet 	exit_req = 1;
39*a5db7817SDavid Vernet }
40*a5db7817SDavid Vernet 
print_test_preamble(const struct scx_test * test,bool quiet)41*a5db7817SDavid Vernet static void print_test_preamble(const struct scx_test *test, bool quiet)
42*a5db7817SDavid Vernet {
43*a5db7817SDavid Vernet 	printf("===== START =====\n");
44*a5db7817SDavid Vernet 	printf("TEST: %s\n", test->name);
45*a5db7817SDavid Vernet 	if (!quiet)
46*a5db7817SDavid Vernet 		printf("DESCRIPTION: %s\n", test->description);
47*a5db7817SDavid Vernet 	printf("OUTPUT:\n");
48*a5db7817SDavid Vernet }
49*a5db7817SDavid Vernet 
status_to_result(enum scx_test_status status)50*a5db7817SDavid Vernet static const char *status_to_result(enum scx_test_status status)
51*a5db7817SDavid Vernet {
52*a5db7817SDavid Vernet 	switch (status) {
53*a5db7817SDavid Vernet 	case SCX_TEST_PASS:
54*a5db7817SDavid Vernet 	case SCX_TEST_SKIP:
55*a5db7817SDavid Vernet 		return "ok";
56*a5db7817SDavid Vernet 	case SCX_TEST_FAIL:
57*a5db7817SDavid Vernet 		return "not ok";
58*a5db7817SDavid Vernet 	default:
59*a5db7817SDavid Vernet 		return "<UNKNOWN>";
60*a5db7817SDavid Vernet 	}
61*a5db7817SDavid Vernet }
62*a5db7817SDavid Vernet 
print_test_result(const struct scx_test * test,enum scx_test_status status,unsigned int testnum)63*a5db7817SDavid Vernet static void print_test_result(const struct scx_test *test,
64*a5db7817SDavid Vernet 			      enum scx_test_status status,
65*a5db7817SDavid Vernet 			      unsigned int testnum)
66*a5db7817SDavid Vernet {
67*a5db7817SDavid Vernet 	const char *result = status_to_result(status);
68*a5db7817SDavid Vernet 	const char *directive = status == SCX_TEST_SKIP ? "SKIP " : "";
69*a5db7817SDavid Vernet 
70*a5db7817SDavid Vernet 	printf("%s %u %s # %s\n", result, testnum, test->name, directive);
71*a5db7817SDavid Vernet 	printf("=====  END  =====\n");
72*a5db7817SDavid Vernet }
73*a5db7817SDavid Vernet 
should_skip_test(const struct scx_test * test,const char * filter)74*a5db7817SDavid Vernet static bool should_skip_test(const struct scx_test *test, const char * filter)
75*a5db7817SDavid Vernet {
76*a5db7817SDavid Vernet 	return !strstr(test->name, filter);
77*a5db7817SDavid Vernet }
78*a5db7817SDavid Vernet 
run_test(const struct scx_test * test)79*a5db7817SDavid Vernet static enum scx_test_status run_test(const struct scx_test *test)
80*a5db7817SDavid Vernet {
81*a5db7817SDavid Vernet 	enum scx_test_status status;
82*a5db7817SDavid Vernet 	void *context = NULL;
83*a5db7817SDavid Vernet 
84*a5db7817SDavid Vernet 	if (test->setup) {
85*a5db7817SDavid Vernet 		status = test->setup(&context);
86*a5db7817SDavid Vernet 		if (status != SCX_TEST_PASS)
87*a5db7817SDavid Vernet 			return status;
88*a5db7817SDavid Vernet 	}
89*a5db7817SDavid Vernet 
90*a5db7817SDavid Vernet 	status = test->run(context);
91*a5db7817SDavid Vernet 
92*a5db7817SDavid Vernet 	if (test->cleanup)
93*a5db7817SDavid Vernet 		test->cleanup(context);
94*a5db7817SDavid Vernet 
95*a5db7817SDavid Vernet 	return status;
96*a5db7817SDavid Vernet }
97*a5db7817SDavid Vernet 
test_valid(const struct scx_test * test)98*a5db7817SDavid Vernet static bool test_valid(const struct scx_test *test)
99*a5db7817SDavid Vernet {
100*a5db7817SDavid Vernet 	if (!test) {
101*a5db7817SDavid Vernet 		fprintf(stderr, "NULL test detected\n");
102*a5db7817SDavid Vernet 		return false;
103*a5db7817SDavid Vernet 	}
104*a5db7817SDavid Vernet 
105*a5db7817SDavid Vernet 	if (!test->name) {
106*a5db7817SDavid Vernet 		fprintf(stderr,
107*a5db7817SDavid Vernet 			"Test with no name found. Must specify test name.\n");
108*a5db7817SDavid Vernet 		return false;
109*a5db7817SDavid Vernet 	}
110*a5db7817SDavid Vernet 
111*a5db7817SDavid Vernet 	if (!test->description) {
112*a5db7817SDavid Vernet 		fprintf(stderr, "Test %s requires description.\n", test->name);
113*a5db7817SDavid Vernet 		return false;
114*a5db7817SDavid Vernet 	}
115*a5db7817SDavid Vernet 
116*a5db7817SDavid Vernet 	if (!test->run) {
117*a5db7817SDavid Vernet 		fprintf(stderr, "Test %s has no run() callback\n", test->name);
118*a5db7817SDavid Vernet 		return false;
119*a5db7817SDavid Vernet 	}
120*a5db7817SDavid Vernet 
121*a5db7817SDavid Vernet 	return true;
122*a5db7817SDavid Vernet }
123*a5db7817SDavid Vernet 
main(int argc,char ** argv)124*a5db7817SDavid Vernet int main(int argc, char **argv)
125*a5db7817SDavid Vernet {
126*a5db7817SDavid Vernet 	const char *filter = NULL;
127*a5db7817SDavid Vernet 	unsigned testnum = 0, i;
128*a5db7817SDavid Vernet 	unsigned passed = 0, skipped = 0, failed = 0;
129*a5db7817SDavid Vernet 	int opt;
130*a5db7817SDavid Vernet 
131*a5db7817SDavid Vernet 	signal(SIGINT, sigint_handler);
132*a5db7817SDavid Vernet 	signal(SIGTERM, sigint_handler);
133*a5db7817SDavid Vernet 
134*a5db7817SDavid Vernet 	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
135*a5db7817SDavid Vernet 
136*a5db7817SDavid Vernet 	while ((opt = getopt(argc, argv, "qst:h")) != -1) {
137*a5db7817SDavid Vernet 		switch (opt) {
138*a5db7817SDavid Vernet 		case 'q':
139*a5db7817SDavid Vernet 			quiet = true;
140*a5db7817SDavid Vernet 			break;
141*a5db7817SDavid Vernet 		case 's':
142*a5db7817SDavid Vernet 			print_skipped = true;
143*a5db7817SDavid Vernet 			break;
144*a5db7817SDavid Vernet 		case 't':
145*a5db7817SDavid Vernet 			filter = optarg;
146*a5db7817SDavid Vernet 			break;
147*a5db7817SDavid Vernet 		default:
148*a5db7817SDavid Vernet 			fprintf(stderr, help_fmt, basename(argv[0]));
149*a5db7817SDavid Vernet 			return opt != 'h';
150*a5db7817SDavid Vernet 		}
151*a5db7817SDavid Vernet 	}
152*a5db7817SDavid Vernet 
153*a5db7817SDavid Vernet 	for (i = 0; i < __scx_num_tests; i++) {
154*a5db7817SDavid Vernet 		enum scx_test_status status;
155*a5db7817SDavid Vernet 		struct scx_test *test = &__scx_tests[i];
156*a5db7817SDavid Vernet 
157*a5db7817SDavid Vernet 		if (filter && should_skip_test(test, filter)) {
158*a5db7817SDavid Vernet 			/*
159*a5db7817SDavid Vernet 			 * Printing the skipped tests and their preambles can
160*a5db7817SDavid Vernet 			 * add a lot of noise to the runner output. Printing
161*a5db7817SDavid Vernet 			 * this is only really useful for CI, so let's skip it
162*a5db7817SDavid Vernet 			 * by default.
163*a5db7817SDavid Vernet 			 */
164*a5db7817SDavid Vernet 			if (print_skipped) {
165*a5db7817SDavid Vernet 				print_test_preamble(test, quiet);
166*a5db7817SDavid Vernet 				print_test_result(test, SCX_TEST_SKIP, ++testnum);
167*a5db7817SDavid Vernet 			}
168*a5db7817SDavid Vernet 			continue;
169*a5db7817SDavid Vernet 		}
170*a5db7817SDavid Vernet 
171*a5db7817SDavid Vernet 		print_test_preamble(test, quiet);
172*a5db7817SDavid Vernet 		status = run_test(test);
173*a5db7817SDavid Vernet 		print_test_result(test, status, ++testnum);
174*a5db7817SDavid Vernet 		switch (status) {
175*a5db7817SDavid Vernet 		case SCX_TEST_PASS:
176*a5db7817SDavid Vernet 			passed++;
177*a5db7817SDavid Vernet 			break;
178*a5db7817SDavid Vernet 		case SCX_TEST_SKIP:
179*a5db7817SDavid Vernet 			skipped++;
180*a5db7817SDavid Vernet 			break;
181*a5db7817SDavid Vernet 		case SCX_TEST_FAIL:
182*a5db7817SDavid Vernet 			failed++;
183*a5db7817SDavid Vernet 			break;
184*a5db7817SDavid Vernet 		}
185*a5db7817SDavid Vernet 	}
186*a5db7817SDavid Vernet 	printf("\n\n=============================\n\n");
187*a5db7817SDavid Vernet 	printf("RESULTS:\n\n");
188*a5db7817SDavid Vernet 	printf("PASSED:  %u\n", passed);
189*a5db7817SDavid Vernet 	printf("SKIPPED: %u\n", skipped);
190*a5db7817SDavid Vernet 	printf("FAILED:  %u\n", failed);
191*a5db7817SDavid Vernet 
192*a5db7817SDavid Vernet 	return 0;
193*a5db7817SDavid Vernet }
194*a5db7817SDavid Vernet 
scx_test_register(struct scx_test * test)195*a5db7817SDavid Vernet void scx_test_register(struct scx_test *test)
196*a5db7817SDavid Vernet {
197*a5db7817SDavid Vernet 	SCX_BUG_ON(!test_valid(test), "Invalid test found");
198*a5db7817SDavid Vernet 	SCX_BUG_ON(__scx_num_tests >= MAX_SCX_TESTS, "Maximum tests exceeded");
199*a5db7817SDavid Vernet 
200*a5db7817SDavid Vernet 	__scx_tests[__scx_num_tests++] = *test;
201*a5db7817SDavid Vernet }
202