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 "macro.h"
9 #include "static-destruct.h"
10 #include "util.h"
11 
manager_errno_skip_test(int r)12 static inline bool manager_errno_skip_test(int r) {
13         return IN_SET(abs(r),
14                       EPERM,
15                       EACCES,
16                       EADDRINUSE,
17                       EHOSTDOWN,
18                       ENOENT,
19                       ENOMEDIUM /* cannot determine cgroup */
20         );
21 }
22 
23 char* setup_fake_runtime_dir(void);
24 int enter_cgroup_subroot(char **ret_cgroup);
25 int enter_cgroup_root(char **ret_cgroup);
26 int get_testdata_dir(const char *suffix, char **ret);
27 const char* get_catalog_dir(void);
28 bool slow_tests_enabled(void);
29 void test_setup_logging(int level);
30 int log_tests_skipped(const char *message);
31 int log_tests_skipped_errno(int r, const char *message);
32 
33 int write_tmpfile(char *pattern, const char *contents);
34 
35 bool have_namespaces(void);
36 
37 /* We use the small but non-trivial limit here */
38 #define CAN_MEMLOCK_SIZE (512 * 1024U)
39 bool can_memlock(void);
40 
41 #define TEST_REQ_RUNNING_SYSTEMD(x)                                 \
42         if (sd_booted() > 0) {                                      \
43                 x;                                                  \
44         } else {                                                    \
45                 printf("systemd not booted, skipping '%s'\n", #x);   \
46         }
47 
48 /* Provide a convenient way to check if we're running in CI. */
49 const char *ci_environment(void);
50 
51 typedef struct TestFunc {
52         union f {
53                 void (*void_func)(void);
54                 int (*int_func)(void);
55         } f;
56         const char * const name;
57         bool has_ret:1;
58         bool sd_booted:1;
59 } TestFunc;
60 
61 /* See static-destruct.h for an explanation of how this works. */
62 #define REGISTER_TEST(func, ...)                                                                        \
63         _Pragma("GCC diagnostic ignored \"-Wattributes\"")                                              \
64         _section_("SYSTEMD_TEST_TABLE") _alignptr_ _used_ _retain_ _variable_no_sanitize_address_       \
65         static const TestFunc UNIQ_T(static_test_table_entry, UNIQ) = {                                 \
66                 .f = (union f) &(func),                                                                 \
67                 .name = STRINGIFY(func),                                                                \
68                 .has_ret = __builtin_types_compatible_p(typeof((union f){}.int_func), typeof(&(func))), \
69                 ##__VA_ARGS__                                                                           \
70         }
71 
72 extern const TestFunc _weak_ __start_SYSTEMD_TEST_TABLE[];
73 extern const TestFunc _weak_ __stop_SYSTEMD_TEST_TABLE[];
74 
75 #define TEST(name, ...)                            \
76         static void test_##name(void);             \
77         REGISTER_TEST(test_##name, ##__VA_ARGS__); \
78         static void test_##name(void)
79 
80 #define TEST_RET(name, ...)                        \
81         static int test_##name(void);              \
82         REGISTER_TEST(test_##name, ##__VA_ARGS__); \
83         static int test_##name(void)
84 
run_test_table(void)85 static inline int run_test_table(void) {
86         int r = EXIT_SUCCESS;
87 
88         if (!__start_SYSTEMD_TEST_TABLE)
89                 return r;
90 
91         const TestFunc *t = ALIGN_TO_PTR(__start_SYSTEMD_TEST_TABLE, sizeof(TestFunc*));
92         while (t < __stop_SYSTEMD_TEST_TABLE) {
93 
94                 if (t->sd_booted && sd_booted() <= 0) {
95                         log_info("/* systemd not booted, skipping %s */", t->name);
96                         if (t->has_ret && r == EXIT_SUCCESS)
97                                 r = EXIT_TEST_SKIP;
98                 } else {
99                         log_info("/* %s */", t->name);
100 
101                         if (t->has_ret) {
102                                 int r2 = t->f.int_func();
103                                 if (r == EXIT_SUCCESS)
104                                         r = r2;
105                         } else
106                                 t->f.void_func();
107                 }
108 
109                 t = ALIGN_TO_PTR(t + 1, sizeof(TestFunc*));
110         }
111 
112         return r;
113 }
114 
115 #define DEFINE_TEST_MAIN_FULL(log_level, intro, outro)    \
116         int main(int argc, char *argv[]) {                \
117                 int (*_intro)(void) = intro;              \
118                 int (*_outro)(void) = outro;              \
119                 int _r, _q;                               \
120                 test_setup_logging(log_level);            \
121                 save_argc_argv(argc, argv);               \
122                 _r = _intro ? _intro() : EXIT_SUCCESS;    \
123                 if (_r == EXIT_SUCCESS)                   \
124                         _r = run_test_table();            \
125                 _q = _outro ? _outro() : EXIT_SUCCESS;    \
126                 static_destruct();                        \
127                 if (_r < 0)                               \
128                         return EXIT_FAILURE;              \
129                 if (_r != EXIT_SUCCESS)                   \
130                         return _r;                        \
131                 if (_q < 0)                               \
132                         return EXIT_FAILURE;              \
133                 return _q;                                \
134         }
135 
136 #define DEFINE_TEST_MAIN_WITH_INTRO(log_level, intro)   \
137         DEFINE_TEST_MAIN_FULL(log_level, intro, NULL)
138 #define DEFINE_TEST_MAIN(log_level)                     \
139         DEFINE_TEST_MAIN_FULL(log_level, NULL, NULL)
140