From ebe5744375885542daadddd84b143a4414bae513 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 16 Nov 2018 19:22:42 +0000 Subject: test-service: Write diagnostics to stderr When run by a TAP test, stdout is reserved for machine-readable TAP output. Signed-off-by: Simon McVittie --- test/test-service.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test-service.c b/test/test-service.c index 52ea3833..34bedb2d 100644 --- a/test/test-service.c +++ b/test/test-service.c @@ -61,7 +61,7 @@ check_hello_from_self_reply (DBusPendingCall *pcall, if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN) { const char *s; - printf ("Reply from HelloFromSelf received\n"); + fprintf (stderr, "Reply from HelloFromSelf received\n"); if (!dbus_message_get_args (echo_message, &error, @@ -95,7 +95,7 @@ check_hello_from_self_reply (DBusPendingCall *pcall, } else if (dbus_set_error_from_message (&error, reply)) { - printf ("Error type in reply: %s\n", error.message); + fprintf (stderr, "Error type in reply: %s\n", error.message); if (strcmp (error.name, DBUS_ERROR_NO_MEMORY) != 0) { @@ -156,7 +156,7 @@ handle_run_hello_from_self (DBusConnection *connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - printf ("Sending HelloFromSelf\n"); + fprintf (stderr, "Sending HelloFromSelf\n"); _dbus_verbose ("*** Sending message to self\n"); self_message = dbus_message_new_method_call ("org.freedesktop.DBus.TestSuiteEchoService", @@ -174,7 +174,7 @@ handle_run_hello_from_self (DBusConnection *connection, if (!dbus_pending_call_set_notify (pcall, check_hello_from_self_reply, (void *)message, NULL)) die("No memory"); - printf ("Sent HelloFromSelf\n"); + fprintf (stderr, "Sent HelloFromSelf\n"); return DBUS_HANDLER_RESULT_HANDLED; } @@ -351,7 +351,7 @@ path_message_func (DBusConnection *connection, "HelloFromSelf")) { DBusMessage *reply; - printf ("Received the HelloFromSelf message\n"); + fprintf (stderr, "Received the HelloFromSelf message\n"); reply = dbus_message_new_method_return (message); if (reply == NULL) -- cgit v1.2.1 From 1191262f5ebb881f9a07862f0b7b9176c502edc1 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 16 Nov 2018 18:53:11 +0000 Subject: Translate Python-based tests to C This simplifies bootstrapping: now you don't have to build dbus, build dbus-python (with GLib), and use dbus-python to test dbus. It also avoids test failures when using facilities like AddressSanitizer. When libdbus is built with AddressSanitizer, but the system copies of Python and dbus-python were not, dbus-python will exit the Python interpreter on load, because libasan wasn't already initialized. The simplest way to avoid this is to not use Python: the scripts are not *that* hard to translate into C. Both of these tests happen to be conditionally compiled for Unix only. test_activation_forking() relies on code in TestSuiteForkingEchoService that calls fork(), which can only work on Unix; meanwhile, test_system_signals() tests the system bus configuration, which is only relevant to Unix because we don't support using dbus-daemon as a privilege boundary on Windows (and in any case D-Bus is not a Windows OS feature, so the system bus cannot be used to communicate with OS services like it can on most Linux systems). This is also a partial solution to , by reducing the size of name-test/. For this to work, we need to build the test-service helper executable even if embedded tests are disabled. Signed-off-by: Simon McVittie --- .travis.yml | 4 +- README | 24 +-- configure.ac | 18 +-- test/Makefile.am | 2 +- test/dbus-daemon.c | 238 +++++++++++++++++++++++++++++- test/name-test/Makefile.am | 3 +- test/name-test/run-test-systemserver.sh | 18 +-- test/name-test/run-test.sh | 18 +-- test/name-test/test-activation-forking.py | 60 -------- test/name-test/test-wait-for-echo.py | 41 ----- tools/ci-install.sh | 3 - 11 files changed, 252 insertions(+), 177 deletions(-) delete mode 100644 test/name-test/test-activation-forking.py delete mode 100755 test/name-test/test-wait-for-echo.py diff --git a/.travis.yml b/.travis.yml index e719efbd..88425aeb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,7 @@ dist: trusty language: c script: - ./tools/ci-install.sh - # python-dbus and python-gi aren't available to Travis's version of - # Python in /opt, which it uses as a default - - PYTHON=/usr/bin/python ci_parallel=2 ci_sudo=yes ./tools/ci-build.sh + - ci_parallel=2 ci_sudo=yes ./tools/ci-build.sh env: - ci_variant=production diff --git a/README b/README index f9aafdc7..71ebc05d 100644 --- a/README +++ b/README @@ -166,20 +166,10 @@ for your binding. Bootstrapping D-Bus on new platforms === -A full build of D-Bus, with all regression tests enabled and run, has some -dependencies which themselves depend on D-Bus, either for compilation or -for some of *their* regression tests: GLib, dbus-glib and dbus-python are -currently affected. - -To avoid circular dependencies, when bootstrapping D-Bus for the first time -on a new OS or CPU architecture, you can either cross-compile some of -those components, or choose the build order and options carefully: - -* build and install D-Bus without tests - - do not use the --enable-modular-tests=yes configure option - - do not use the --enable-tests=yes configure option -* build and install GLib, again without tests -* use those versions of libdbus and GLib to build and install dbus-glib -* ... and use those to install dbus-python -* rebuild libdbus; this time you can run all of the tests -* rebuild GLib; this time you can run all of the tests +A full build of dbus, with all regression tests enabled and run, depends +on GLib. A full build of GLib, with all regression tests enabled and run, +depends on dbus. + +To break this cycle, don't enable full test coverage (for at least one +of those projects) during bootstrapping. You can rebuild with full test +coverage after you have built both dbus and GLib at least once. diff --git a/configure.ac b/configure.ac index 64a655b1..4b6cba6e 100644 --- a/configure.ac +++ b/configure.ac @@ -247,7 +247,7 @@ AC_ARG_ENABLE([tests], [ if test "x$enableval" = xyes; then AC_MSG_NOTICE([Full test coverage was requested with --enable-tests=yes]) - AC_MSG_NOTICE([This has many dependencies (GLib, Python etc.)]) + AC_MSG_NOTICE([This requires GLib]) fi enable_embedded_tests=$enableval enable_modular_tests=$enableval @@ -313,22 +313,6 @@ AC_ARG_ENABLE([installed-tests], AM_CONDITIONAL([DBUS_ENABLE_INSTALLED_TESTS], [test "x$enable_installed_tests" = xyes]) -if test "x$enable_tests" = xyes; then - # full test coverage is required, Python is a hard dependency - AC_MSG_NOTICE([Full test coverage (--enable-tests=yes) requires Python, dbus-python, pygi]) - AM_PATH_PYTHON([2.6]) - AC_MSG_CHECKING([for Python modules for full test coverage]) - if "$PYTHON" -c "import dbus, gi.repository.GObject, dbus.mainloop.glib"; then - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - AC_MSG_ERROR([cannot import dbus, gi.repository.GObject, dbus.mainloop.glib Python modules]) - fi -else - # --enable-tests not given: do not abort if Python is missing - AM_PATH_PYTHON([2.6], [], [:]) -fi - if test x$enable_verbose_mode = xyes; then AC_DEFINE(DBUS_ENABLE_VERBOSE_MODE,1,[Support a verbose mode]) fi diff --git a/test/Makefile.am b/test/Makefile.am index bc87446c..408cf668 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -64,7 +64,6 @@ TEST_BINARIES = \ test-exit \ test-names \ test-segfault \ - test-service \ test-shell-service \ $(NULL) @@ -145,6 +144,7 @@ nobase_testexec_PROGRAMS = nobase_testmeta_DATA = installable_helpers = \ + test-service \ test-sleep-forever \ $(NULL) installable_tests = \ diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c index 20935ffb..72113f11 100644 --- a/test/dbus-daemon.c +++ b/test/dbus-daemon.c @@ -1,8 +1,9 @@ /* Integration tests for the dbus-daemon * * Author: Simon McVittie + * Copyright © 2008 Red Hat, Inc. * Copyright © 2010-2011 Nokia Corporation - * Copyright © 2015 Collabora Ltd. + * Copyright © 2015-2018 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files @@ -103,6 +104,7 @@ typedef struct { gboolean right_conn_echo; gboolean right_conn_hold; gboolean wait_forever_called; + guint activation_forking_counter; gchar *tmp_runtime_dir; gchar *saved_runtime_dir; @@ -1946,6 +1948,225 @@ test_fd_limit (Fixture *f, #endif /* !HAVE_PRLIMIT */ } + +#define ECHO_SERVICE "org.freedesktop.DBus.TestSuiteEchoService" +#define FORKING_ECHO_SERVICE "org.freedesktop.DBus.TestSuiteForkingEchoService" +#define ECHO_SERVICE_PATH "/org/freedesktop/TestSuite" +#define ECHO_SERVICE_INTERFACE "org.freedesktop.TestSuite" + +/* + * Helper for test_activation_forking: whenever the forking service is + * activated, start it again. + */ +static DBusHandlerResult +activation_forking_signal_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + Fixture *f = user_data; + + if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, + "NameOwnerChanged")) + { + dbus_bool_t ok; + const char *name; + const char *old_owner; + const char *new_owner; + + ok = dbus_message_get_args (message, &f->e, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID); + test_assert_no_error (&f->e); + g_assert_true (ok); + + g_test_message ("owner of \"%s\": \"%s\" -> \"%s\"", + name, old_owner, new_owner); + + if (g_strcmp0 (name, FORKING_ECHO_SERVICE) != 0) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (f->activation_forking_counter > 10) + { + g_test_message ("Activated 10 times OK, TestSuiteForkingEchoService pass"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + f->activation_forking_counter++; + + if (g_strcmp0 (new_owner, "") == 0) + { + /* Reactivate it, and tell it to exit immediately. */ + DBusMessage *echo_call = NULL; + DBusMessage *exit_call = NULL; + gchar *payload = NULL; + + payload = g_strdup_printf ("counter %u", f->activation_forking_counter); + echo_call = dbus_message_new_method_call (FORKING_ECHO_SERVICE, + ECHO_SERVICE_PATH, + ECHO_SERVICE_INTERFACE, + "Echo"); + exit_call = dbus_message_new_method_call (FORKING_ECHO_SERVICE, + ECHO_SERVICE_PATH, + ECHO_SERVICE_INTERFACE, + "Exit"); + + if (echo_call == NULL || + !dbus_message_append_args (echo_call, + DBUS_TYPE_STRING, &payload, + DBUS_TYPE_INVALID) || + exit_call == NULL || + !dbus_connection_send (connection, echo_call, NULL) || + !dbus_connection_send (connection, exit_call, NULL)) + g_error ("OOM"); + + dbus_clear_message (&echo_call); + dbus_clear_message (&exit_call); + g_free (payload); + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/* + * Assert that Unix services are allowed to daemonize, and this does not + * cause us to signal an activation failure. + */ +static void +test_activation_forking (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + DBusMessage *call = NULL; + DBusMessage *reply = NULL; + const char *hello = "hello world"; + + if (f->skip) + return; + + if (!dbus_connection_add_filter (f->left_conn, + activation_forking_signal_filter, + f, NULL)) + g_error ("OOM"); + + /* Start it up */ + call = dbus_message_new_method_call (FORKING_ECHO_SERVICE, + ECHO_SERVICE_PATH, + ECHO_SERVICE_INTERFACE, + "Echo"); + + if (call == NULL || + !dbus_message_append_args (call, + DBUS_TYPE_STRING, &hello, + DBUS_TYPE_INVALID)) + g_error ("OOM"); + + dbus_bus_add_match (f->left_conn, + "sender='org.freedesktop.DBus'", + &f->e); + test_assert_no_error (&f->e); + + reply = test_main_context_call_and_wait (f->ctx, f->left_conn, call, + DBUS_TIMEOUT_USE_DEFAULT); + dbus_clear_message (&call); + g_test_message ("TestSuiteForkingEchoService initial reply OK"); + dbus_clear_message (&reply); + + /* Now monitor for exits: when that happens, start it up again. + * The goal here is to try to hit any race conditions in activation. */ + f->activation_forking_counter = 0; + + call = dbus_message_new_method_call (FORKING_ECHO_SERVICE, + ECHO_SERVICE_PATH, + ECHO_SERVICE_INTERFACE, + "Exit"); + + if (call == NULL || !dbus_connection_send (f->left_conn, call, NULL)) + g_error ("OOM"); + + dbus_clear_message (&call); + + while (f->activation_forking_counter <= 10) + test_main_context_iterate (f->ctx, TRUE); + + dbus_connection_remove_filter (f->left_conn, + activation_forking_signal_filter, f); +} + +/* + * Helper for test_system_signals: Receive Foo signals and add them to + * the held_messages queue. + */ +static DBusHandlerResult +foo_signal_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + Fixture *f = user_data; + + if (dbus_message_is_signal (message, ECHO_SERVICE_INTERFACE, "Foo")) + g_queue_push_tail (&f->held_messages, dbus_message_ref (message)); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/* + * Assert that the system bus(-like) configuration allows services + * to emit signals, even if there is no service-specific configuration + * to allow it. + * + * Essentially equivalent to the old test/name-test/test-wait-for-echo.py. + */ +static void +test_system_signals (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + DBusMessage *call = NULL; + DBusMessage *response = NULL; + + g_test_bug ("18229"); + + if (f->skip) + return; + + if (!dbus_connection_add_filter (f->left_conn, foo_signal_filter, + f, NULL)) + g_error ("OOM"); + + dbus_bus_add_match (f->left_conn, + "interface='" ECHO_SERVICE_INTERFACE "'", + &f->e); + test_assert_no_error (&f->e); + + call = dbus_message_new_method_call (ECHO_SERVICE, + ECHO_SERVICE_PATH, + ECHO_SERVICE_INTERFACE, + "EmitFoo"); + + if (call == NULL || !dbus_connection_send (f->left_conn, call, NULL)) + g_error ("OOM"); + + dbus_clear_message (&call); + + while (g_queue_get_length (&f->held_messages) < 1) + test_main_context_iterate (f->ctx, TRUE); + + g_test_message ("got signal"); + g_assert_cmpuint (g_queue_get_length (&f->held_messages), ==, 1); + response = g_queue_pop_head (&f->held_messages); + g_assert_cmpint (dbus_message_get_type (response), ==, + DBUS_MESSAGE_TYPE_SIGNAL); + g_assert_cmpstr (dbus_message_get_interface (response), ==, + ECHO_SERVICE_INTERFACE); + g_assert_cmpstr (dbus_message_get_path (response), ==, + ECHO_SERVICE_PATH); + g_assert_cmpstr (dbus_message_get_signature (response), ==, "d"); + g_assert_cmpstr (dbus_message_get_member (response), ==, "Foo"); + dbus_clear_message (&response); + + dbus_connection_remove_filter (f->left_conn, foo_signal_filter, f); +} #endif static void @@ -2077,6 +2298,16 @@ static Config as_another_user_config = { * real system bus does */ TEST_USER_ROOT, SPECIFY_ADDRESS }; + +static Config tmp_session_config = { + NULL, 1, "valid-config-files/tmp-session.conf", + TEST_USER_ME, SPECIFY_ADDRESS +}; + +static Config nearly_system_config = { + NULL, 1, "valid-config-files-system/tmp-session-like-system.conf", + TEST_USER_ME, SPECIFY_ADDRESS +}; #endif int @@ -2160,6 +2391,11 @@ main (int argc, setup, test_fd_limit, teardown); g_test_add ("/fd-limit/system", Fixture, &as_another_user_config, setup, test_fd_limit, teardown); + + g_test_add ("/activation/forking", Fixture, &tmp_session_config, + setup, test_activation_forking, teardown); + g_test_add ("/system-policy/allow-signals", Fixture, &nearly_system_config, + setup, test_system_signals, teardown); #endif ret = g_test_run (); diff --git a/test/name-test/Makefile.am b/test/name-test/Makefile.am index 8269feff..ea63e579 100644 --- a/test/name-test/Makefile.am +++ b/test/name-test/Makefile.am @@ -37,7 +37,6 @@ AM_TESTS_ENVIRONMENT = \ export DBUS_TOP_BUILDDIR=@abs_top_builddir@; \ export DBUS_TOP_SRCDIR=@abs_top_srcdir@; \ export PATH="@abs_top_builddir@/bus:$$PATH"; \ - export PYTHON=@PYTHON@; \ export DBUS_TEST_DATA=@abs_top_builddir@/test/data; \ export DBUS_TEST_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT); \ export DBUS_TEST_DBUS_LAUNCH=@abs_top_builddir@/tools/dbus-launch$(EXEEXT); \ @@ -60,7 +59,7 @@ TESTS += \ endif endif -EXTRA_DIST=run-test.sh run-test-systemserver.sh test-wait-for-echo.py test-activation-forking.py +EXTRA_DIST=run-test.sh run-test-systemserver.sh if DBUS_ENABLE_EMBEDDED_TESTS diff --git a/test/name-test/run-test-systemserver.sh b/test/name-test/run-test-systemserver.sh index b4a04984..d8515a8c 100755 --- a/test/name-test/run-test-systemserver.sh +++ b/test/name-test/run-test-systemserver.sh @@ -68,22 +68,8 @@ dbus_send_test () { rm -f output.tmp } -py_test () { - t="$1" - shift - if test "x$PYTHON" = "x:"; then - interpret_result 77 "$t" "(Python interpreter not found)" - else - e=0 - echo "# running test $t" - $PYTHON "$DBUS_TOP_SRCDIR/test/name-test/$t" "$@" >&2 || e=$? - interpret_result "$e" "$t" "$@" - fi -} - test_num=1 -# TAP syntax: we plan to run 2 tests -echo "1..2" +# TAP syntax: we plan to run 1 test +echo "1..1" dbus_send_test test-expected-echo-fail 1 DBus.Error --print-reply --dest=org.freedesktop.DBus.TestSuiteEchoService /org/freedesktop/TestSuite org.freedesktop.TestSuite.Echo string:hi -py_test test-wait-for-echo.py diff --git a/test/name-test/run-test.sh b/test/name-test/run-test.sh index 092ec69f..9c6a55cc 100755 --- a/test/name-test/run-test.sh +++ b/test/name-test/run-test.sh @@ -53,22 +53,8 @@ c_test () { interpret_result "$e" "$t" "$@" } -py_test () { - t="$1" - shift - if test "x$PYTHON" = "x:"; then - interpret_result 77 "$t" "(Python interpreter not found)" - else - e=0 - echo "# running test $t" - $PYTHON "$DBUS_TOP_SRCDIR/test/name-test/$t" "$@" >&2 || e=$? - interpret_result "$e" "$t" "$@" - fi -} - test_num=1 -# TAP test plan: we will run 2 tests -echo "1..2" +# TAP test plan: we will run 1 test +echo "1..1" -py_test test-activation-forking.py c_test test-autolaunch diff --git a/test/name-test/test-activation-forking.py b/test/name-test/test-activation-forking.py deleted file mode 100644 index 44bc1a6e..00000000 --- a/test/name-test/test-activation-forking.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env python - -import os,sys - -try: - from gi.repository import GObject - import dbus - import dbus.mainloop.glib -except: - print("Failed import, aborting test") - sys.exit(0) - -dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) -loop = GObject.MainLoop() - -exitcode = 0 - -bus = dbus.SessionBus() -bus_iface = dbus.Interface(bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus'), 'org.freedesktop.DBus') - -o = bus.get_object('org.freedesktop.DBus.TestSuiteForkingEchoService', '/org/freedesktop/TestSuite') -i = dbus.Interface(o, 'org.freedesktop.TestSuite') - -# Start it up -reply = i.Echo("hello world") -print("TestSuiteForkingEchoService initial reply OK") - -def ignore(*args, **kwargs): - pass - -# Now monitor for exits, when that happens, start it up again. -# The goal here is to try to hit any race conditions in activation. -counter = 0 -def on_forking_echo_owner_changed(name, old, new): - global counter - global o - global i - if counter > 10: - print("Activated 10 times OK, TestSuiteForkingEchoService pass") - loop.quit() - return - counter += 1 - if new == '': - o = bus.get_object('org.freedesktop.DBus.TestSuiteForkingEchoService', '/org/freedesktop/TestSuite') - i = dbus.Interface(o, 'org.freedesktop.TestSuite') - i.Echo("counter %r" % counter) - i.Exit(reply_handler=ignore, error_handler=ignore) - -bus_iface.connect_to_signal('NameOwnerChanged', on_forking_echo_owner_changed, arg0='org.freedesktop.DBus.TestSuiteForkingEchoService') - -i.Exit(reply_handler=ignore, error_handler=ignore) - -def check_counter(): - if counter == 0: - print("Failed to get NameOwnerChanged for TestSuiteForkingEchoService") - sys.exit(1) -GObject.timeout_add(15000, check_counter) - -loop.run() -sys.exit(0) diff --git a/test/name-test/test-wait-for-echo.py b/test/name-test/test-wait-for-echo.py deleted file mode 100755 index d1b9e0c4..00000000 --- a/test/name-test/test-wait-for-echo.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python - -import os,sys - -try: - import dbus - import dbus.mainloop.glib - from gi.repository import GObject -except: - print("Failed import, aborting test") - sys.exit(0) - -dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) -loop = GObject.MainLoop() - -exitcode = 0 - -def handle_noreceipt(): - print("Failed to get signal") - global exitcode - exitcode = 1 - loop.quit() - -GObject.timeout_add(7000, handle_noreceipt) - -bus = dbus.SessionBus() - -def sighandler(*args, **kwargs): - print("got signal") - loop.quit() - -bus.add_signal_receiver(sighandler, dbus_interface='org.freedesktop.TestSuite', signal_name='Foo') - -o = bus.get_object('org.freedesktop.DBus.TestSuiteEchoService', '/org/freedesktop/TestSuite') -i = dbus.Interface(o, 'org.freedesktop.TestSuite') -def nullhandler(*args, **kwargs): - pass -i.EmitFoo(reply_handler=nullhandler, error_handler=nullhandler) - -loop.run() -sys.exit(exitcode) diff --git a/tools/ci-install.sh b/tools/ci-install.sh index b50e534e..438568f6 100755 --- a/tools/ci-install.sh +++ b/tools/ci-install.sh @@ -143,9 +143,6 @@ case "$ci_distro" in libglib2.0-dev \ libselinux1-dev \ libx11-dev \ - python \ - python-dbus \ - python-gi \ valgrind \ wget \ xauth \ -- cgit v1.2.1