From cf35dad25ab0a3819454d4acb3ac8b10109bb840 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 26 Jan 2015 20:10:39 +0000 Subject: Add a regression test for systemd activation 4.5 years after it was implemented, here is the regression test. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=57952 Reviewed-by: Philip Withnall --- test/Makefile.am | 15 + .../com.example.SystemdActivatable1.service | 4 + .../com.example.SystemdActivatable2.service | 4 + .../com.example.SystemdActivatable3.service | 4 + .../org.freedesktop.systemd1.service | 3 + .../valid-config-files/systemd-activation.conf.in | 11 + test/sd-activation.c | 319 +++++++++++++++++++++ test/test-utils-glib.c | 3 + 8 files changed, 363 insertions(+) create mode 100644 test/data/systemd-activation/com.example.SystemdActivatable1.service create mode 100644 test/data/systemd-activation/com.example.SystemdActivatable2.service create mode 100644 test/data/systemd-activation/com.example.SystemdActivatable3.service create mode 100644 test/data/systemd-activation/org.freedesktop.systemd1.service create mode 100644 test/data/valid-config-files/systemd-activation.conf.in create mode 100644 test/sd-activation.c (limited to 'test') diff --git a/test/Makefile.am b/test/Makefile.am index 75e70d29..8b84c7d2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -160,6 +160,7 @@ installable_tests += \ test-marshal \ test-refs \ test-relay \ + test-sd-activation \ test-syntax \ test-syslog \ test-uid-permissions \ @@ -229,6 +230,15 @@ test_dbus_daemon_eavesdrop_LDADD = \ $(GLIB_LIBS) \ $(NULL) +test_sd_activation_SOURCES = \ + sd-activation.c \ + $(NULL) +test_sd_activation_CPPFLAGS = $(testutils_shared_if_possible_cppflags) +test_sd_activation_LDADD = \ + $(testutils_shared_if_possible_libs) \ + $(GLIB_LIBS) \ + $(NULL) + test_marshal_SOURCES = marshal.c test_marshal_LDADD = \ $(top_builddir)/dbus/libdbus-1.la \ @@ -287,6 +297,7 @@ in_data = \ data/valid-config-files/finite-timeout.conf.in \ data/valid-config-files/incoming-limit.conf.in \ data/valid-config-files/multi-user.conf.in \ + data/valid-config-files/systemd-activation.conf.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoUser.service.in \ @@ -355,6 +366,10 @@ static_data = \ data/sha-1/bit-messages.sha1 \ data/sha-1/byte-hashes.sha1 \ data/sha-1/byte-messages.sha1 \ + data/systemd-activation/com.example.SystemdActivatable1.service \ + data/systemd-activation/com.example.SystemdActivatable2.service \ + data/systemd-activation/com.example.SystemdActivatable3.service \ + data/systemd-activation/org.freedesktop.systemd1.service \ data/valid-config-files/basic.conf \ data/valid-config-files/basic.d/basic.conf \ data/valid-config-files/entities.conf \ diff --git a/test/data/systemd-activation/com.example.SystemdActivatable1.service b/test/data/systemd-activation/com.example.SystemdActivatable1.service new file mode 100644 index 00000000..f15f0386 --- /dev/null +++ b/test/data/systemd-activation/com.example.SystemdActivatable1.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.example.SystemdActivatable1 +Exec=/bin/false 1 +SystemdService=dbus-com.example.SystemdActivatable1.service diff --git a/test/data/systemd-activation/com.example.SystemdActivatable2.service b/test/data/systemd-activation/com.example.SystemdActivatable2.service new file mode 100644 index 00000000..dcedd734 --- /dev/null +++ b/test/data/systemd-activation/com.example.SystemdActivatable2.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.example.SystemdActivatable2 +Exec=/bin/false 2 +SystemdService=dbus-com.example.SystemdActivatable2.service diff --git a/test/data/systemd-activation/com.example.SystemdActivatable3.service b/test/data/systemd-activation/com.example.SystemdActivatable3.service new file mode 100644 index 00000000..f6f0559c --- /dev/null +++ b/test/data/systemd-activation/com.example.SystemdActivatable3.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.example.SystemdActivatable3 +Exec=/bin/false 3 +SystemdService=dbus-com.example.SystemdActivatable3.service diff --git a/test/data/systemd-activation/org.freedesktop.systemd1.service b/test/data/systemd-activation/org.freedesktop.systemd1.service new file mode 100644 index 00000000..aea93113 --- /dev/null +++ b/test/data/systemd-activation/org.freedesktop.systemd1.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.systemd1 +Exec=/bin/false diff --git a/test/data/valid-config-files/systemd-activation.conf.in b/test/data/valid-config-files/systemd-activation.conf.in new file mode 100644 index 00000000..bcd6416c --- /dev/null +++ b/test/data/valid-config-files/systemd-activation.conf.in @@ -0,0 +1,11 @@ + + + @TEST_LISTEN@ + @DBUS_TEST_DATA@/systemd-activation + + + + + + diff --git a/test/sd-activation.c b/test/sd-activation.c new file mode 100644 index 00000000..14e4ae80 --- /dev/null +++ b/test/sd-activation.c @@ -0,0 +1,319 @@ +/* Unit tests for systemd activation. + * + * Copyright © 2010-2011 Nokia Corporation + * Copyright © 2015 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include + +#include "test-utils-glib.h" + +typedef struct { + TestMainContext *ctx; + DBusError e; + GError *ge; + + gchar *address; + GPid daemon_pid; + + DBusConnection *caller; + const char *caller_name; + DBusConnection *systemd; + const char *systemd_name; + DBusMessage *systemd_message; + DBusConnection *activated; + const char *activated_name; + DBusMessage *activated_message; +} Fixture; + +/* this is a macro so it gets the right line number */ +#define assert_signal(m, \ + sender, path, iface, member, signature, \ + destination) \ +do { \ + g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \ + ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_SIGNAL)); \ + g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \ + g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \ + g_assert_cmpstr (dbus_message_get_path (m), ==, path); \ + g_assert_cmpstr (dbus_message_get_interface (m), ==, iface); \ + g_assert_cmpstr (dbus_message_get_member (m), ==, member); \ + g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \ + g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \ + g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \ +} while (0) + +static DBusHandlerResult +systemd_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + Fixture *f = user_data; + + if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, + "NameAcquired") || + dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, + "NameLost")) + { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + g_assert (f->systemd_message == NULL); + f->systemd_message = dbus_message_ref (message); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult +activated_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + Fixture *f = user_data; + + if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, + "NameAcquired") || + dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, + "NameLost")) + { + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + g_assert (f->activated_message == NULL); + f->activated_message = dbus_message_ref (message); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static void +setup (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + f->ctx = test_main_context_get (); + + f->ge = NULL; + dbus_error_init (&f->e); + + f->address = test_get_dbus_daemon ( + "valid-config-files/systemd-activation.conf", + TEST_USER_ME, &f->daemon_pid); + + if (f->address == NULL) + return; + + f->caller = test_connect_to_bus (f->ctx, f->address); + f->caller_name = dbus_bus_get_unique_name (f->caller); +} + +static void +take_well_known_name (Fixture *f, + DBusConnection *connection, + const char *name) +{ + int ret; + + ret = dbus_bus_request_name (connection, name, + DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e); + test_assert_no_error (&f->e); + g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); +} + +static void +test_activation (Fixture *f, + gconstpointer context) +{ + DBusMessage *m; + + if (f->address == NULL) + return; + + /* The sender sends a message to an activatable service. */ + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal1"); + if (!dbus_message_set_destination (m, "com.example.SystemdActivatable1")) + g_error ("OOM"); + dbus_connection_send (f->caller, m, NULL); + dbus_message_unref (m); + + /* The fake systemd connects to the bus. */ + f->systemd = test_connect_to_bus (f->ctx, f->address); + if (!dbus_connection_add_filter (f->systemd, systemd_filter, f, NULL)) + g_error ("OOM"); + f->systemd_name = dbus_bus_get_unique_name (f->systemd); + take_well_known_name (f, f->systemd, "org.freedesktop.systemd1"); + + /* It gets its activation request. */ + while (f->systemd_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->systemd_message; + f->systemd_message = NULL; + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + + /* systemd starts the activatable service. */ + f->activated = test_connect_to_bus (f->ctx, f->address); + if (!dbus_connection_add_filter (f->activated, activated_filter, + f, NULL)) + g_error ("OOM"); + f->activated_name = dbus_bus_get_unique_name (f->activated); + take_well_known_name (f, f->activated, "com.example.SystemdActivatable1"); + + /* The message is delivered to the activatable service. */ + while (f->activated_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->activated_message; + f->activated_message = NULL; + assert_signal (m, f->caller_name, "/foo", + "com.example.bar", "UnicastSignal1", "", + "com.example.SystemdActivatable1"); + dbus_message_unref (m); + + /* The sender sends a message to a different activatable service. */ + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal2"); + if (!dbus_message_set_destination (m, "com.example.SystemdActivatable2")) + g_error ("OOM"); + dbus_connection_send (f->caller, m, NULL); + dbus_message_unref (m); + + /* This time systemd is already ready for it. */ + while (f->systemd_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->systemd_message; + f->systemd_message = NULL; + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + + /* The activatable service takes its name. Here I'm faking it by using + * an existing connection; in real life it would be yet another + * connection. */ + take_well_known_name (f, f->activated, "com.example.SystemdActivatable2"); + + /* The message is delivered to the activatable service. */ + while (f->activated_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->activated_message; + f->activated_message = NULL; + assert_signal (m, f->caller_name, "/foo", + "com.example.bar", "UnicastSignal2", "", + "com.example.SystemdActivatable2"); + dbus_message_unref (m); + + /* A third activation. */ + m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal3"); + if (!dbus_message_set_destination (m, "com.example.SystemdActivatable3")) + g_error ("OOM"); + dbus_connection_send (f->caller, m, NULL); + dbus_message_unref (m); + + while (f->systemd_message == NULL) + test_main_context_iterate (f->ctx, TRUE); + + m = f->systemd_message; + f->systemd_message = NULL; + assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, + "org.freedesktop.systemd1.Activator", "ActivationRequest", "s", + "org.freedesktop.systemd1"); + dbus_message_unref (m); + + /* This time activation fails */ + m = dbus_message_new_signal ("/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Activator", "ActivationFailure"); + + do + { + const char *unit = "dbus-com.example.SystemdActivatable3.service"; + const char *error_name = "com.example.Nope"; + const char *error_message = "Computer says no"; + + if (!dbus_message_append_args (m, + DBUS_TYPE_STRING, &unit, + DBUS_TYPE_STRING, &error_name, + DBUS_TYPE_STRING, &error_message, + DBUS_TYPE_INVALID)) + g_error ("OOM"); + } + while (0); + + if (!dbus_message_set_destination (m, "org.freedesktop.DBus")) + g_error ("OOM"); + dbus_connection_send (f->systemd, m, NULL); + dbus_message_unref (m); +} + +static void +teardown (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + dbus_error_free (&f->e); + g_clear_error (&f->ge); + + if (f->caller != NULL) + { + dbus_connection_close (f->caller); + dbus_connection_unref (f->caller); + f->caller = NULL; + } + + if (f->systemd != NULL) + { + dbus_connection_remove_filter (f->systemd, systemd_filter, f); + dbus_connection_close (f->systemd); + dbus_connection_unref (f->systemd); + f->systemd = NULL; + } + + if (f->activated != NULL) + { + dbus_connection_remove_filter (f->activated, activated_filter, f); + dbus_connection_close (f->activated); + dbus_connection_unref (f->activated); + f->activated = NULL; + } + + test_kill_pid (f->daemon_pid); + g_spawn_close_pid (f->daemon_pid); + test_main_context_unref (f->ctx); + g_free (f->address); +} + +int +main (int argc, + char **argv) +{ + g_test_init (&argc, &argv, NULL); + g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id="); + + g_test_add ("/sd-activation", Fixture, NULL, + setup, test_activation, teardown); + + return g_test_run (); +} diff --git a/test/test-utils-glib.c b/test/test-utils-glib.c index e2ad5791..c9fb5c8a 100644 --- a/test/test-utils-glib.c +++ b/test/test-utils-glib.c @@ -100,6 +100,9 @@ spawn_dbus_daemon (const gchar *binary, configuration, "--nofork", "--print-address=1", /* stdout */ +#ifdef DBUS_UNIX + "--systemd-activation", +#endif NULL }; #ifdef DBUS_UNIX -- cgit v1.2.1