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 14 const 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 29 static volatile int exit_req; 30 static bool quiet, print_skipped, list; 31 32 #define MAX_SCX_TESTS 2048 33 34 static struct scx_test __scx_tests[MAX_SCX_TESTS]; 35 static unsigned __scx_num_tests = 0; 36 37 static void sigint_handler(int simple) 38 { 39 exit_req = 1; 40 } 41 42 static 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 static const char *status_to_result(enum scx_test_status status) 52 { 53 switch (status) { 54 case SCX_TEST_PASS: 55 case SCX_TEST_SKIP: 56 return "ok"; 57 case SCX_TEST_FAIL: 58 return "not ok"; 59 default: 60 return "<UNKNOWN>"; 61 } 62 } 63 64 static void print_test_result(const struct scx_test *test, 65 enum scx_test_status status, 66 unsigned int testnum) 67 { 68 const char *result = status_to_result(status); 69 const char *directive = status == SCX_TEST_SKIP ? "SKIP " : ""; 70 71 printf("%s %u %s # %s\n", result, testnum, test->name, directive); 72 printf("===== END =====\n"); 73 } 74 75 static bool should_skip_test(const struct scx_test *test, const char * filter) 76 { 77 return !strstr(test->name, filter); 78 } 79 80 static enum scx_test_status run_test(const struct scx_test *test) 81 { 82 enum scx_test_status status; 83 void *context = NULL; 84 85 if (test->setup) { 86 status = test->setup(&context); 87 if (status != SCX_TEST_PASS) 88 return status; 89 } 90 91 status = test->run(context); 92 93 if (test->cleanup) 94 test->cleanup(context); 95 96 return status; 97 } 98 99 static bool test_valid(const struct scx_test *test) 100 { 101 if (!test) { 102 fprintf(stderr, "NULL test detected\n"); 103 return false; 104 } 105 106 if (!test->name) { 107 fprintf(stderr, 108 "Test with no name found. Must specify test name.\n"); 109 return false; 110 } 111 112 if (!test->description) { 113 fprintf(stderr, "Test %s requires description.\n", test->name); 114 return false; 115 } 116 117 if (!test->run) { 118 fprintf(stderr, "Test %s has no run() callback\n", test->name); 119 return false; 120 } 121 122 return true; 123 } 124 125 int main(int argc, char **argv) 126 { 127 const char *filter = NULL; 128 unsigned testnum = 0, i; 129 unsigned passed = 0, skipped = 0, failed = 0; 130 int opt; 131 132 signal(SIGINT, sigint_handler); 133 signal(SIGTERM, sigint_handler); 134 135 libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 136 137 while ((opt = getopt(argc, argv, "qslt:h")) != -1) { 138 switch (opt) { 139 case 'q': 140 quiet = true; 141 break; 142 case 's': 143 print_skipped = true; 144 break; 145 case 'l': 146 list = true; 147 break; 148 case 't': 149 filter = optarg; 150 break; 151 default: 152 fprintf(stderr, help_fmt, basename(argv[0])); 153 return opt != 'h'; 154 } 155 } 156 157 for (i = 0; i < __scx_num_tests; i++) { 158 enum scx_test_status status; 159 struct scx_test *test = &__scx_tests[i]; 160 161 if (list) { 162 printf("%s\n", test->name); 163 if (i == (__scx_num_tests - 1)) 164 return 0; 165 continue; 166 } 167 168 if (filter && should_skip_test(test, filter)) { 169 /* 170 * Printing the skipped tests and their preambles can 171 * add a lot of noise to the runner output. Printing 172 * this is only really useful for CI, so let's skip it 173 * by default. 174 */ 175 if (print_skipped) { 176 print_test_preamble(test, quiet); 177 print_test_result(test, SCX_TEST_SKIP, ++testnum); 178 } 179 continue; 180 } 181 182 print_test_preamble(test, quiet); 183 status = run_test(test); 184 print_test_result(test, status, ++testnum); 185 switch (status) { 186 case SCX_TEST_PASS: 187 passed++; 188 break; 189 case SCX_TEST_SKIP: 190 skipped++; 191 break; 192 case SCX_TEST_FAIL: 193 failed++; 194 break; 195 } 196 } 197 printf("\n\n=============================\n\n"); 198 printf("RESULTS:\n\n"); 199 printf("PASSED: %u\n", passed); 200 printf("SKIPPED: %u\n", skipped); 201 printf("FAILED: %u\n", failed); 202 203 return 0; 204 } 205 206 void scx_test_register(struct scx_test *test) 207 { 208 SCX_BUG_ON(!test_valid(test), "Invalid test found"); 209 SCX_BUG_ON(__scx_num_tests >= MAX_SCX_TESTS, "Maximum tests exceeded"); 210 211 __scx_tests[__scx_num_tests++] = *test; 212 } 213