X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgtestdbus.c;h=4af270be0fb32e625047df3e6e60c2d22e18da51;hb=d9ad40b4eaf1a9197ab363de4346a8d84f45f5c1;hp=f205733d8591cb65d1095f9f65e2610b92da7c43;hpb=bb78753a661f7149fc0ac85cfdbf10f9f7e9fc42;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gtestdbus.c b/gio/gtestdbus.c index f205733..4af270b 100644 --- a/gio/gtestdbus.c +++ b/gio/gtestdbus.c @@ -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 . * * Authors: David Zeuthen * Xavier Claessens @@ -26,7 +24,13 @@ #include #include +#include +#ifdef G_OS_UNIX #include +#endif +#ifdef G_OS_WIN32 +#include +#endif #include @@ -39,7 +43,6 @@ #include "glibintl.h" #ifdef G_OS_WIN32 -#define _WIN32_WINNT 0x0500 #include #endif @@ -62,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; } -gboolean -_g_object_unref_and_wait_weak_notify (gpointer object) +static gboolean +_g_object_dispose_and_wait_weak_notify (gpointer object) { WeakNotifyData data; guint timeout_id; @@ -81,21 +85,24 @@ _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; } @@ -311,7 +318,78 @@ _g_test_watcher_remove_pid (GPid pid) * @short_description: D-Bus testing helper * @include: gio/gio.h * - * Helper to test D-Bus code wihtout messing up with user' session bus. + * A helper class for testing code which uses D-Bus without touching the user's + * session bus. + * + * 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 + * ]| + * 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"\" + * ]| + * Once you have a service definition file which is local to your source tree, + * 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 + * ]| */ typedef struct _GTestDBusClass GTestDBusClass; @@ -340,6 +418,7 @@ struct _GTestDBusPrivate GTestDBusFlags flags; GPtrArray *service_dirs; GPid bus_pid; + gint bus_stdout_fd; gchar *bus_address; gboolean up; }; @@ -350,14 +429,12 @@ enum PROP_FLAGS, }; -G_DEFINE_TYPE (GTestDBus, g_test_dbus, G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_PRIVATE (GTestDBus, g_test_dbus, G_TYPE_OBJECT) static void g_test_dbus_init (GTestDBus *self) { - self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), G_TYPE_TEST_DBUS, - GTestDBusPrivate); - + self->priv = g_test_dbus_get_instance_private (self); self->priv->service_dirs = g_ptr_array_new_with_free_func (g_free); } @@ -431,37 +508,34 @@ g_test_dbus_class_init (GTestDBusClass *klass) object_class->get_property = g_test_dbus_get_property; object_class->set_property = g_test_dbus_set_property; - g_type_class_add_private (object_class, sizeof (GTestDBusPrivate)); - /** * 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)); } -static GFile * +static gchar * write_config_file (GTestDBus *self) { GString *contents; - GFile *file; - GFileIOStream *iostream; + gint fd; guint i; GError *error = NULL; + gchar *path = NULL; - file = g_file_new_tmp ("g-test-dbus-XXXXXX", &iostream, &error); + fd = g_file_open_tmp ("g-test-dbus-XXXXXX", &path, &error); g_assert_no_error (error); - g_object_unref (iostream); contents = g_string_new (NULL); g_string_append (contents, @@ -476,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, - " %s\n", path); + " %s\n", dir_path); } g_string_append (contents, @@ -493,56 +567,62 @@ write_config_file (GTestDBus *self) " \n" "\n"); - g_file_replace_contents (file, contents->str, contents->len, - NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); + close (fd); + g_file_set_contents (path, contents->str, contents->len, &error); g_assert_no_error (error); g_string_free (contents, TRUE); - return file; + return path; } static void start_daemon (GTestDBus *self) { - gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL}; - GFile *file; + 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; + if (g_getenv ("G_TEST_DBUS_DAEMON") != NULL) + argv[0] = (gchar *)g_getenv ("G_TEST_DBUS_DAEMON"); + /* Write config file and set its path in argv */ - file = write_config_file (self); - config_path = g_file_get_path (file); + config_path = write_config_file (self); config_arg = g_strdup_printf ("--config-file=%s", config_path); argv[2] = config_arg; /* Spawn dbus-daemon */ g_spawn_async_with_pipes (NULL, - argv, + (gchar **) argv, NULL, - G_SPAWN_SEARCH_PATH #ifdef G_OS_WIN32 - /* We Need this to get the pid returned on win32 */ - | G_SPAWN_DO_NOT_REAP_CHILD + /* We Need this to get the pid returned on win32 */ + G_SPAWN_DO_NOT_REAP_CHILD | #endif - , + G_SPAWN_SEARCH_PATH, NULL, 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); @@ -558,7 +638,7 @@ start_daemon (GTestDBus *self) g_spawn_command_line_async (command, NULL); g_free (command); - usleep (500 * 1000); + g_usleep (500 * 1000); } /* Cleanup */ @@ -566,9 +646,9 @@ start_daemon (GTestDBus *self) g_assert_no_error (error); g_io_channel_unref (channel); - g_file_delete (file, NULL, &error); - g_assert_no_error (error); - g_object_unref (file); + /* Don't use g_file_delete since it calls into gvfs */ + if (g_unlink (config_path) != 0) + g_assert_not_reached (); g_free (config_path); g_free (config_arg); @@ -586,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; @@ -611,6 +693,8 @@ g_test_dbus_new (GTestDBusFlags flags) * g_test_dbus_get_flags: * @self: a #GTestDBus * + * Get the flags of the #GTestDBus object. + * * Returns: the value of #GTestDBus:flags property */ GTestDBusFlags @@ -625,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) @@ -644,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 @@ -662,7 +746,7 @@ g_test_dbus_add_service_dir (GTestDBus *self, * @self: a #GTestDBus * * Start a dbus-daemon instance and set DBUS_SESSION_BUS_ADDRESS. After this - * call, it is safe for unit tests to start sending messages on the session bug. + * call, it is safe for unit tests to start sending messages on the session bus. * * If this function is called from setup callback of g_test_add(), * g_test_dbus_down() must be called in its teardown callback. @@ -679,6 +763,7 @@ g_test_dbus_up (GTestDBus *self) start_daemon (self); + g_test_dbus_unset (); g_setenv ("DBUS_SESSION_BUS_ADDRESS", self->priv->bus_address, TRUE); self->priv->up = TRUE; } @@ -730,9 +815,9 @@ 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_unsetenv ("DBUS_SESSION_BUS_ADDRESS"); + g_test_dbus_unset (); self->priv->up = FALSE; } @@ -751,4 +836,6 @@ g_test_dbus_unset (void) { g_unsetenv ("DISPLAY"); g_unsetenv ("DBUS_SESSION_BUS_ADDRESS"); + g_unsetenv ("DBUS_STARTER_ADDRESS"); + g_unsetenv ("DBUS_STARTER_BUS_TYPE"); }