From: Daan De Meyer Date: Wed, 20 May 2026 13:30:28 +0000 (+0000) Subject: test-path: Migrate to test framework and macros X-Git-Tag: v261-rc1~64^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc7a32df38dd4797ce7266df6ece6bd9b9842783;p=thirdparty%2Fsystemd.git test-path: Migrate to test framework and macros - Also clean up the logging in check_states() to only log on state changes so it's less noisy. --- diff --git a/src/test/test-path.c b/src/test/test-path.c index d9e11fd0294..b02f7e2e7f7 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -16,15 +16,15 @@ #include "tests.h" #include "unit.h" -typedef void (*test_function_t)(Manager *m); +static char *runtime_dir = NULL; static int setup_test(Manager **m) { char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit", "directorynotempty", "makedirectory"); - Manager *tmp = NULL; + _cleanup_(manager_freep) Manager *tmp = NULL; int r; - assert_se(m); + ASSERT_NOT_NULL(m); r = enter_cgroup_subroot(NULL); if (r == -ENOMEDIUM) @@ -33,396 +33,360 @@ static int setup_test(Manager **m) { r = manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &tmp); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); - assert_se(r >= 0); - assert_se(manager_startup(tmp, NULL, NULL, NULL, NULL) >= 0); + ASSERT_OK(r); + ASSERT_OK(manager_startup(tmp, NULL, NULL, NULL, NULL)); STRV_FOREACH(test_path, tests_path) { _cleanup_free_ char *p = NULL; p = strjoin("/tmp/test-path_", *test_path); - assert_se(p); + ASSERT_NOT_NULL(p); (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); } - *m = tmp; - + *m = TAKE_PTR(tmp); return 0; } -static void shutdown_test(Manager *m) { - assert_se(m); - - manager_free(m); -} - static Service *service_for_path(Manager *m, Path *path, const char *service_name) { _cleanup_free_ char *tmp = NULL; Unit *service_unit = NULL; - assert_se(m); - assert_se(path); + ASSERT_NOT_NULL(m); + ASSERT_NOT_NULL(path); if (!service_name) { - assert_se(tmp = strreplace(UNIT(path)->id, ".path", ".service")); + tmp = ASSERT_NOT_NULL(strreplace(UNIT(path)->id, ".path", ".service")); service_unit = manager_get_unit(m, tmp); } else service_unit = manager_get_unit(m, service_name); - assert_se(service_unit); + ASSERT_NOT_NULL(service_unit); return SERVICE(service_unit); } -static int _check_states(unsigned line, - Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) { - assert_se(m); - assert_se(service); +/* Define a test that gets a freshly-initialized Manager passed as `m`. The body returns 0 on + * success or EXIT_TEST_SKIP to skip; the wrapper hands the value back to the test framework. */ +#define PATH_TEST(name) \ + static int test_##name##_body(Manager *m); \ + TEST_RET(name) { \ + _cleanup_(manager_freep) Manager *m = NULL; \ + int r = setup_test(&m); \ + if (r != 0) \ + return r; \ + return test_##name##_body(m); \ + } \ + static int test_##name##_body(Manager *m) + +static int _check_states( + unsigned line, + Manager *m, + Path *path, + Service *service, + PathState path_state, + ServiceState service_state) { + + ASSERT_NOT_NULL(m); + ASSERT_NOT_NULL(service); usec_t end = usec_add(now(CLOCK_MONOTONIC), 30 * USEC_PER_SEC); + PathState last_path_state = _PATH_STATE_INVALID; + PathResult last_path_result = _PATH_RESULT_INVALID; + ServiceState last_service_state = _SERVICE_STATE_INVALID; + ServiceResult last_service_result = _SERVICE_RESULT_INVALID; while (path->state != path_state || service->state != service_state || path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS) { - assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0); + ASSERT_OK(sd_event_run(m->event, 100 * USEC_PER_MSEC)); usec_t n = now(CLOCK_MONOTONIC); - log_info("line %u: %s: state = %s; result = %s (left: %" PRIi64 ")", - line, - UNIT(path)->id, - path_state_to_string(path->state), - path_result_to_string(path->result), - (int64_t) (end - n)); - log_info("line %u: %s: state = %s; result = %s", - line, - UNIT(service)->id, - service_state_to_string(service->state), - service_result_to_string(service->result)); + if (path->state != last_path_state || path->result != last_path_result || + service->state != last_service_state || service->result != last_service_result) { + log_info("line %u: %s: state = %s; result = %s (left: %" PRIi64 ")", + line, + UNIT(path)->id, + path_state_to_string(path->state), + path_result_to_string(path->result), + (int64_t) (end - n)); + log_info("line %u: %s: state = %s; result = %s", + line, + UNIT(service)->id, + service_state_to_string(service->state), + service_result_to_string(service->result)); + last_path_state = path->state; + last_path_result = path->result; + last_service_state = service->state; + last_service_result = service->result; + } if (service->state == SERVICE_FAILED && (service->main_exec_status.status == EXIT_CGROUP || service->result == SERVICE_FAILURE_RESOURCES)) { - const char *ci = ci_environment(); - /* On a general purpose system we may fail to start the service for reasons which are * not under our control: permission limits, resource exhaustion, etc. Let's skip the - * test in those cases. On developer machines we require proper setup. */ - if (!ci) - return log_notice_errno(SYNTHETIC_ERRNO(ECANCELED), - "Failed to start service %s, aborting test: %s/%s", - UNIT(service)->id, - service_state_to_string(service->state), - service_result_to_string(service->result)); - - /* On Salsa we can't setup cgroups so the unit always fails. The test checks if it - * can but continues if it cannot at the beginning, but on Salsa it fails here. */ - if (streq(ci, "salsa-ci")) - exit(EXIT_TEST_SKIP); + * test in those cases. On CI we expect a properly configured environment, except for + * Salsa where we can't set up cgroups so the unit always fails. */ + const char *ci = ci_environment(); + if (!ci || streq(ci, "salsa-ci")) + return log_tests_skipped("Failed to start service %s: %s/%s", + UNIT(service)->id, + service_state_to_string(service->state), + service_result_to_string(service->result)); } /* SERVICE_FAILURE_START_LIMIT_HIT is terminal: the unit won't recover without an explicit - * reset, so further looping is pointless. Abort the subtest rather than burning the 30s - * timeout. */ + * reset, so further looping is pointless. Skip the test rather than burning the 30s timeout. */ if (service->state == SERVICE_FAILED && service->result == SERVICE_FAILURE_START_LIMIT_HIT) - return log_notice_errno(SYNTHETIC_ERRNO(ECANCELED), - "Failed to start service %s, aborting test: %s/%s", - UNIT(service)->id, - service_state_to_string(service->state), - service_result_to_string(service->result)); - - if (n >= end) { - log_error("Test timeout when testing %s", UNIT(path)->id); - exit(EXIT_FAILURE); - } + return log_tests_skipped("Failed to start service %s: %s/%s", + UNIT(service)->id, + service_state_to_string(service->state), + service_result_to_string(service->result)); + + if (n >= end) + log_test_failed("Test timeout when testing %s", UNIT(path)->id); } return 0; } -#define check_states(...) _check_states(__LINE__, __VA_ARGS__) -static void test_path_exists(Manager *m) { +#define check_states(...) \ + do { \ + int _r = _check_states(__LINE__, __VA_ARGS__); \ + if (_r != 0) \ + return _r; \ + } while (0) + +PATH_TEST(path_exists) { const char *test_path = "/tmp/test-path_exists"; Unit *unit = NULL; Path *path = NULL; Service *service = NULL; - assert_se(m); - - assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0); + ASSERT_OK(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit)); path = PATH(unit); service = service_for_path(m, path, NULL); - assert_se(unit_start(unit, NULL) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_start(unit, NULL)); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(touch(test_path) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(touch(test_path)); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); /* Service restarts if file still exists */ - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK_ZERO(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL)); + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(unit_stop(unit) >= 0); + ASSERT_OK(unit_stop(unit)); + return 0; } -static void test_path_existsglob(Manager *m) { +PATH_TEST(path_existsglob) { const char *test_path = "/tmp/test-path_existsglobFOOBAR"; Unit *unit = NULL; Path *path = NULL; Service *service = NULL; - assert_se(m); - - assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0); + ASSERT_OK(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit)); path = PATH(unit); service = service_for_path(m, path, NULL); - assert_se(unit_start(unit, NULL) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_start(unit, NULL)); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(touch(test_path) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(touch(test_path)); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); /* Service restarts if file still exists */ - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK_ZERO(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL)); + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(unit_stop(unit) >= 0); + ASSERT_OK(unit_stop(unit)); + return 0; } -static void test_path_changed(Manager *m) { +PATH_TEST(path_changed) { const char *test_path = "/tmp/test-path_changed"; FILE *f; Unit *unit = NULL; Path *path = NULL; Service *service = NULL; - assert_se(m); - - assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0); + ASSERT_OK(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit)); path = PATH(unit); service = service_for_path(m, path, NULL); - assert_se(unit_start(unit, NULL) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_start(unit, NULL)); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(touch(test_path) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(touch(test_path)); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); /* Service does not restart if file still exists */ - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - f = fopen(test_path, "w"); - assert_se(f); + f = ASSERT_NOT_NULL(fopen(test_path, "w")); fclose(f); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); - assert_se(unit_stop(unit) >= 0); + ASSERT_OK(unit_stop(unit)); + return 0; } -static void test_path_modified(Manager *m) { +PATH_TEST(path_modified) { _cleanup_fclose_ FILE *f = NULL; const char *test_path = "/tmp/test-path_modified"; Unit *unit = NULL; Path *path = NULL; Service *service = NULL; - assert_se(m); - - assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0); + ASSERT_OK(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit)); path = PATH(unit); service = service_for_path(m, path, NULL); - assert_se(unit_start(unit, NULL) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_start(unit, NULL)); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(touch(test_path) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(touch(test_path)); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); /* Service does not restart if file still exists */ - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - f = fopen(test_path, "w"); - assert_se(f); + f = ASSERT_NOT_NULL(fopen(test_path, "w")); fputs("test", f); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); - assert_se(unit_stop(unit) >= 0); + ASSERT_OK(unit_stop(unit)); + return 0; } -static void test_path_unit(Manager *m) { +PATH_TEST(path_unit) { const char *test_path = "/tmp/test-path_unit"; Unit *unit = NULL; Path *path = NULL; Service *service = NULL; - assert_se(m); - - assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0); + ASSERT_OK(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit)); path = PATH(unit); service = service_for_path(m, path, "path-mycustomunit.service"); - assert_se(unit_start(unit, NULL) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_start(unit, NULL)); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(touch(test_path) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(touch(test_path)); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK_ZERO(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL)); + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(unit_stop(unit) >= 0); + ASSERT_OK(unit_stop(unit)); + return 0; } -static void test_path_directorynotempty(Manager *m) { +PATH_TEST(path_directorynotempty) { const char *test_file, *test_path = "/tmp/test-path_directorynotempty/"; Unit *unit = NULL; Path *path = NULL; Service *service = NULL; - assert_se(m); - - assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0); + ASSERT_OK(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit)); path = PATH(unit); service = service_for_path(m, path, NULL); - assert_se(access(test_path, F_OK) < 0); + ASSERT_FAIL(access(test_path, F_OK)); - assert_se(unit_start(unit, NULL) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK(unit_start(unit, NULL)); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); /* MakeDirectory default to no */ - assert_se(access(test_path, F_OK) < 0); + ASSERT_FAIL(access(test_path, F_OK)); - assert_se(mkdir_p(test_path, 0755) >= 0); + ASSERT_OK(mkdir_p(test_path, 0755)); test_file = strjoina(test_path, "test_file"); - assert_se(touch(test_file) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(touch(test_file)); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); /* Service restarts if directory is still not empty */ - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING) < 0) - return; + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING); - assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); - assert_se(unit_stop(UNIT(service)) >= 0); - if (check_states(m, path, service, PATH_WAITING, SERVICE_DEAD) < 0) - return; + ASSERT_OK_ZERO(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL)); + ASSERT_OK(unit_stop(UNIT(service))); + check_states(m, path, service, PATH_WAITING, SERVICE_DEAD); - assert_se(unit_stop(unit) >= 0); + ASSERT_OK(unit_stop(unit)); + return 0; } -static void test_path_makedirectory_directorymode(Manager *m) { +PATH_TEST(path_makedirectory_directorymode) { const char *test_path = "/tmp/test-path_makedirectory/"; Unit *unit = NULL; struct stat s; - assert_se(m); - - assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0); + ASSERT_OK(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit)); - assert_se(access(test_path, F_OK) < 0); + ASSERT_FAIL(access(test_path, F_OK)); - assert_se(unit_start(unit, NULL) >= 0); + ASSERT_OK(unit_start(unit, NULL)); /* Check if the directory has been created */ - assert_se(access(test_path, F_OK) >= 0); + ASSERT_OK_ERRNO(access(test_path, F_OK)); /* Check the mode we specified with DirectoryMode=0744 */ - assert_se(stat(test_path, &s) >= 0); - assert_se((s.st_mode & S_IRWXU) == 0700); - assert_se((s.st_mode & S_IRWXG) == 0040); - assert_se((s.st_mode & S_IRWXO) == 0004); + ASSERT_OK_ERRNO(stat(test_path, &s)); + ASSERT_EQ((mode_t) (s.st_mode & S_IRWXU), (mode_t) 0700); + ASSERT_EQ((mode_t) (s.st_mode & S_IRWXG), (mode_t) 0040); + ASSERT_EQ((mode_t) (s.st_mode & S_IRWXO), (mode_t) 0004); - assert_se(unit_stop(unit) >= 0); + ASSERT_OK(unit_stop(unit)); (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL); + return 0; } -int main(int argc, char *argv[]) { - static const test_function_t tests[] = { - test_path_exists, - test_path_existsglob, - test_path_changed, - test_path_modified, - test_path_unit, - test_path_directorynotempty, - test_path_makedirectory_directorymode, - NULL, - }; - +static int intro(void) { _cleanup_free_ char *test_path = NULL; - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; umask(022); - test_setup_logging(LOG_INFO); - ASSERT_OK(get_testdata_dir("test-path", &test_path)); ASSERT_OK(setenv_unit_path(test_path)); - assert_se(runtime_dir = setup_fake_runtime_dir()); - - for (const test_function_t *test = tests; *test; test++) { - Manager *m = NULL; - int r; - - /* We create a clean environment for each test */ - r = setup_test(&m); - if (r != 0) - return r; - - (*test)(m); + ASSERT_NOT_NULL(runtime_dir = setup_fake_runtime_dir()); - shutdown_test(m); - } + return EXIT_SUCCESS; +} - return 0; +static int outro(void) { + runtime_dir = rm_rf_physical_and_free(runtime_dir); + return EXIT_SUCCESS; } + +DEFINE_TEST_MAIN_FULL(LOG_INFO, intro, outro);