1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Copyright (c) 2023 Meta Platforms, Inc. and affiliates. 4 * Copyright (c) 2023 Tejun Heo <tj@kernel.org> 5 * Copyright (c) 2023 David Vernet <dvernet@meta.com> 6 */ 7 8 #ifndef __SCX_TEST_H__ 9 #define __SCX_TEST_H__ 10 11 #include <errno.h> 12 #include <scx/common.h> 13 #include <scx/compat.h> 14 15 enum scx_test_status { 16 SCX_TEST_PASS = 0, 17 SCX_TEST_SKIP, 18 SCX_TEST_FAIL, 19 }; 20 21 struct scx_test { 22 /** 23 * name - The name of the testcase. 24 */ 25 const char *name; 26 27 /** 28 * description - A description of your testcase: what it tests and is 29 * meant to validate. 30 */ 31 const char *description; 32 33 /* 34 * setup - Setup the test. 35 * @ctx: A pointer to a context object that will be passed to run and 36 * cleanup. 37 * 38 * An optional callback that allows a testcase to perform setup for its 39 * run. A test may return SCX_TEST_SKIP to skip the run. 40 */ 41 enum scx_test_status (*setup)(void **ctx); 42 43 /* 44 * run - Run the test. 45 * @ctx: Context set in the setup() callback. If @ctx was not set in 46 * setup(), it is NULL. 47 * 48 * The main test. Callers should return one of: 49 * 50 * - SCX_TEST_PASS: Test passed 51 * - SCX_TEST_SKIP: Test should be skipped 52 * - SCX_TEST_FAIL: Test failed 53 * 54 * This callback must be defined. 55 */ 56 enum scx_test_status (*run)(void *ctx); 57 58 /* 59 * cleanup - Perform cleanup following the test 60 * @ctx: Context set in the setup() callback. If @ctx was not set in 61 * setup(), it is NULL. 62 * 63 * An optional callback that allows a test to perform cleanup after 64 * being run. This callback is run even if the run() callback returns 65 * SCX_TEST_SKIP or SCX_TEST_FAIL. It is not run if setup() returns 66 * SCX_TEST_SKIP or SCX_TEST_FAIL. 67 */ 68 void (*cleanup)(void *ctx); 69 }; 70 71 void scx_test_register(struct scx_test *test); 72 73 #define REGISTER_SCX_TEST(__test) \ 74 __attribute__((constructor)) \ 75 static void ___scxregister##__LINE__(void) \ 76 { \ 77 scx_test_register(__test); \ 78 } 79 80 #define SCX_ERR(__fmt, ...) \ 81 do { \ 82 fprintf(stderr, "ERR: %s:%d\n", __FILE__, __LINE__); \ 83 fprintf(stderr, __fmt"\n", ##__VA_ARGS__); \ 84 } while (0) 85 86 #define SCX_FAIL(__fmt, ...) \ 87 do { \ 88 SCX_ERR(__fmt, ##__VA_ARGS__); \ 89 return SCX_TEST_FAIL; \ 90 } while (0) 91 92 #define SCX_FAIL_IF(__cond, __fmt, ...) \ 93 do { \ 94 if (__cond) \ 95 SCX_FAIL(__fmt, ##__VA_ARGS__); \ 96 } while (0) 97 98 #define SCX_GT(_x, _y) SCX_FAIL_IF((_x) <= (_y), "Expected %s > %s (%lu > %lu)", \ 99 #_x, #_y, (u64)(_x), (u64)(_y)) 100 #define SCX_GE(_x, _y) SCX_FAIL_IF((_x) < (_y), "Expected %s >= %s (%lu >= %lu)", \ 101 #_x, #_y, (u64)(_x), (u64)(_y)) 102 #define SCX_LT(_x, _y) SCX_FAIL_IF((_x) >= (_y), "Expected %s < %s (%lu < %lu)", \ 103 #_x, #_y, (u64)(_x), (u64)(_y)) 104 #define SCX_LE(_x, _y) SCX_FAIL_IF((_x) > (_y), "Expected %s <= %s (%lu <= %lu)", \ 105 #_x, #_y, (u64)(_x), (u64)(_y)) 106 #define SCX_EQ(_x, _y) SCX_FAIL_IF((_x) != (_y), "Expected %s == %s (%lu == %lu)", \ 107 #_x, #_y, (u64)(_x), (u64)(_y)) 108 #define SCX_ASSERT(_x) SCX_FAIL_IF(!(_x), "Expected %s to be true (%lu)", \ 109 #_x, (u64)(_x)) 110 111 /* 112 * SCX_ASSERT_ALIVE - Fail the test if the scheduler under test exited 113 * on its own between attach and this check, e.g. from a runtime error. 114 * A scheduler that dies mid-test freezes whatever state the test is 115 * about to assert on, which can make the assertions pass vacuously. 116 * uei.msg carries the actual error text, so print it alongside the 117 * recorded kind and exit code. 118 */ 119 #define SCX_ASSERT_ALIVE(__skel, __uei) \ 120 do { \ 121 if (UEI_EXITED(__skel, __uei)) \ 122 SCX_FAIL("Scheduler exited unexpectedly" \ 123 " (kind=%llu code=%lld msg='%s')", \ 124 (unsigned long long)(__skel)->data->__uei.kind, \ 125 (long long)(__skel)->data->__uei.exit_code, \ 126 (__skel)->data->__uei.msg); \ 127 } while (0) 128 129 /* 130 * SCX_TIMED_WAIT_UEI - Wait for the scheduler under test to exit, i.e. 131 * its uei to be filled in, for up to @__ms iterations at ~1ms each. 132 * Returns true if the scheduler exited and false on timeout, so a 133 * scheduler that never exits fails the test instead of hanging it. 134 * The bound is iteration-based rather than wall-clock and stretches 135 * on a loaded system instead of failing early. 136 */ 137 #define SCX_TIMED_WAIT_UEI(__skel, __uei, __ms) \ 138 ({ \ 139 s64 __left = (__ms); \ 140 \ 141 while (__left-- > 0 && !UEI_EXITED(__skel, __uei)) \ 142 usleep(1000); \ 143 UEI_EXITED(__skel, __uei) != 0; \ 144 }) 145 146 #define SCX_ECODE_VAL(__ecode) ({ \ 147 u64 __val = 0; \ 148 bool __found = false; \ 149 \ 150 __found = __COMPAT_read_enum("scx_exit_code", #__ecode, &__val); \ 151 SCX_ASSERT(__found); \ 152 (s64)__val; \ 153 }) 154 155 /* 156 * SCX_KIND_VAL - Resolve an scx_exit_kind enumerator from kernel BTF. 157 * 158 * Unlike __COMPAT_ENUM_OR_ZERO(), a missing enumerator fails the test 159 * loudly instead of silently resolving to 0 (== SCX_EXIT_NONE), which 160 * would make the comparison vacuous. 161 */ 162 #define SCX_KIND_VAL(__kind) ({ \ 163 u64 __val = 0; \ 164 \ 165 if (!__COMPAT_read_enum("scx_exit_kind", #__kind, &__val)) \ 166 SCX_FAIL("scx_exit_kind.%s not found in kernel BTF", #__kind); \ 167 __val; \ 168 }) 169 170 /* 171 * EXIT_KIND - Alias of SCX_KIND_VAL() for resolving scx_exit_kind 172 * enumerator values, kept for readability at the EXIT_KIND() call sites. 173 */ 174 #define EXIT_KIND(__ent) SCX_KIND_VAL(__ent) 175 176 #endif // # __SCX_TEST_H__ 177