]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/tests.h
a8fa4a6622e7176083681e42f7710e2ccca1fb14
[thirdparty/systemd.git] / src / shared / tests.h
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 #pragma once
3
4 #include <stdbool.h>
5
6 #include "sd-daemon.h"
7
8 #include "argv-util.h"
9 #include "macro.h"
10 #include "static-destruct.h"
11 #include "strv.h"
12
13 static inline void log_set_assert_return_is_criticalp(bool *p) {
14 log_set_assert_return_is_critical(*p);
15 }
16
17 #define _SAVE_ASSERT_RETURN_IS_CRITICAL(b) \
18 _unused_ _cleanup_(log_set_assert_return_is_criticalp) bool b = \
19 log_get_assert_return_is_critical()
20
21 #define SAVE_ASSERT_RETURN_IS_CRITICAL \
22 _SAVE_ASSERT_RETURN_IS_CRITICAL(UNIQ_T(saved, UNIQ))
23
24 #define ASSERT_RETURN_IS_CRITICAL(b, expr) \
25 ({ \
26 SAVE_ASSERT_RETURN_IS_CRITICAL; \
27 log_set_assert_return_is_critical(b); \
28 expr; \
29 })
30
31 #define ASSERT_RETURN_EXPECTED(expr) ASSERT_RETURN_IS_CRITICAL(false, expr)
32 #define ASSERT_RETURN_EXPECTED_SE(expr) ASSERT_RETURN_EXPECTED(assert_se(expr));
33
34 static inline bool manager_errno_skip_test(int r) {
35 return IN_SET(abs(r),
36 EPERM,
37 EACCES,
38 EADDRINUSE,
39 EHOSTDOWN,
40 ENOENT,
41 ENOMEDIUM /* cannot determine cgroup */
42 );
43 }
44
45 char* setup_fake_runtime_dir(void);
46 int enter_cgroup_subroot(char **ret_cgroup);
47 int enter_cgroup_root(char **ret_cgroup);
48 int get_testdata_dir(const char *suffix, char **ret);
49 const char* get_catalog_dir(void);
50 bool slow_tests_enabled(void);
51 void test_setup_logging(int level);
52
53 #define log_tests_skipped(fmt, ...) \
54 ({ \
55 log_notice("%s: " fmt ", skipping tests.", \
56 program_invocation_short_name, \
57 ##__VA_ARGS__); \
58 EXIT_TEST_SKIP; \
59 })
60
61 #define log_tests_skipped_errno(error, fmt, ...) \
62 ({ \
63 log_notice_errno(error, \
64 "%s: " fmt ", skipping tests: %m", \
65 program_invocation_short_name, \
66 ##__VA_ARGS__); \
67 EXIT_TEST_SKIP; \
68 })
69
70 int write_tmpfile(char *pattern, const char *contents);
71
72 bool have_namespaces(void);
73
74 /* We use the small but non-trivial limit here */
75 #define CAN_MEMLOCK_SIZE (512 * 1024U)
76 bool can_memlock(void);
77
78 /* Define void* buffer and size_t length variables from a hex string. */
79 #define DEFINE_HEX_PTR(name, hex) \
80 _cleanup_free_ void *name = NULL; \
81 size_t name##_len = 0; \
82 assert_se(unhexmem_full(hex, strlen_ptr(hex), false, &name, &name##_len) >= 0);
83
84 #define TEST_REQ_RUNNING_SYSTEMD(x) \
85 if (sd_booted() > 0) { \
86 x; \
87 } else { \
88 printf("systemd not booted, skipping '%s'\n", #x); \
89 }
90
91 /* Provide a convenient way to check if we're running in CI. */
92 const char *ci_environment(void);
93
94 typedef struct TestFunc {
95 union f {
96 void (*void_func)(void);
97 int (*int_func)(void);
98 } f;
99 const char * const name;
100 bool has_ret:1;
101 bool sd_booted:1;
102 } TestFunc;
103
104 /* See static-destruct.h for an explanation of how this works. */
105 #define REGISTER_TEST(func, ...) \
106 _Pragma("GCC diagnostic ignored \"-Wattributes\"") \
107 _section_("SYSTEMD_TEST_TABLE") _alignptr_ _used_ _retain_ _variable_no_sanitize_address_ \
108 static const TestFunc UNIQ_T(static_test_table_entry, UNIQ) = { \
109 .f = (union f) &(func), \
110 .name = STRINGIFY(func), \
111 .has_ret = __builtin_types_compatible_p(typeof((union f){}.int_func), typeof(&(func))), \
112 ##__VA_ARGS__ \
113 }
114
115 extern const TestFunc _weak_ __start_SYSTEMD_TEST_TABLE[];
116 extern const TestFunc _weak_ __stop_SYSTEMD_TEST_TABLE[];
117
118 #define TEST(name, ...) \
119 static void test_##name(void); \
120 REGISTER_TEST(test_##name, ##__VA_ARGS__); \
121 static void test_##name(void)
122
123 #define TEST_RET(name, ...) \
124 static int test_##name(void); \
125 REGISTER_TEST(test_##name, ##__VA_ARGS__); \
126 static int test_##name(void)
127
128 #define TEST_LOG_FUNC() \
129 log_info("/* %s() */", __func__)
130
131 static inline int run_test_table(void) {
132 _cleanup_strv_free_ char **tests = NULL;
133 int r = EXIT_SUCCESS;
134 bool ran = false;
135 const char *e;
136
137 if (!__start_SYSTEMD_TEST_TABLE)
138 return r;
139
140 e = getenv("TESTFUNCS");
141 if (e) {
142 r = strv_split_full(&tests, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
143 if (r < 0)
144 return log_error_errno(r, "Failed to parse $TESTFUNCS: %m");
145 }
146
147 for (const TestFunc *t = ALIGN_PTR(__start_SYSTEMD_TEST_TABLE);
148 t + 1 <= __stop_SYSTEMD_TEST_TABLE;
149 t = ALIGN_PTR(t + 1)) {
150
151 if (tests && !strv_contains(tests, t->name))
152 continue;
153
154 if (t->sd_booted && sd_booted() <= 0) {
155 log_info("/* systemd not booted, skipping %s */", t->name);
156 if (t->has_ret && r == EXIT_SUCCESS)
157 r = EXIT_TEST_SKIP;
158 } else {
159 log_info("/* %s */", t->name);
160
161 if (t->has_ret) {
162 int r2 = t->f.int_func();
163 if (r == EXIT_SUCCESS)
164 r = r2;
165 } else
166 t->f.void_func();
167 }
168
169 ran = true;
170 }
171
172 if (!ran)
173 return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No matching tests found.");
174
175 return r;
176 }
177
178 #define DEFINE_TEST_MAIN_FULL(log_level, intro, outro) \
179 int main(int argc, char *argv[]) { \
180 int (*_intro)(void) = intro; \
181 int (*_outro)(void) = outro; \
182 int _r, _q; \
183 test_setup_logging(log_level); \
184 save_argc_argv(argc, argv); \
185 _r = _intro ? _intro() : EXIT_SUCCESS; \
186 if (_r == EXIT_SUCCESS) \
187 _r = run_test_table(); \
188 _q = _outro ? _outro() : EXIT_SUCCESS; \
189 static_destruct(); \
190 if (_r < 0) \
191 return EXIT_FAILURE; \
192 if (_r != EXIT_SUCCESS) \
193 return _r; \
194 if (_q < 0) \
195 return EXIT_FAILURE; \
196 return _q; \
197 }
198
199 #define DEFINE_TEST_MAIN_WITH_INTRO(log_level, intro) \
200 DEFINE_TEST_MAIN_FULL(log_level, intro, NULL)
201 #define DEFINE_TEST_MAIN(log_level) \
202 DEFINE_TEST_MAIN_FULL(log_level, NULL, NULL)
203
204 #define ASSERT_OK(expr) \
205 ({ \
206 typeof(expr) _result = (expr); \
207 if (_result < 0) { \
208 log_error_errno(_result, "%s:%i: Assertion failed: %s: %m", \
209 PROJECT_FILE, __LINE__, #expr); \
210 abort(); \
211 } \
212 })
213
214 #define ASSERT_TRUE(expr) \
215 ({ \
216 if (!(expr)) { \
217 log_error("%s:%i: Assertion failed: expected \"%s\" to be true", \
218 PROJECT_FILE, __LINE__, #expr); \
219 abort(); \
220 } \
221 })
222
223 #define ASSERT_FALSE(expr) \
224 ({ \
225 if ((expr)) { \
226 log_error("%s:%i: Assertion failed: expected \"%s\" to be false", \
227 PROJECT_FILE, __LINE__, #expr); \
228 abort(); \
229 } \
230 })
231
232 #define ASSERT_NULL(expr) \
233 ({ \
234 if ((expr) != NULL) { \
235 log_error("%s:%i: Assertion failed: expected \"%s\" to be NULL", \
236 PROJECT_FILE, __LINE__, #expr); \
237 abort(); \
238 } \
239 })
240
241 #define ASSERT_NOT_NULL(expr) \
242 ({ \
243 if ((expr) == NULL) { \
244 log_error("%s:%i: Assertion failed: expected \"%s\" to be not NULL", \
245 PROJECT_FILE, __LINE__, #expr); \
246 abort(); \
247 } \
248 })
249
250 #define ASSERT_STREQ(expr1, expr2) \
251 ({ \
252 const char *_expr1 = (expr1), *_expr2 = (expr2); \
253 if (!streq_ptr(_expr1, _expr2)) { \
254 log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"", \
255 PROJECT_FILE, __LINE__, #expr1, #expr2, strnull(_expr1), strnull(_expr2)); \
256 abort(); \
257 } \
258 })
259
260 /* DECIMAL_STR_FMT() uses _Generic which cannot be used in string concatenation so we have to format the
261 * input into strings first and then format those into the final assertion message. */
262
263 #define ASSERT_EQ(expr1, expr2) \
264 ({ \
265 typeof(expr1) _expr1 = (expr1); \
266 typeof(expr2) _expr2 = (expr2); \
267 if (_expr1 != _expr2) { \
268 char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \
269 char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \
270 xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \
271 xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \
272 log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"", \
273 PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \
274 abort(); \
275 } \
276 })
277
278 #define ASSERT_GE(expr1, expr2) \
279 ({ \
280 typeof(expr1) _expr1 = (expr1); \
281 typeof(expr2) _expr2 = (expr2); \
282 if (_expr1 < _expr2) { \
283 char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \
284 char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \
285 xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \
286 xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \
287 log_error("%s:%i: Assertion failed: expected \"%s >= %s\", but \"%s < %s\"", \
288 PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \
289 abort(); \
290 } \
291 })
292
293 #define ASSERT_LE(expr1, expr2) \
294 ({ \
295 typeof(expr1) _expr1 = (expr1); \
296 typeof(expr2) _expr2 = (expr2); \
297 if (_expr1 > _expr2) { \
298 char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \
299 char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \
300 xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \
301 xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \
302 log_error("%s:%i: Assertion failed: expected \"%s <= %s\", but \"%s > %s\"", \
303 PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \
304 abort(); \
305 } \
306 })
307
308 #define ASSERT_NE(expr1, expr2) \
309 ({ \
310 typeof(expr1) _expr1 = (expr1); \
311 typeof(expr2) _expr2 = (expr2); \
312 if (_expr1 == _expr2) { \
313 char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \
314 char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \
315 xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \
316 xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \
317 log_error("%s:%i: Assertion failed: expected \"%s != %s\", but \"%s == %s\"", \
318 PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \
319 abort(); \
320 } \
321 })
322
323 #define ASSERT_GT(expr1, expr2) \
324 ({ \
325 typeof(expr1) _expr1 = (expr1); \
326 typeof(expr2) _expr2 = (expr2); \
327 if (!(_expr1 > _expr2)) { \
328 char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \
329 char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \
330 xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \
331 xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \
332 log_error("%s:%i: Assertion failed: expected \"%s > %s\", but \"%s <= %s\"", \
333 PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \
334 abort(); \
335 } \
336 })
337
338 #define ASSERT_LT(expr1, expr2) \
339 ({ \
340 typeof(expr1) _expr1 = (expr1); \
341 typeof(expr2) _expr2 = (expr2); \
342 if (!(_expr1 < _expr2)) { \
343 char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \
344 char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \
345 xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \
346 xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \
347 log_error("%s:%i: Assertion failed: expected \"%s < %s\", but \"%s >= %s\"", \
348 PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \
349 abort(); \
350 } \
351 })