cleanup
[platform/upstream/glib.git] / gio / gtestdbus.c
index 151173f..4af270b 100644 (file)
@@ -14,9 +14,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Authors: David Zeuthen <davidz@redhat.com>
  *          Xavier Claessens <xavier.claessens@collabora.co.uk>
@@ -27,7 +25,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <gstdio.h>
-#ifdef HAVE_UNISTD_H
+#ifdef G_OS_UNIX
 #include <unistd.h>
 #endif
 #ifdef G_OS_WIN32
@@ -67,14 +65,15 @@ on_weak_notify_timeout (gpointer user_data)
 }
 
 static gboolean
-unref_on_idle (gpointer object)
+dispose_on_idle (gpointer object)
 {
+  g_object_run_dispose (object);
   g_object_unref (object);
   return FALSE;
 }
 
 static gboolean
-_g_object_unref_and_wait_weak_notify (gpointer object)
+_g_object_dispose_and_wait_weak_notify (gpointer object)
 {
   WeakNotifyData data;
   guint timeout_id;
@@ -86,20 +85,22 @@ _g_object_unref_and_wait_weak_notify (gpointer object)
 
   /* Drop the ref in an idle callback, this is to make sure the mainloop
    * is already running when weak notify happens */
-  g_idle_add (unref_on_idle, object);
+  g_idle_add (dispose_on_idle, object);
 
   /* Make sure we don't block forever */
   timeout_id = g_timeout_add (30 * 1000, on_weak_notify_timeout, &data);
 
   g_main_loop_run (data.loop);
 
-  g_source_remove (timeout_id);
-
   if (data.timed_out)
     {
       g_warning ("Weak notify timeout, object ref_count=%d\n",
           G_OBJECT (object)->ref_count);
     }
+  else
+    {
+      g_source_remove (timeout_id);
+    }
 
   g_main_loop_unref (data.loop);
   return data.timed_out;
@@ -320,80 +321,75 @@ _g_test_watcher_remove_pid (GPid pid)
  * A helper class for testing code which uses D-Bus without touching the user's
  * session bus.
  *
- * <refsect2 id="gio-D-Bus-Test-Scaffolding">
- *   <title>Creating unit tests using GTestDBus</title>
- *   <para>
- *     Testing of D-Bus services can be tricky because normally we only ever run
- *     D-Bus services over an existing instance of the D-Bus daemon thus we
- *     usually don't activate D-Bus services that are not yet installed into the
- *     target system. The #GTestDBus object makes this easier for us by taking care
- *     of the lower level tasks such as running a private D-Bus daemon and looking
- *     up uninstalled services in customizable locations, typically in your source code tree.
- *   </para>
- *   <para>
- *     The first thing you will need is a separate service description file for the
- *     D-Bus daemon. Typically a 'services' subdirectory of your 'tests' directory
- *     is a good place to put this file.
- *   </para>
- *   <para>
- *     The service file should list your service along with an absolute path to the
- *     uninstalled service executable in your source tree. Using autotools we would
- *     achieve this by adding a file such as 'my-server.service.in' in the services
- *     directory and have it processed by configure.
- *     <informalexample><programlisting>
+ * Note that #GTestDBus modifies the user’s environment, calling setenv().
+ * This is not thread-safe, so all #GTestDBus calls should be completed before
+ * threads are spawned, or should have appropriate locking to ensure no access
+ * conflicts to environment variables shared between #GTestDBus and other
+ * threads.
+ *
+ * ## Creating unit tests using GTestDBus
+ * 
+ * Testing of D-Bus services can be tricky because normally we only ever run
+ * D-Bus services over an existing instance of the D-Bus daemon thus we
+ * usually don't activate D-Bus services that are not yet installed into the
+ * target system. The #GTestDBus object makes this easier for us by taking care
+ * of the lower level tasks such as running a private D-Bus daemon and looking
+ * up uninstalled services in customizable locations, typically in your source
+ * code tree.
+ *
+ * The first thing you will need is a separate service description file for the
+ * D-Bus daemon. Typically a `services` subdirectory of your `tests` directory
+ * is a good place to put this file.
+ *
+ * The service file should list your service along with an absolute path to the
+ * uninstalled service executable in your source tree. Using autotools we would
+ * achieve this by adding a file such as `my-server.service.in` in the services
+ * directory and have it processed by configure.
+ * |[
  *     [D-BUS Service]
  *     Name=org.gtk.GDBus.Examples.ObjectManager
  *     Exec=@abs_top_builddir@/gio/tests/gdbus-example-objectmanager-server
- *     </programlisting></informalexample>
- *     You will also need to indicate this service directory in your test
- *     fixtures, so you will need to pass the path while compiling your
- *     test cases. Typically this is done with autotools with an added
- *     preprocessor flag specified to compile your tests such as:
- *     <informalexample><programlisting>
+ * ]|
+ * You will also need to indicate this service directory in your test
+ * fixtures, so you will need to pass the path while compiling your
+ * test cases. Typically this is done with autotools with an added
+ * preprocessor flag specified to compile your tests such as:
+ * |[
  *     -DTEST_SERVICES=\""$(abs_top_builddir)/tests/services"\"
- *     </programlisting></informalexample>
- *   </para>
- *   <para>
+ * ]|
  *     Once you have a service definition file which is local to your source tree,
- *     you can proceed to setup a GTest fixture using the GTestDBus scaffolding.
- *     <example id="gdbus-test-fixture">
- *       <title>Test Fixture for D-Bus services</title>
- *       <programlisting>
- *         <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gdbus-test-fixture.c">
- *           <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
- *         </xi:include>
- *       </programlisting>
- *     </example>
- *   </para>
- *   <para>
- *     Note that these examples only deal with isolating the D-Bus aspect of your
- *     service. To successfully run isolated unit tests on your service you may need
- *     some additional modifications to your test case fixture. For example; if your
- *     service uses GSettings and installs a schema then it is important that your test service
- *     not load the schema in the ordinary installed location (chances are that your service
- *     and schema files are not yet installed, or worse; there is an older version of the
- *     schema file sitting in the install location).
- *   </para>
- *   <para>
- *     Most of the time we can work around these obstacles using the environment. Since the
- *     environment is inherited by the D-Bus daemon created by GTestDBus and then in turn
- *     inherited by any services the D-Bus daemon activates, using the setup routine for your
- *     fixture is a practical place to help sandbox your runtime environment. For the rather
- *     typical GSettings case we can work around this by setting GSETTINGS_SCHEMA_DIR to the
- *     in tree directory holding your schemas in the above fixture_setup() routine.
- *   </para>
- *   <para>
- *     The GSettings schemas need to be locally pre-compiled for this to work. This can be achieved
- *     by compiling the schemas locally as a step before running test cases, an autotools setup might
- *     do the following in the directory holding schemas:
- *     <informalexample><programlisting>
+ * you can proceed to set up a GTest fixture using the #GTestDBus scaffolding.
+ *
+ * An example of a test fixture for D-Bus services can be found
+ * here:
+ * [gdbus-test-fixture.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-test-fixture.c)
+ *
+ * Note that these examples only deal with isolating the D-Bus aspect of your
+ * service. To successfully run isolated unit tests on your service you may need
+ * some additional modifications to your test case fixture. For example; if your
+ * service uses GSettings and installs a schema then it is important that your test service
+ * not load the schema in the ordinary installed location (chances are that your service
+ * and schema files are not yet installed, or worse; there is an older version of the
+ * schema file sitting in the install location).
+ *
+ * Most of the time we can work around these obstacles using the
+ * environment. Since the environment is inherited by the D-Bus daemon
+ * created by #GTestDBus and then in turn inherited by any services the
+ * D-Bus daemon activates, using the setup routine for your fixture is
+ * a practical place to help sandbox your runtime environment. For the
+ * rather typical GSettings case we can work around this by setting
+ * `GSETTINGS_SCHEMA_DIR` to the in tree directory holding your schemas
+ * in the above fixture_setup() routine.
+ *
+ * The GSettings schemas need to be locally pre-compiled for this to work. This can be achieved
+ * by compiling the schemas locally as a step before running test cases, an autotools setup might
+ * do the following in the directory holding schemas:
+ * |[
  *     all-am:
  *             $(GLIB_COMPILE_SCHEMAS) .
  *
  *     CLEANFILES += gschemas.compiled
- *     </programlisting></informalexample>
- *   </para>
- * </refsect2>
+ * ]|
  */
 
 typedef struct _GTestDBusClass   GTestDBusClass;
@@ -422,6 +418,7 @@ struct _GTestDBusPrivate
   GTestDBusFlags flags;
   GPtrArray *service_dirs;
   GPid bus_pid;
+  gint bus_stdout_fd;
   gchar *bus_address;
   gboolean up;
 };
@@ -514,14 +511,14 @@ g_test_dbus_class_init (GTestDBusClass *klass)
   /**
    * GTestDBus:flags:
    *
-   * #GTestDBusFlags specifying the behaviour of the dbus session
+   * #GTestDBusFlags specifying the behaviour of the D-Bus session.
    *
    * Since: 2.34
    */
   g_object_class_install_property (object_class, PROP_FLAGS,
     g_param_spec_flags ("flags",
-                        P_("dbus session flags"),
-                        P_("Flags specifying the behaviour of the dbus session"),
+                        P_("D-Bus session flags"),
+                        P_("Flags specifying the behaviour of the D-Bus session"),
                         G_TYPE_TEST_DBUS_FLAGS, G_TEST_DBUS_NONE,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                         G_PARAM_STATIC_STRINGS));
@@ -553,10 +550,10 @@ write_config_file (GTestDBus *self)
 
   for (i = 0; i < self->priv->service_dirs->len; i++)
     {
-      const gchar *path = g_ptr_array_index (self->priv->service_dirs, i);
+      const gchar *dir_path = g_ptr_array_index (self->priv->service_dirs, i);
 
       g_string_append_printf (contents,
-          "  <servicedir>%s</servicedir>\n", path);
+          "  <servicedir>%s</servicedir>\n", dir_path);
     }
 
   g_string_append (contents,
@@ -570,24 +567,23 @@ write_config_file (GTestDBus *self)
       "  </policy>\n"
       "</busconfig>\n");
 
+  close (fd);
   g_file_set_contents (path, contents->str, contents->len, &error);
   g_assert_no_error (error);
 
   g_string_free (contents, TRUE);
 
-  close (fd);
-
   return path;
 }
 
 static void
 start_daemon (GTestDBus *self)
 {
-  gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL};
+  const gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL};
   gchar *config_path;
   gchar *config_arg;
-  gint stdout_fd;
   GIOChannel *channel;
+  gint stdout_fd2;
   gsize termpos;
   GError *error = NULL;
 
@@ -601,7 +597,7 @@ start_daemon (GTestDBus *self)
 
   /* Spawn dbus-daemon */
   g_spawn_async_with_pipes (NULL,
-                            argv,
+                            (gchar **) argv,
                             NULL,
 #ifdef G_OS_WIN32
                             /* We Need this to get the pid returned on win32 */
@@ -612,15 +608,21 @@ start_daemon (GTestDBus *self)
                             NULL,
                             &self->priv->bus_pid,
                             NULL,
-                            &stdout_fd,
+                            &self->priv->bus_stdout_fd,
                             NULL,
                             &error);
   g_assert_no_error (error);
 
   _g_test_watcher_add_pid (self->priv->bus_pid);
 
-  /* Read bus address from daemon' stdout */
-  channel = g_io_channel_unix_new (stdout_fd);
+  /* Read bus address from daemon' stdout. We have to be careful to avoid
+   * closing the FD, as it is passed to any D-Bus service activated processes,
+   * and if we close it, they will get a SIGPIPE and die when they try to write
+   * to their stdout. */
+  stdout_fd2 = dup (self->priv->bus_stdout_fd);
+  g_assert_cmpint (stdout_fd2, >=, 0);
+  channel = g_io_channel_unix_new (stdout_fd2);
+
   g_io_channel_read_line (channel, &self->priv->bus_address, NULL,
       &termpos, &error);
   g_assert_no_error (error);
@@ -664,6 +666,8 @@ stop_daemon (GTestDBus *self)
   _g_test_watcher_remove_pid (self->priv->bus_pid);
   g_spawn_close_pid (self->priv->bus_pid);
   self->priv->bus_pid = 0;
+  close (self->priv->bus_stdout_fd);
+  self->priv->bus_stdout_fd = -1;
 
   g_free (self->priv->bus_address);
   self->priv->bus_address = NULL;
@@ -689,7 +693,7 @@ g_test_dbus_new (GTestDBusFlags flags)
  * g_test_dbus_get_flags:
  * @self: a #GTestDBus
  *
- * Gets the flags of the #GTestDBus object.
+ * Get the flags of the #GTestDBus object.
  *
  * Returns: the value of #GTestDBus:flags property
  */
@@ -705,11 +709,11 @@ g_test_dbus_get_flags (GTestDBus *self)
  * g_test_dbus_get_bus_address:
  * @self: a #GTestDBus
  *
- * Get the address on which dbus-daemon is running. if g_test_dbus_up() has not
+ * Get the address on which dbus-daemon is running. If g_test_dbus_up() has not
  * been called yet, %NULL is returned. This can be used with
- * g_dbus_connection_new_for_address()
+ * g_dbus_connection_new_for_address().
  *
- * Returns: the address of the bus, or %NULL.
+ * Returns: (allow-none): the address of the bus, or %NULL.
  */
 const gchar *
 g_test_dbus_get_bus_address (GTestDBus *self)
@@ -724,7 +728,7 @@ g_test_dbus_get_bus_address (GTestDBus *self)
  * @self: a #GTestDBus
  * @path: path to a directory containing .service files
  *
- * Add a path where dbus-daemon will lookup for .services files. This can't be
+ * Add a path where dbus-daemon will look up .service files. This can't be
  * called after g_test_dbus_up().
  */
 void
@@ -811,7 +815,7 @@ g_test_dbus_down (GTestDBus *self)
     stop_daemon (self);
 
   if (connection != NULL)
-    _g_object_unref_and_wait_weak_notify (connection);
+    _g_object_dispose_and_wait_weak_notify (connection);
 
   g_test_dbus_unset ();
   self->priv->up = FALSE;