From 35243b77360c9cc7d1446617fe4fd304bfdecd4c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 23 Apr 2021 12:37:09 +0200 Subject: [PATCH] test-unit-serialize: add a very basic test that command deserialization works We should test both serialization and deserialization works properly. But the serialization/deserialization code is deeply entwined with the manager state, and I think quite a bit of refactoring will be required before this is possible. But let's at least add this simple test for now. --- src/core/service.c | 2 +- src/core/service.h | 3 ++ src/test/meson.build | 11 ++++++ src/test/test-unit-serialize.c | 68 ++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/test/test-unit-serialize.c diff --git a/src/core/service.c b/src/core/service.c index d448d558db0..d43189f4f24 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -2725,7 +2725,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { return 0; } -static int service_deserialize_exec_command( +int service_deserialize_exec_command( Unit *u, const char *key, const char *value) { diff --git a/src/core/service.h b/src/core/service.h index 92c1caf7949..0d51fc31535 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -255,3 +255,6 @@ ServiceTimeoutFailureMode service_timeout_failure_mode_from_string(const char *s DEFINE_CAST(SERVICE, Service); #define STATUS_TEXT_MAX (16U*1024U) + +/* Only exported for unit tests */ +int service_deserialize_exec_command(Unit *u, const char *key, const char *value); diff --git a/src/test/meson.build b/src/test/meson.build index e077c8e03f2..991c0f027d1 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -133,6 +133,17 @@ tests += [ [['src/test/test-serialize.c']], + [['src/test/test-unit-serialize.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid], + core_includes], + [['src/test/test-utf8.c']], [['src/test/test-dev-setup.c']], diff --git a/src/test/test-unit-serialize.c b/src/test/test-unit-serialize.c new file mode 100644 index 00000000000..58a0d9dfd4d --- /dev/null +++ b/src/test/test-unit-serialize.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "rm-rf.h" +#include "service.h" +#include "tests.h" + +#define EXEC_START_ABSOLUTE \ + "ExecStart 0 /bin/sh \"sh\" \"-e\" \"-x\" \"-c\" \"systemctl --state=failed --no-legend --no-pager >/failed ; systemctl daemon-reload ; echo OK >/testok\"" +#define EXEC_START_RELATIVE \ + "ExecStart 0 sh \"sh\" \"-e\" \"-x\" \"-c\" \"systemctl --state=failed --no-legend --no-pager >/failed ; systemctl daemon-reload ; echo OK >/testok\"" + +static void test_deserialize_exec_command_one(Manager *m, const char *key, const char *line, int expected) { + _cleanup_(unit_freep) Unit *u = NULL; + int r; + + assert_se(unit_new_for_name(m, sizeof(Service), "test.service", &u) >= 0); + + r = service_deserialize_exec_command(u, key, line); + log_debug("[%s] → %d (expected: %d)", line, r, expected); + assert(r == expected); + + /* Note that the command doesn't match any command in the empty list of commands in 's', so it is + * always rejected with "Current command vanished from the unit file", and we don't leak anything. */ +} + +static void test_deserialize_exec_command(void) { + _cleanup_(manager_freep) Manager *m = NULL; + int r; + + log_info("/* %s */", __func__); + + r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + if (manager_errno_skip_test(r)) { + log_notice_errno(r, "Skipping test: manager_new: %m"); + return; + } + + assert_se(r >= 0); + + test_deserialize_exec_command_one(m, "main-command", EXEC_START_ABSOLUTE, 0); + test_deserialize_exec_command_one(m, "main-command", EXEC_START_RELATIVE, 0); + test_deserialize_exec_command_one(m, "control-command", EXEC_START_ABSOLUTE, 0); + test_deserialize_exec_command_one(m, "control-command", EXEC_START_RELATIVE, 0); + + test_deserialize_exec_command_one(m, "control-command", "ExecStart 0 /bin/sh \"sh\"", 0); + test_deserialize_exec_command_one(m, "control-command", "ExecStart 0 /no/command ", -EINVAL); + test_deserialize_exec_command_one(m, "control-command", "ExecStart 0 /bad/quote \"", -EINVAL); + test_deserialize_exec_command_one(m, "control-command", "ExecStart s /bad/id x y z", -EINVAL); + test_deserialize_exec_command_one(m, "control-command", "ExecStart 11", -EINVAL); + test_deserialize_exec_command_one(m, "control-command", "ExecWhat 11 /a/b c d e", -EINVAL); +} + +int main(int argc, char *argv[]) { + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; + int r; + + test_setup_logging(LOG_DEBUG); + + r = enter_cgroup_subroot(NULL); + if (r == -ENOMEDIUM) + return log_tests_skipped("cgroupfs not available"); + + assert_se(runtime_dir = setup_fake_runtime_dir()); + + test_deserialize_exec_command(); + + return EXIT_SUCCESS; +} -- 2.47.3