/* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once #include #include #include #include "sd-daemon.h" #include "argv-util.h" #include "errno-util.h" #include "macro.h" #include "process-util.h" #include "rlimit-util.h" #include "signal-util.h" #include "static-destruct.h" #include "strv.h" static inline void log_set_assert_return_is_criticalp(bool *p) { log_set_assert_return_is_critical(*p); } #define _SAVE_ASSERT_RETURN_IS_CRITICAL(b) \ _unused_ _cleanup_(log_set_assert_return_is_criticalp) bool b = \ log_get_assert_return_is_critical() #define SAVE_ASSERT_RETURN_IS_CRITICAL \ _SAVE_ASSERT_RETURN_IS_CRITICAL(UNIQ_T(saved, UNIQ)) #define ASSERT_RETURN_IS_CRITICAL(b, expr) \ ({ \ SAVE_ASSERT_RETURN_IS_CRITICAL; \ log_set_assert_return_is_critical(b); \ expr; \ }) #define ASSERT_RETURN_EXPECTED(expr) ASSERT_RETURN_IS_CRITICAL(false, expr) #define ASSERT_RETURN_EXPECTED_SE(expr) ASSERT_RETURN_EXPECTED(assert_se(expr)); static inline bool manager_errno_skip_test(int r) { return IN_SET(abs(r), EPERM, EACCES, EADDRINUSE, EHOSTDOWN, ENOENT, ENOMEDIUM /* cannot determine cgroup */ ); } char* setup_fake_runtime_dir(void); int enter_cgroup_subroot(char **ret_cgroup); int enter_cgroup_root(char **ret_cgroup); int get_testdata_dir(const char *suffix, char **ret); const char* get_catalog_dir(void); bool slow_tests_enabled(void); void test_setup_logging(int level); #define log_tests_skipped(fmt, ...) \ ({ \ log_notice("%s: " fmt ", skipping tests.", \ program_invocation_short_name, \ ##__VA_ARGS__); \ EXIT_TEST_SKIP; \ }) #define log_tests_skipped_errno(error, fmt, ...) \ ({ \ log_notice_errno(error, \ "%s: " fmt ", skipping tests: %m", \ program_invocation_short_name, \ ##__VA_ARGS__); \ EXIT_TEST_SKIP; \ }) int write_tmpfile(char *pattern, const char *contents); bool have_namespaces(void); /* We use the small but non-trivial limit here */ #define CAN_MEMLOCK_SIZE (512 * 1024U) bool can_memlock(void); /* Define void* buffer and size_t length variables from a hex string. */ #define DEFINE_HEX_PTR(name, hex) \ _cleanup_free_ void *name = NULL; \ size_t name##_len = 0; \ assert_se(unhexmem_full(hex, strlen_ptr(hex), false, &name, &name##_len) >= 0); #define TEST_REQ_RUNNING_SYSTEMD(x) \ if (sd_booted() > 0) { \ x; \ } else { \ printf("systemd not booted, skipping '%s'\n", #x); \ } /* Provide a convenient way to check if we're running in CI. */ const char *ci_environment(void); typedef struct TestFunc { union f { void (*void_func)(void); int (*int_func)(void); } f; const char * const name; bool has_ret:1; bool sd_booted:1; } TestFunc; /* See static-destruct.h for an explanation of how this works. */ #define REGISTER_TEST(func, ...) \ _Pragma("GCC diagnostic ignored \"-Wattributes\"") \ _section_("SYSTEMD_TEST_TABLE") _alignptr_ _used_ _retain_ _variable_no_sanitize_address_ \ static const TestFunc UNIQ_T(static_test_table_entry, UNIQ) = { \ .f = (union f) &(func), \ .name = STRINGIFY(func), \ .has_ret = __builtin_types_compatible_p(typeof((union f){}.int_func), typeof(&(func))), \ ##__VA_ARGS__ \ } extern const TestFunc _weak_ __start_SYSTEMD_TEST_TABLE[]; extern const TestFunc _weak_ __stop_SYSTEMD_TEST_TABLE[]; #define TEST(name, ...) \ static void test_##name(void); \ REGISTER_TEST(test_##name, ##__VA_ARGS__); \ static void test_##name(void) #define TEST_RET(name, ...) \ static int test_##name(void); \ REGISTER_TEST(test_##name, ##__VA_ARGS__); \ static int test_##name(void) #define TEST_LOG_FUNC() \ log_info("/* %s() */", __func__) static inline int run_test_table(void) { _cleanup_strv_free_ char **tests = NULL; int r = EXIT_SUCCESS; bool ran = false; const char *e; if (!__start_SYSTEMD_TEST_TABLE) return r; e = getenv("TESTFUNCS"); if (e) { r = strv_split_full(&tests, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return log_error_errno(r, "Failed to parse $TESTFUNCS: %m"); } for (const TestFunc *t = ALIGN_PTR(__start_SYSTEMD_TEST_TABLE); t + 1 <= __stop_SYSTEMD_TEST_TABLE; t = ALIGN_PTR(t + 1)) { if (tests && !strv_contains(tests, t->name)) continue; if (t->sd_booted && sd_booted() <= 0) { log_info("/* systemd not booted, skipping %s */", t->name); if (t->has_ret && r == EXIT_SUCCESS) r = EXIT_TEST_SKIP; } else { log_info("/* %s */", t->name); if (t->has_ret) { int r2 = t->f.int_func(); if (r == EXIT_SUCCESS) r = r2; } else t->f.void_func(); } ran = true; } if (!ran) return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "No matching tests found."); return r; } #define DEFINE_TEST_MAIN_FULL(log_level, intro, outro) \ int main(int argc, char *argv[]) { \ int (*_intro)(void) = intro; \ int (*_outro)(void) = outro; \ int _r, _q; \ test_setup_logging(log_level); \ save_argc_argv(argc, argv); \ _r = _intro ? _intro() : EXIT_SUCCESS; \ if (_r == EXIT_SUCCESS) \ _r = run_test_table(); \ _q = _outro ? _outro() : EXIT_SUCCESS; \ static_destruct(); \ if (_r < 0) \ return EXIT_FAILURE; \ if (_r != EXIT_SUCCESS) \ return _r; \ if (_q < 0) \ return EXIT_FAILURE; \ return _q; \ } #define DEFINE_TEST_MAIN_WITH_INTRO(log_level, intro) \ DEFINE_TEST_MAIN_FULL(log_level, intro, NULL) #define DEFINE_TEST_MAIN(log_level) \ DEFINE_TEST_MAIN_FULL(log_level, NULL, NULL) #define ASSERT_OK(expr) \ ({ \ typeof(expr) _result = (expr); \ if (_result < 0) { \ log_error_errno(_result, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \ PROJECT_FILE, __LINE__, #expr); \ abort(); \ } \ }) #define ASSERT_OK_ERRNO(expr) \ ({ \ typeof(expr) _result = (expr); \ if (_result < 0) { \ log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to succeed but got the following error: %m", \ PROJECT_FILE, __LINE__, #expr); \ abort(); \ } \ }) #define ASSERT_ERROR(expr1, expr2) \ ({ \ int _expr1 = (expr1); \ int _expr2 = (expr2); \ if (_expr1 >= 0) { \ log_error("%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but it succeeded", \ PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2)); \ abort(); \ } else if (-_expr1 != _expr2) { \ log_error_errno(_expr1, "%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but got the following error: %m", \ PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2)); \ abort(); \ } \ }) #define ASSERT_ERROR_ERRNO(expr1, expr2) \ ({ \ int _expr1 = (expr1); \ int _expr2 = (expr2); \ if (_expr1 >= 0) { \ log_error("%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but it succeeded", \ PROJECT_FILE, __LINE__, #expr1, STRERROR(_expr2)); \ abort(); \ } else if (errno != _expr2) { \ log_error_errno(errno, "%s:%i: Assertion failed: expected \"%s\" to fail with error \"%s\", but got the following error: %m", \ PROJECT_FILE, __LINE__, #expr1, STRERROR(errno)); \ abort(); \ } \ }) #define ASSERT_TRUE(expr) \ ({ \ if (!(expr)) { \ log_error("%s:%i: Assertion failed: expected \"%s\" to be true", \ PROJECT_FILE, __LINE__, #expr); \ abort(); \ } \ }) #define ASSERT_FALSE(expr) \ ({ \ if ((expr)) { \ log_error("%s:%i: Assertion failed: expected \"%s\" to be false", \ PROJECT_FILE, __LINE__, #expr); \ abort(); \ } \ }) #define ASSERT_NULL(expr) \ ({ \ typeof(expr) _result = (expr); \ if (_result != NULL) { \ log_error("%s:%i: Assertion failed: expected \"%s\" to be NULL, but \"%p\" != NULL", \ PROJECT_FILE, __LINE__, #expr, _result); \ abort(); \ } \ }) #define ASSERT_NOT_NULL(expr) \ ({ \ if ((expr) == NULL) { \ log_error("%s:%i: Assertion failed: expected \"%s\" to be not NULL", \ PROJECT_FILE, __LINE__, #expr); \ abort(); \ } \ }) #define ASSERT_STREQ(expr1, expr2) \ ({ \ const char *_expr1 = (expr1), *_expr2 = (expr2); \ if (!streq_ptr(_expr1, _expr2)) { \ log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"", \ PROJECT_FILE, __LINE__, #expr1, #expr2, strnull(_expr1), strnull(_expr2)); \ abort(); \ } \ }) /* DECIMAL_STR_FMT() uses _Generic which cannot be used in string concatenation so we have to format the * input into strings first and then format those into the final assertion message. */ #define ASSERT_EQ(expr1, expr2) \ ({ \ typeof(expr1) _expr1 = (expr1); \ typeof(expr2) _expr2 = (expr2); \ if (_expr1 != _expr2) { \ char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \ char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \ xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \ xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \ log_error("%s:%i: Assertion failed: expected \"%s == %s\", but \"%s != %s\"", \ PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \ abort(); \ } \ }) #define ASSERT_GE(expr1, expr2) \ ({ \ typeof(expr1) _expr1 = (expr1); \ typeof(expr2) _expr2 = (expr2); \ if (_expr1 < _expr2) { \ char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \ char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \ xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \ xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \ log_error("%s:%i: Assertion failed: expected \"%s >= %s\", but \"%s < %s\"", \ PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \ abort(); \ } \ }) #define ASSERT_LE(expr1, expr2) \ ({ \ typeof(expr1) _expr1 = (expr1); \ typeof(expr2) _expr2 = (expr2); \ if (_expr1 > _expr2) { \ char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \ char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \ xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \ xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \ log_error("%s:%i: Assertion failed: expected \"%s <= %s\", but \"%s > %s\"", \ PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \ abort(); \ } \ }) #define ASSERT_NE(expr1, expr2) \ ({ \ typeof(expr1) _expr1 = (expr1); \ typeof(expr2) _expr2 = (expr2); \ if (_expr1 == _expr2) { \ char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \ char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \ xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \ xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \ log_error("%s:%i: Assertion failed: expected \"%s != %s\", but \"%s == %s\"", \ PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \ abort(); \ } \ }) #define ASSERT_GT(expr1, expr2) \ ({ \ typeof(expr1) _expr1 = (expr1); \ typeof(expr2) _expr2 = (expr2); \ if (!(_expr1 > _expr2)) { \ char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \ char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \ xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \ xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \ log_error("%s:%i: Assertion failed: expected \"%s > %s\", but \"%s <= %s\"", \ PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \ abort(); \ } \ }) #define ASSERT_LT(expr1, expr2) \ ({ \ typeof(expr1) _expr1 = (expr1); \ typeof(expr2) _expr2 = (expr2); \ if (!(_expr1 < _expr2)) { \ char _sexpr1[DECIMAL_STR_MAX(typeof(expr1))]; \ char _sexpr2[DECIMAL_STR_MAX(typeof(expr2))]; \ xsprintf(_sexpr1, DECIMAL_STR_FMT(_expr1), _expr1); \ xsprintf(_sexpr2, DECIMAL_STR_FMT(_expr2), _expr2); \ log_error("%s:%i: Assertion failed: expected \"%s < %s\", but \"%s >= %s\"", \ PROJECT_FILE, __LINE__, #expr1, #expr2, _sexpr1, _sexpr2); \ abort(); \ } \ }) #define ASSERT_SIGNAL(expr, signal) \ ({ \ ASSERT_TRUE(SIGNAL_VALID(signal)); \ siginfo_t _siginfo = {}; \ int _pid = fork(); \ ASSERT_OK(_pid); \ if (_pid == 0) { \ /* Speed things up by never even attempting to generate a coredump */ \ (void) prctl(PR_SET_DUMPABLE, 0); \ /* But still set an rlimit just in case */ \ (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(0)); \ expr; \ _exit(EXIT_SUCCESS); \ } \ (void) wait_for_terminate(_pid, &_siginfo); \ if (_siginfo.si_status != signal) { \ log_error("%s:%i: Assertion failed: \"%s\" died with signal %s, but %s was expected", \ PROJECT_FILE, __LINE__, #expr, signal_to_string(_siginfo.si_status), \ signal_to_string(signal)); \ abort(); \ } \ })