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