File Index
Files and directories
Sign in
FILE

runner.c

File preview with syntax highlighting.

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