]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
Translate Python-based tests to C
authorSimon McVittie <smcv@collabora.com>
Fri, 16 Nov 2018 18:53:11 +0000 (18:53 +0000)
committerSimon McVittie <smcv@collabora.com>
Mon, 3 Dec 2018 17:42:48 +0000 (17:42 +0000)
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
<https://gitlab.freedesktop.org/dbus/dbus/issues/135>, 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 <smcv@collabora.com>
.travis.yml
README
configure.ac
test/Makefile.am
test/dbus-daemon.c
test/name-test/Makefile.am
test/name-test/run-test-systemserver.sh
test/name-test/run-test.sh
test/name-test/test-activation-forking.py [deleted file]
test/name-test/test-wait-for-echo.py [deleted file]
tools/ci-install.sh

index e719efbd4e77dbfaffd15dc080ed5bdf0774ebb8..88425aeb44f11d15cf4c256219a60fde2468704d 100644 (file)
@@ -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 f9aafdc7a60911ef434dbd6c134fd8ff7515445b..71ebc05d5324bb7441b90c24cb1d5eccbddb28d7 100644 (file)
--- 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.
index 64a655b16740ef0c2587bbd1e342edc83d39c5ba..4b6cba6ea96d5c5107a4e45fffb0cd59d0a8912d 100644 (file)
@@ -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
index bc87446c05c9523cbaffad5f0b4070105ee12b71..408cf668dcb76c0fbf86b78363d29aa5cf0ec839 100644 (file)
@@ -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 = \
index 20935ffb19f88e91feea17136b4996ee69b9b6d4..72113f1168705cd9c6cc5f0c44e8c5d98c32ae32 100644 (file)
@@ -1,8 +1,9 @@
 /* Integration tests for the dbus-daemon
  *
  * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * 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 ();
index 8269feff36fd87d69c2f0e6d56d734f808f686eb..ea63e579290c4180969ccef0d9a049f145af4731 100644 (file)
@@ -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
 
index b4a04984488a6ead7cff69165cdeffc75ddfbed2..d8515a8ccfc2650aa95536b76f496a186186fb71 100755 (executable)
@@ -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
index 092ec69ff92908a8b5947e4ac39c1c4239302941..9c6a55cc8ef09ff1a32c16c15e58ddcbdc4e8caa 100755 (executable)
@@ -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 (file)
index 44bc1a6..0000000
+++ /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 (executable)
index d1b9e0c..0000000
+++ /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)
index b50e534e9dda8adffb28f0c816a1f6a95aedbaed..438568f61c6421f72455dcd4f65b5b34fe6e8131 100755 (executable)
@@ -143,9 +143,6 @@ case "$ci_distro" in
             libglib2.0-dev \
             libselinux1-dev \
             libx11-dev \
-            python \
-            python-dbus \
-            python-gi \
             valgrind \
             wget \
             xauth \