···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.44+ * Copyright (c) 2023 David Vernet <dvernet@meta.com>55+ * Copyright (c) 2023 Tejun Heo <tj@kernel.org>66+ */77+#include <stdio.h>88+#include <unistd.h>99+#include <sched.h>1010+#include <bpf/bpf.h>1111+#include <scx/common.h>1212+#include <sys/wait.h>1313+#include "scx_test.h"1414+#include "init_enable_count.bpf.skel.h"1515+1616+#define SCHED_EXT 71717+1818+static struct init_enable_count *1919+open_load_prog(bool global)2020+{2121+ struct init_enable_count *skel;2222+2323+ skel = init_enable_count__open();2424+ SCX_BUG_ON(!skel, "Failed to open skel");2525+2626+ if (!global)2727+ skel->struct_ops.init_enable_count_ops->flags |= SCX_OPS_SWITCH_PARTIAL;2828+2929+ SCX_BUG_ON(init_enable_count__load(skel), "Failed to load skel");3030+3131+ return skel;3232+}3333+3434+static enum scx_test_status run_test(bool global)3535+{3636+ struct init_enable_count *skel;3737+ struct bpf_link *link;3838+ const u32 num_children = 5, num_pre_forks = 1024;3939+ int ret, i, status;4040+ struct sched_param param = {};4141+ pid_t pids[num_pre_forks];4242+4343+ skel = open_load_prog(global);4444+4545+ /*4646+ * Fork a bunch of children before we attach the scheduler so that we4747+ * ensure (at least in practical terms) that there are more tasks that4848+ * transition from SCHED_OTHER -> SCHED_EXT than there are tasks that4949+ * take the fork() path either below or in other processes.5050+ */5151+ for (i = 0; i < num_pre_forks; i++) {5252+ pids[i] = fork();5353+ SCX_FAIL_IF(pids[i] < 0, "Failed to fork child");5454+ if (pids[i] == 0) {5555+ sleep(1);5656+ exit(0);5757+ }5858+ }5959+6060+ link = bpf_map__attach_struct_ops(skel->maps.init_enable_count_ops);6161+ SCX_FAIL_IF(!link, "Failed to attach struct_ops");6262+6363+ for (i = 0; i < num_pre_forks; i++) {6464+ SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],6565+ "Failed to wait for pre-forked child\n");6666+6767+ SCX_FAIL_IF(status != 0, "Pre-forked child %d exited with status %d\n", i,6868+ status);6969+ }7070+7171+ bpf_link__destroy(link);7272+ SCX_GE(skel->bss->init_task_cnt, num_pre_forks);7373+ SCX_GE(skel->bss->exit_task_cnt, num_pre_forks);7474+7575+ link = bpf_map__attach_struct_ops(skel->maps.init_enable_count_ops);7676+ SCX_FAIL_IF(!link, "Failed to attach struct_ops");7777+7878+ /* SCHED_EXT children */7979+ for (i = 0; i < num_children; i++) {8080+ pids[i] = fork();8181+ SCX_FAIL_IF(pids[i] < 0, "Failed to fork child");8282+8383+ if (pids[i] == 0) {8484+ ret = sched_setscheduler(0, SCHED_EXT, ¶m);8585+ SCX_BUG_ON(ret, "Failed to set sched to sched_ext");8686+8787+ /*8888+ * Reset to SCHED_OTHER for half of them. Counts for8989+ * everything should still be the same regardless, as9090+ * ops.disable() is invoked even if a task is still on9191+ * SCHED_EXT before it exits.9292+ */9393+ if (i % 2 == 0) {9494+ ret = sched_setscheduler(0, SCHED_OTHER, ¶m);9595+ SCX_BUG_ON(ret, "Failed to reset sched to normal");9696+ }9797+ exit(0);9898+ }9999+ }100100+ for (i = 0; i < num_children; i++) {101101+ SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],102102+ "Failed to wait for SCX child\n");103103+104104+ SCX_FAIL_IF(status != 0, "SCX child %d exited with status %d\n", i,105105+ status);106106+ }107107+108108+ /* SCHED_OTHER children */109109+ for (i = 0; i < num_children; i++) {110110+ pids[i] = fork();111111+ if (pids[i] == 0)112112+ exit(0);113113+ }114114+115115+ for (i = 0; i < num_children; i++) {116116+ SCX_FAIL_IF(waitpid(pids[i], &status, 0) != pids[i],117117+ "Failed to wait for normal child\n");118118+119119+ SCX_FAIL_IF(status != 0, "Normal child %d exited with status %d\n", i,120120+ status);121121+ }122122+123123+ bpf_link__destroy(link);124124+125125+ SCX_GE(skel->bss->init_task_cnt, 2 * num_children);126126+ SCX_GE(skel->bss->exit_task_cnt, 2 * num_children);127127+128128+ if (global) {129129+ SCX_GE(skel->bss->enable_cnt, 2 * num_children);130130+ SCX_GE(skel->bss->disable_cnt, 2 * num_children);131131+ } else {132132+ SCX_EQ(skel->bss->enable_cnt, num_children);133133+ SCX_EQ(skel->bss->disable_cnt, num_children);134134+ }135135+ /*136136+ * We forked a ton of tasks before we attached the scheduler above, so137137+ * this should be fine. Technically it could be flaky if a ton of forks138138+ * are happening at the same time in other processes, but that should139139+ * be exceedingly unlikely.140140+ */141141+ SCX_GT(skel->bss->init_transition_cnt, skel->bss->init_fork_cnt);142142+ SCX_GE(skel->bss->init_fork_cnt, 2 * num_children);143143+144144+ init_enable_count__destroy(skel);145145+146146+ return SCX_TEST_PASS;147147+}148148+149149+static enum scx_test_status run(void *ctx)150150+{151151+ enum scx_test_status status;152152+153153+ status = run_test(true);154154+ if (status != SCX_TEST_PASS)155155+ return status;156156+157157+ return run_test(false);158158+}159159+160160+struct scx_test init_enable_count = {161161+ .name = "init_enable_count",162162+ .description = "Verify we do the correct amount of counting of init, "163163+ "enable, etc callbacks.",164164+ .run = run,165165+};166166+REGISTER_SCX_TEST(&init_enable_count)
···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * A completely minimal scheduler.44+ *55+ * This scheduler defines the absolute minimal set of struct sched_ext_ops66+ * fields: its name. It should _not_ fail to be loaded, and can be used to77+ * exercise the default scheduling paths in ext.c.88+ *99+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.1010+ * Copyright (c) 2023 David Vernet <dvernet@meta.com>1111+ * Copyright (c) 2023 Tejun Heo <tj@kernel.org>1212+ */1313+1414+#include <scx/common.bpf.h>1515+1616+char _license[] SEC("license") = "GPL";1717+1818+SEC(".struct_ops.link")1919+struct sched_ext_ops minimal_ops = {2020+ .name = "minimal",2121+};
+58
tools/testing/selftests/sched_ext/minimal.c
···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.44+ * Copyright (c) 2023 David Vernet <dvernet@meta.com>55+ * Copyright (c) 2023 Tejun Heo <tj@kernel.org>66+ */77+#include <bpf/bpf.h>88+#include <scx/common.h>99+#include <sys/wait.h>1010+#include <unistd.h>1111+#include "minimal.bpf.skel.h"1212+#include "scx_test.h"1313+1414+static enum scx_test_status setup(void **ctx)1515+{1616+ struct minimal *skel;1717+1818+ skel = minimal__open_and_load();1919+ if (!skel) {2020+ SCX_ERR("Failed to open and load skel");2121+ return SCX_TEST_FAIL;2222+ }2323+ *ctx = skel;2424+2525+ return SCX_TEST_PASS;2626+}2727+2828+static enum scx_test_status run(void *ctx)2929+{3030+ struct minimal *skel = ctx;3131+ struct bpf_link *link;3232+3333+ link = bpf_map__attach_struct_ops(skel->maps.minimal_ops);3434+ if (!link) {3535+ SCX_ERR("Failed to attach scheduler");3636+ return SCX_TEST_FAIL;3737+ }3838+3939+ bpf_link__destroy(link);4040+4141+ return SCX_TEST_PASS;4242+}4343+4444+static void cleanup(void *ctx)4545+{4646+ struct minimal *skel = ctx;4747+4848+ minimal__destroy(skel);4949+}5050+5151+struct scx_test minimal = {5252+ .name = "minimal",5353+ .description = "Verify we can load a fully minimal scheduler",5454+ .setup = setup,5555+ .run = run,5656+ .cleanup = cleanup,5757+};5858+REGISTER_SCX_TEST(&minimal)
+32
tools/testing/selftests/sched_ext/prog_run.bpf.c
···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * A scheduler that validates that we can invoke sched_ext kfuncs in44+ * BPF_PROG_TYPE_SYSCALL programs.55+ *66+ * Copyright (c) 2024 Meta Platforms, Inc. and affiliates.77+ * Copyright (c) 2024 David Vernet <dvernet@meta.com>88+ */99+1010+#include <scx/common.bpf.h>1111+1212+UEI_DEFINE(uei);1313+1414+char _license[] SEC("license") = "GPL";1515+1616+SEC("syscall")1717+int BPF_PROG(prog_run_syscall)1818+{1919+ scx_bpf_exit(0xdeadbeef, "Exited from PROG_RUN");2020+ return 0;2121+}2222+2323+void BPF_STRUCT_OPS(prog_run_exit, struct scx_exit_info *ei)2424+{2525+ UEI_RECORD(uei, ei);2626+}2727+2828+SEC(".struct_ops.link")2929+struct sched_ext_ops prog_run_ops = {3030+ .exit = prog_run_exit,3131+ .name = "prog_run",3232+};
+78
tools/testing/selftests/sched_ext/prog_run.c
···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * Copyright (c) 2024 Meta Platforms, Inc. and affiliates.44+ * Copyright (c) 2024 David Vernet <dvernet@meta.com>55+ */66+#include <bpf/bpf.h>77+#include <sched.h>88+#include <scx/common.h>99+#include <sys/wait.h>1010+#include <unistd.h>1111+#include "prog_run.bpf.skel.h"1212+#include "scx_test.h"1313+1414+static enum scx_test_status setup(void **ctx)1515+{1616+ struct prog_run *skel;1717+1818+ skel = prog_run__open_and_load();1919+ if (!skel) {2020+ SCX_ERR("Failed to open and load skel");2121+ return SCX_TEST_FAIL;2222+ }2323+ *ctx = skel;2424+2525+ return SCX_TEST_PASS;2626+}2727+2828+static enum scx_test_status run(void *ctx)2929+{3030+ struct prog_run *skel = ctx;3131+ struct bpf_link *link;3232+ int prog_fd, err = 0;3333+3434+ prog_fd = bpf_program__fd(skel->progs.prog_run_syscall);3535+ if (prog_fd < 0) {3636+ SCX_ERR("Failed to get BPF_PROG_RUN prog");3737+ return SCX_TEST_FAIL;3838+ }3939+4040+ LIBBPF_OPTS(bpf_test_run_opts, topts);4141+4242+ link = bpf_map__attach_struct_ops(skel->maps.prog_run_ops);4343+ if (!link) {4444+ SCX_ERR("Failed to attach scheduler");4545+ close(prog_fd);4646+ return SCX_TEST_FAIL;4747+ }4848+4949+ err = bpf_prog_test_run_opts(prog_fd, &topts);5050+ SCX_EQ(err, 0);5151+5252+ /* Assumes uei.kind is written last */5353+ while (skel->data->uei.kind == EXIT_KIND(SCX_EXIT_NONE))5454+ sched_yield();5555+5656+ SCX_EQ(skel->data->uei.kind, EXIT_KIND(SCX_EXIT_UNREG_BPF));5757+ SCX_EQ(skel->data->uei.exit_code, 0xdeadbeef);5858+ close(prog_fd);5959+ bpf_link__destroy(link);6060+6161+ return SCX_TEST_PASS;6262+}6363+6464+static void cleanup(void *ctx)6565+{6666+ struct prog_run *skel = ctx;6767+6868+ prog_run__destroy(skel);6969+}7070+7171+struct scx_test prog_run = {7272+ .name = "prog_run",7373+ .description = "Verify we can call into a scheduler with BPF_PROG_RUN, and invoke kfuncs",7474+ .setup = setup,7575+ .run = run,7676+ .cleanup = cleanup,7777+};7878+REGISTER_SCX_TEST(&prog_run)
+75
tools/testing/selftests/sched_ext/reload_loop.c
···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * Copyright (c) 2024 Meta Platforms, Inc. and affiliates.44+ * Copyright (c) 2024 David Vernet <dvernet@meta.com>55+ */66+#include <bpf/bpf.h>77+#include <pthread.h>88+#include <scx/common.h>99+#include <sys/wait.h>1010+#include <unistd.h>1111+#include "maximal.bpf.skel.h"1212+#include "scx_test.h"1313+1414+static struct maximal *skel;1515+static pthread_t threads[2];1616+1717+bool force_exit = false;1818+1919+static enum scx_test_status setup(void **ctx)2020+{2121+ skel = maximal__open_and_load();2222+ if (!skel) {2323+ SCX_ERR("Failed to open and load skel");2424+ return SCX_TEST_FAIL;2525+ }2626+2727+ return SCX_TEST_PASS;2828+}2929+3030+static void *do_reload_loop(void *arg)3131+{3232+ u32 i;3333+3434+ for (i = 0; i < 1024 && !force_exit; i++) {3535+ struct bpf_link *link;3636+3737+ link = bpf_map__attach_struct_ops(skel->maps.maximal_ops);3838+ if (link)3939+ bpf_link__destroy(link);4040+ }4141+4242+ return NULL;4343+}4444+4545+static enum scx_test_status run(void *ctx)4646+{4747+ int err;4848+ void *ret;4949+5050+ err = pthread_create(&threads[0], NULL, do_reload_loop, NULL);5151+ SCX_FAIL_IF(err, "Failed to create thread 0");5252+5353+ err = pthread_create(&threads[1], NULL, do_reload_loop, NULL);5454+ SCX_FAIL_IF(err, "Failed to create thread 1");5555+5656+ SCX_FAIL_IF(pthread_join(threads[0], &ret), "thread 0 failed");5757+ SCX_FAIL_IF(pthread_join(threads[1], &ret), "thread 1 failed");5858+5959+ return SCX_TEST_PASS;6060+}6161+6262+static void cleanup(void *ctx)6363+{6464+ force_exit = true;6565+ maximal__destroy(skel);6666+}6767+6868+struct scx_test reload_loop = {6969+ .name = "reload_loop",7070+ .description = "Stress test loading and unloading schedulers repeatedly in a tight loop",7171+ .setup = setup,7272+ .run = run,7373+ .cleanup = cleanup,7474+};7575+REGISTER_SCX_TEST(&reload_loop)
+201
tools/testing/selftests/sched_ext/runner.c
···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * Copyright (c) 2024 Meta Platforms, Inc. and affiliates.44+ * Copyright (c) 2024 David Vernet <dvernet@meta.com>55+ * Copyright (c) 2024 Tejun Heo <tj@kernel.org>66+ */77+#include <stdio.h>88+#include <unistd.h>99+#include <signal.h>1010+#include <libgen.h>1111+#include <bpf/bpf.h>1212+#include "scx_test.h"1313+1414+const char help_fmt[] =1515+"The runner for sched_ext tests.\n"1616+"\n"1717+"The runner is statically linked against all testcases, and runs them all serially.\n"1818+"It's required for the testcases to be serial, as only a single host-wide sched_ext\n"1919+"scheduler may be loaded at any given time."2020+"\n"2121+"Usage: %s [-t TEST] [-h]\n"2222+"\n"2323+" -t TEST Only run tests whose name includes this string\n"2424+" -s Include print output for skipped tests\n"2525+" -q Don't print the test descriptions during run\n"2626+" -h Display this help and exit\n";2727+2828+static volatile int exit_req;2929+static bool quiet, print_skipped;3030+3131+#define MAX_SCX_TESTS 20483232+3333+static struct scx_test __scx_tests[MAX_SCX_TESTS];3434+static unsigned __scx_num_tests = 0;3535+3636+static void sigint_handler(int simple)3737+{3838+ exit_req = 1;3939+}4040+4141+static void print_test_preamble(const struct scx_test *test, bool quiet)4242+{4343+ printf("===== START =====\n");4444+ printf("TEST: %s\n", test->name);4545+ if (!quiet)4646+ printf("DESCRIPTION: %s\n", test->description);4747+ printf("OUTPUT:\n");4848+}4949+5050+static const char *status_to_result(enum scx_test_status status)5151+{5252+ switch (status) {5353+ case SCX_TEST_PASS:5454+ case SCX_TEST_SKIP:5555+ return "ok";5656+ case SCX_TEST_FAIL:5757+ return "not ok";5858+ default:5959+ return "<UNKNOWN>";6060+ }6161+}6262+6363+static void print_test_result(const struct scx_test *test,6464+ enum scx_test_status status,6565+ unsigned int testnum)6666+{6767+ const char *result = status_to_result(status);6868+ const char *directive = status == SCX_TEST_SKIP ? "SKIP " : "";6969+7070+ printf("%s %u %s # %s\n", result, testnum, test->name, directive);7171+ printf("===== END =====\n");7272+}7373+7474+static bool should_skip_test(const struct scx_test *test, const char * filter)7575+{7676+ return !strstr(test->name, filter);7777+}7878+7979+static enum scx_test_status run_test(const struct scx_test *test)8080+{8181+ enum scx_test_status status;8282+ void *context = NULL;8383+8484+ if (test->setup) {8585+ status = test->setup(&context);8686+ if (status != SCX_TEST_PASS)8787+ return status;8888+ }8989+9090+ status = test->run(context);9191+9292+ if (test->cleanup)9393+ test->cleanup(context);9494+9595+ return status;9696+}9797+9898+static bool test_valid(const struct scx_test *test)9999+{100100+ if (!test) {101101+ fprintf(stderr, "NULL test detected\n");102102+ return false;103103+ }104104+105105+ if (!test->name) {106106+ fprintf(stderr,107107+ "Test with no name found. Must specify test name.\n");108108+ return false;109109+ }110110+111111+ if (!test->description) {112112+ fprintf(stderr, "Test %s requires description.\n", test->name);113113+ return false;114114+ }115115+116116+ if (!test->run) {117117+ fprintf(stderr, "Test %s has no run() callback\n", test->name);118118+ return false;119119+ }120120+121121+ return true;122122+}123123+124124+int main(int argc, char **argv)125125+{126126+ const char *filter = NULL;127127+ unsigned testnum = 0, i;128128+ unsigned passed = 0, skipped = 0, failed = 0;129129+ int opt;130130+131131+ signal(SIGINT, sigint_handler);132132+ signal(SIGTERM, sigint_handler);133133+134134+ libbpf_set_strict_mode(LIBBPF_STRICT_ALL);135135+136136+ while ((opt = getopt(argc, argv, "qst:h")) != -1) {137137+ switch (opt) {138138+ case 'q':139139+ quiet = true;140140+ break;141141+ case 's':142142+ print_skipped = true;143143+ break;144144+ case 't':145145+ filter = optarg;146146+ break;147147+ default:148148+ fprintf(stderr, help_fmt, basename(argv[0]));149149+ return opt != 'h';150150+ }151151+ }152152+153153+ for (i = 0; i < __scx_num_tests; i++) {154154+ enum scx_test_status status;155155+ struct scx_test *test = &__scx_tests[i];156156+157157+ if (filter && should_skip_test(test, filter)) {158158+ /*159159+ * Printing the skipped tests and their preambles can160160+ * add a lot of noise to the runner output. Printing161161+ * this is only really useful for CI, so let's skip it162162+ * by default.163163+ */164164+ if (print_skipped) {165165+ print_test_preamble(test, quiet);166166+ print_test_result(test, SCX_TEST_SKIP, ++testnum);167167+ }168168+ continue;169169+ }170170+171171+ print_test_preamble(test, quiet);172172+ status = run_test(test);173173+ print_test_result(test, status, ++testnum);174174+ switch (status) {175175+ case SCX_TEST_PASS:176176+ passed++;177177+ break;178178+ case SCX_TEST_SKIP:179179+ skipped++;180180+ break;181181+ case SCX_TEST_FAIL:182182+ failed++;183183+ break;184184+ }185185+ }186186+ printf("\n\n=============================\n\n");187187+ printf("RESULTS:\n\n");188188+ printf("PASSED: %u\n", passed);189189+ printf("SKIPPED: %u\n", skipped);190190+ printf("FAILED: %u\n", failed);191191+192192+ return 0;193193+}194194+195195+void scx_test_register(struct scx_test *test)196196+{197197+ SCX_BUG_ON(!test_valid(test), "Invalid test found");198198+ SCX_BUG_ON(__scx_num_tests >= MAX_SCX_TESTS, "Maximum tests exceeded");199199+200200+ __scx_tests[__scx_num_tests++] = *test;201201+}
+131
tools/testing/selftests/sched_ext/scx_test.h
···11+/* SPDX-License-Identifier: GPL-2.0 */22+/*33+ * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.44+ * Copyright (c) 2023 Tejun Heo <tj@kernel.org>55+ * Copyright (c) 2023 David Vernet <dvernet@meta.com>66+ */77+88+#ifndef __SCX_TEST_H__99+#define __SCX_TEST_H__1010+1111+#include <errno.h>1212+#include <scx/common.h>1313+#include <scx/compat.h>1414+1515+enum scx_test_status {1616+ SCX_TEST_PASS = 0,1717+ SCX_TEST_SKIP,1818+ SCX_TEST_FAIL,1919+};2020+2121+#define EXIT_KIND(__ent) __COMPAT_ENUM_OR_ZERO("scx_exit_kind", #__ent)2222+2323+struct scx_test {2424+ /**2525+ * name - The name of the testcase.2626+ */2727+ const char *name;2828+2929+ /**3030+ * description - A description of your testcase: what it tests and is3131+ * meant to validate.3232+ */3333+ const char *description;3434+3535+ /*3636+ * setup - Setup the test.3737+ * @ctx: A pointer to a context object that will be passed to run and3838+ * cleanup.3939+ *4040+ * An optional callback that allows a testcase to perform setup for its4141+ * run. A test may return SCX_TEST_SKIP to skip the run.4242+ */4343+ enum scx_test_status (*setup)(void **ctx);4444+4545+ /*4646+ * run - Run the test.4747+ * @ctx: Context set in the setup() callback. If @ctx was not set in4848+ * setup(), it is NULL.4949+ *5050+ * The main test. Callers should return one of:5151+ *5252+ * - SCX_TEST_PASS: Test passed5353+ * - SCX_TEST_SKIP: Test should be skipped5454+ * - SCX_TEST_FAIL: Test failed5555+ *5656+ * This callback must be defined.5757+ */5858+ enum scx_test_status (*run)(void *ctx);5959+6060+ /*6161+ * cleanup - Perform cleanup following the test6262+ * @ctx: Context set in the setup() callback. If @ctx was not set in6363+ * setup(), it is NULL.6464+ *6565+ * An optional callback that allows a test to perform cleanup after6666+ * being run. This callback is run even if the run() callback returns6767+ * SCX_TEST_SKIP or SCX_TEST_FAIL. It is not run if setup() returns6868+ * SCX_TEST_SKIP or SCX_TEST_FAIL.6969+ */7070+ void (*cleanup)(void *ctx);7171+};7272+7373+void scx_test_register(struct scx_test *test);7474+7575+#define REGISTER_SCX_TEST(__test) \7676+ __attribute__((constructor)) \7777+ static void ___scxregister##__LINE__(void) \7878+ { \7979+ scx_test_register(__test); \8080+ }8181+8282+#define SCX_ERR(__fmt, ...) \8383+ do { \8484+ fprintf(stderr, "ERR: %s:%d\n", __FILE__, __LINE__); \8585+ fprintf(stderr, __fmt"\n", ##__VA_ARGS__); \8686+ } while (0)8787+8888+#define SCX_FAIL(__fmt, ...) \8989+ do { \9090+ SCX_ERR(__fmt, ##__VA_ARGS__); \9191+ return SCX_TEST_FAIL; \9292+ } while (0)9393+9494+#define SCX_FAIL_IF(__cond, __fmt, ...) \9595+ do { \9696+ if (__cond) \9797+ SCX_FAIL(__fmt, ##__VA_ARGS__); \9898+ } while (0)9999+100100+#define SCX_GT(_x, _y) SCX_FAIL_IF((_x) <= (_y), "Expected %s > %s (%lu > %lu)", \101101+ #_x, #_y, (u64)(_x), (u64)(_y))102102+#define SCX_GE(_x, _y) SCX_FAIL_IF((_x) < (_y), "Expected %s >= %s (%lu >= %lu)", \103103+ #_x, #_y, (u64)(_x), (u64)(_y))104104+#define SCX_LT(_x, _y) SCX_FAIL_IF((_x) >= (_y), "Expected %s < %s (%lu < %lu)", \105105+ #_x, #_y, (u64)(_x), (u64)(_y))106106+#define SCX_LE(_x, _y) SCX_FAIL_IF((_x) > (_y), "Expected %s <= %s (%lu <= %lu)", \107107+ #_x, #_y, (u64)(_x), (u64)(_y))108108+#define SCX_EQ(_x, _y) SCX_FAIL_IF((_x) != (_y), "Expected %s == %s (%lu == %lu)", \109109+ #_x, #_y, (u64)(_x), (u64)(_y))110110+#define SCX_ASSERT(_x) SCX_FAIL_IF(!(_x), "Expected %s to be true (%lu)", \111111+ #_x, (u64)(_x))112112+113113+#define SCX_ECODE_VAL(__ecode) ({ \114114+ u64 __val = 0; \115115+ bool __found = false; \116116+ \117117+ __found = __COMPAT_read_enum("scx_exit_code", #__ecode, &__val); \118118+ SCX_ASSERT(__found); \119119+ (s64)__val; \120120+})121121+122122+#define SCX_KIND_VAL(__kind) ({ \123123+ u64 __val = 0; \124124+ bool __found = false; \125125+ \126126+ __found = __COMPAT_read_enum("scx_exit_kind", #__kind, &__val); \127127+ SCX_ASSERT(__found); \128128+ __val; \129129+})130130+131131+#endif // # __SCX_TEST_H__