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