From a103af968253f36f4c2d785f741d4b9447b2e26b Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Thu, 6 May 2010 11:22:25 -0400 Subject: Prefix all tests with gdbus- This is for the upcoming merge. --- gdbus/tests/Makefile.am | 76 +- gdbus/tests/addresses.c | 77 -- gdbus/tests/connection.c | 653 ----------------- gdbus/tests/error.c | 198 ------ gdbus/tests/exit-on-close.c | 82 --- gdbus/tests/export.c | 1410 ------------------------------------- gdbus/tests/gdbus-addresses.c | 77 ++ gdbus/tests/gdbus-connection.c | 653 +++++++++++++++++ gdbus/tests/gdbus-error.c | 198 ++++++ gdbus/tests/gdbus-exit-on-close.c | 82 +++ gdbus/tests/gdbus-export.c | 1410 +++++++++++++++++++++++++++++++++++++ gdbus/tests/gdbus-introspection.c | 169 +++++ gdbus/tests/gdbus-names.c | 749 ++++++++++++++++++++ gdbus/tests/gdbus-peer.c | 746 ++++++++++++++++++++ gdbus/tests/gdbus-proxy.c | 455 ++++++++++++ gdbus/tests/gdbus-serialization.c | 650 +++++++++++++++++ gdbus/tests/gdbus-sessionbus.c | 342 +++++++++ gdbus/tests/gdbus-sessionbus.h | 38 + gdbus/tests/gdbus-tests.c | 218 ++++++ gdbus/tests/gdbus-tests.h | 146 ++++ gdbus/tests/gdbus-threading.c | 532 ++++++++++++++ gdbus/tests/introspection.c | 169 ----- gdbus/tests/names.c | 749 -------------------- gdbus/tests/peer.c | 746 -------------------- gdbus/tests/proxy.c | 455 ------------ gdbus/tests/serialization.c | 650 ----------------- gdbus/tests/sessionbus.c | 342 --------- gdbus/tests/sessionbus.h | 38 - gdbus/tests/tests.c | 218 ------ gdbus/tests/tests.h | 146 ---- gdbus/tests/threading.c | 532 -------------- 31 files changed, 6503 insertions(+), 6503 deletions(-) delete mode 100644 gdbus/tests/addresses.c delete mode 100644 gdbus/tests/connection.c delete mode 100644 gdbus/tests/error.c delete mode 100644 gdbus/tests/exit-on-close.c delete mode 100644 gdbus/tests/export.c create mode 100644 gdbus/tests/gdbus-addresses.c create mode 100644 gdbus/tests/gdbus-connection.c create mode 100644 gdbus/tests/gdbus-error.c create mode 100644 gdbus/tests/gdbus-exit-on-close.c create mode 100644 gdbus/tests/gdbus-export.c create mode 100644 gdbus/tests/gdbus-introspection.c create mode 100644 gdbus/tests/gdbus-names.c create mode 100644 gdbus/tests/gdbus-peer.c create mode 100644 gdbus/tests/gdbus-proxy.c create mode 100644 gdbus/tests/gdbus-serialization.c create mode 100644 gdbus/tests/gdbus-sessionbus.c create mode 100644 gdbus/tests/gdbus-sessionbus.h create mode 100644 gdbus/tests/gdbus-tests.c create mode 100644 gdbus/tests/gdbus-tests.h create mode 100644 gdbus/tests/gdbus-threading.c delete mode 100644 gdbus/tests/introspection.c delete mode 100644 gdbus/tests/names.c delete mode 100644 gdbus/tests/peer.c delete mode 100644 gdbus/tests/proxy.c delete mode 100644 gdbus/tests/serialization.c delete mode 100644 gdbus/tests/sessionbus.c delete mode 100644 gdbus/tests/sessionbus.h delete mode 100644 gdbus/tests/tests.c delete mode 100644 gdbus/tests/tests.h delete mode 100644 gdbus/tests/threading.c diff --git a/gdbus/tests/Makefile.am b/gdbus/tests/Makefile.am index 0b2a755..adf387a 100644 --- a/gdbus/tests/Makefile.am +++ b/gdbus/tests/Makefile.am @@ -22,53 +22,53 @@ progs_ldadd = \ $(top_builddir)/gdbus/libgdbus-standalone.la \ $(NULL) -TEST_PROGS += addresses -addresses_SOURCES = addresses.c -addresses_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-addresses +gdbus_addresses_SOURCES = gdbus-addresses.c +gdbus_addresses_LDADD = $(progs_ldadd) if HAVE_DBUS1 -TEST_PROGS += serialization -serialization_SOURCES = serialization.c tests.h tests.c -serialization_CFLAGS = $(DBUS1_CFLAGS) -serialization_LDADD = $(progs_ldadd) $(DBUS1_LIBS) +TEST_PROGS += gdbus-serialization +gdbus_serialization_SOURCES = gdbus-serialization.c gdbus-tests.h gdbus-tests.c +gdbus_serialization_CFLAGS = $(DBUS1_CFLAGS) +gdbus_serialization_LDADD = $(progs_ldadd) $(DBUS1_LIBS) endif -TEST_PROGS += connection -connection_SOURCES = connection.c sessionbus.c sessionbus.h tests.h tests.c -connection_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-connection +gdbus_connection_SOURCES = gdbus-connection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_connection_LDADD = $(progs_ldadd) -TEST_PROGS += names -names_SOURCES = names.c sessionbus.c sessionbus.h tests.h tests.c -names_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-names +gdbus_names_SOURCES = gdbus-names.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_names_LDADD = $(progs_ldadd) -TEST_PROGS += proxy -proxy_SOURCES = proxy.c sessionbus.c sessionbus.h tests.h tests.c -proxy_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-proxy +gdbus_proxy_SOURCES = gdbus-proxy.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_proxy_LDADD = $(progs_ldadd) -TEST_PROGS += introspection -introspection_SOURCES = introspection.c sessionbus.c sessionbus.h tests.h tests.c -introspection_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-introspection +gdbus_introspection_SOURCES = gdbus-introspection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_introspection_LDADD = $(progs_ldadd) -TEST_PROGS += threading -threading_SOURCES = threading.c sessionbus.c sessionbus.h tests.h tests.c -threading_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-threading +gdbus_threading_SOURCES = gdbus-threading.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_threading_LDADD = $(progs_ldadd) -TEST_PROGS += export -export_SOURCES = export.c sessionbus.c sessionbus.h tests.h tests.c -export_CFLAGS = $(DBUS1_CFLAGS) -export_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-export +gdbus_export_SOURCES = gdbus-export.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_export_CFLAGS = $(DBUS1_CFLAGS) +gdbus_export_LDADD = $(progs_ldadd) -TEST_PROGS += error -error_SOURCES = error.c sessionbus.c sessionbus.h tests.h tests.c -error_CFLAGS = $(DBUS1_CFLAGS) -error_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-error +gdbus_error_SOURCES = gdbus-error.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_error_CFLAGS = $(DBUS1_CFLAGS) +gdbus_error_LDADD = $(progs_ldadd) -TEST_PROGS += peer -peer_SOURCES = peer.c sessionbus.c sessionbus.h tests.h tests.c -peer_CFLAGS = $(DBUS1_CFLAGS) -peer_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-peer +gdbus_peer_SOURCES = gdbus-peer.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_peer_CFLAGS = $(DBUS1_CFLAGS) +gdbus_peer_LDADD = $(progs_ldadd) -TEST_PROGS += exit-on-close -exit_on_close_SOURCES = exit-on-close.c sessionbus.c sessionbus.h tests.h tests.c -exit_on_close_CFLAGS = $(DBUS1_CFLAGS) -exit_on_close_LDADD = $(progs_ldadd) +TEST_PROGS += gdbus-exit-on-close +gdbus_exit_on_close_SOURCES = gdbus-exit-on-close.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_exit_on_close_CFLAGS = $(DBUS1_CFLAGS) +gdbus_exit_on_close_LDADD = $(progs_ldadd) diff --git a/gdbus/tests/addresses.c b/gdbus/tests/addresses.c deleted file mode 100644 index ae123d9..0000000 --- a/gdbus/tests/addresses.c +++ /dev/null @@ -1,77 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include - -#ifdef G_OS_UNIX -#include -#endif - -/* ---------------------------------------------------------------------------------------------------- */ - -#ifdef G_OS_UNIX -static void -test_unix_address (void) -{ - g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar", NULL)); - g_assert (g_dbus_is_supported_address ("unix:path=/tmp/dbus-test", NULL)); - g_assert (g_dbus_is_supported_address ("unix:abstract=/tmp/dbus-another-test", NULL)); - g_assert (g_dbus_is_address ("unix:foo=bar")); - g_assert (!g_dbus_is_supported_address ("unix:foo=bar", NULL)); - g_assert (!g_dbus_is_address ("unix:path=/foo;abstract=/bar")); - g_assert (!g_dbus_is_supported_address ("unix:path=/foo;abstract=/bar", NULL)); - g_assert (g_dbus_is_supported_address ("unix:path=/tmp/concrete;unix:abstract=/tmp/abstract", NULL)); - g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar")); - - g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid")); - g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid", NULL)); -} -#endif - -static void -test_nonce_tcp_address (void) -{ - g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar", NULL)); - g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv6", NULL)); - g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv4", NULL)); - - g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=blah", NULL)); - g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=420000,noncefile=/foo/bar,family=ipv4", NULL)); - g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=x42,noncefile=/foo/bar,family=ipv4", NULL)); - g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=42x,noncefile=/foo/bar,family=ipv4", NULL)); - g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=420000,noncefile=/foo/bar,family=ipv4", NULL)); -} - -int -main (int argc, - char *argv[]) -{ - g_type_init (); - g_test_init (&argc, &argv, NULL); - -#ifdef G_OS_UNIX - g_test_add_func ("/gdbus/unix-address", test_unix_address); -#endif - g_test_add_func ("/gdbus/nonce-tcp-address", test_nonce_tcp_address); - return g_test_run(); -} - diff --git a/gdbus/tests/connection.c b/gdbus/tests/connection.c deleted file mode 100644 index be8b061..0000000 --- a/gdbus/tests/connection.c +++ /dev/null @@ -1,653 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -#include "tests.h" - -/* all tests rely on a shared mainloop */ -static GMainLoop *loop = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ -/* Connection life-cycle testing */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -test_connection_life_cycle (void) -{ - GDBusConnection *c; - GDBusConnection *c2; - GError *error; - - error = NULL; - - /* - * Check for correct behavior when no bus is present - * - */ - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - _g_assert_error_domain (error, G_IO_ERROR); - g_assert (!g_dbus_error_is_remote_error (error)); - g_assert (c == NULL); - g_error_free (error); - error = NULL; - - /* - * Check for correct behavior when a bus is present - */ - session_bus_up (); - /* case 1 */ - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (c != NULL); - g_assert (!g_dbus_connection_is_closed (c)); - - /* - * Check that singleton handling work - */ - c2 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (c2 != NULL); - g_assert (c == c2); - g_object_unref (c2); - - /* - * Check that private connections work - */ - c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (c2 != NULL); - g_assert (c != c2); - g_object_unref (c2); - - c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (c2 != NULL); - g_assert (!g_dbus_connection_is_closed (c2)); - g_dbus_connection_close (c2); - _g_assert_signal_received (c2, "closed"); - g_assert (g_dbus_connection_is_closed (c2)); - g_object_unref (c2); - - /* - * Check for correct behavior when the bus goes away - * - */ - g_assert (!g_dbus_connection_is_closed (c)); - g_dbus_connection_set_exit_on_close (c, FALSE); - session_bus_down (); - if (!g_dbus_connection_is_closed (c)) - _g_assert_signal_received (c, "closed"); - g_assert (g_dbus_connection_is_closed (c)); - - _g_object_wait_for_single_ref (c); - g_object_unref (c); -} - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that sending and receiving messages work as expected */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -msg_cb_expect_error_disconnected (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); - g_assert (!g_dbus_error_is_remote_error (error)); - g_error_free (error); - g_assert (result == NULL); - - g_main_loop_quit (loop); -} - -static void -msg_cb_expect_error_unknown_method (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); - g_assert (g_dbus_error_is_remote_error (error)); - g_assert (result == NULL); - - g_main_loop_quit (loop); -} - -static void -msg_cb_expect_success (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_unref (result); - - g_main_loop_quit (loop); -} - -static void -msg_cb_expect_error_cancelled (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); - g_assert (!g_dbus_error_is_remote_error (error)); - g_error_free (error); - g_assert (result == NULL); - - g_main_loop_quit (loop); -} - -static void -msg_cb_expect_error_cancelled_2 (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); - g_assert (!g_dbus_error_is_remote_error (error)); - g_error_free (error); - g_assert (result == NULL); - - g_main_loop_quit (loop); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -test_connection_send (void) -{ - GDBusConnection *c; - GCancellable *ca; - - session_bus_up (); - - /* First, get an unopened connection */ - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c != NULL); - g_assert (!g_dbus_connection_is_closed (c)); - - /* - * Check that we never actually send a message if the GCancellable - * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED - * when the actual connection is not up. - */ - ca = g_cancellable_new (); - g_cancellable_cancel (ca); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled, - NULL); - g_main_loop_run (loop); - g_object_unref (ca); - - /* - * Check that we get a reply to the GetId() method call. - */ - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_success, - NULL); - g_main_loop_run (loop); - - /* - * Check that we get an error reply to the NonExistantMethod() method call. - */ - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "NonExistantMethod", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_error_unknown_method, - NULL); - g_main_loop_run (loop); - - /* - * Check that cancellation works when the message is already in flight. - */ - ca = g_cancellable_new (); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2, - NULL); - g_cancellable_cancel (ca); - g_main_loop_run (loop); - g_object_unref (ca); - - /* - * Check that we get an error when sending to a connection that is disconnected. - */ - g_dbus_connection_set_exit_on_close (c, FALSE); - session_bus_down (); - _g_assert_signal_received (c, "closed"); - g_assert (g_dbus_connection_is_closed (c)); - - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_error_disconnected, - NULL); - g_main_loop_run (loop); - - _g_object_wait_for_single_ref (c); - g_object_unref (c); -} - -/* ---------------------------------------------------------------------------------------------------- */ -/* Connection signal tests */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -test_connection_signal_handler (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - gint *counter = user_data; - *counter += 1; - - /*g_debug ("in test_connection_signal_handler (sender=%s path=%s interface=%s member=%s)", - sender_name, - object_path, - interface_name, - signal_name);*/ - - g_main_loop_quit (loop); -} - -static gboolean -test_connection_signal_quit_mainloop (gpointer user_data) -{ - gboolean *quit_mainloop_fired = user_data; - *quit_mainloop_fired = TRUE; - g_main_loop_quit (loop); - return TRUE; -} - -static void -test_connection_signals (void) -{ - GDBusConnection *c1; - GDBusConnection *c2; - GDBusConnection *c3; - guint s1; - guint s2; - guint s3; - gint count_s1; - gint count_s2; - gint count_name_owner_changed; - GError *error; - gboolean ret; - GVariant *result; - - error = NULL; - - /* - * Bring up first separate connections - */ - session_bus_up (); - /* if running with dbus-monitor, it claims the name :1.0 - so if we don't run with the monitor - * emulate this - */ - if (g_getenv ("G_DBUS_MONITOR") == NULL) - { - c1 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c1 != NULL); - g_assert (!g_dbus_connection_is_closed (c1)); - g_object_unref (c1); - } - c1 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c1 != NULL); - g_assert (!g_dbus_connection_is_closed (c1)); - g_assert_cmpstr (g_dbus_connection_get_unique_name (c1), ==, ":1.1"); - - /* - * Install two signal handlers for the first connection - * - * - Listen to the signal "Foo" from :1.2 (e.g. c2) - * - Listen to the signal "Foo" from anyone (e.g. both c2 and c3) - * - * and then count how many times this signal handler was invoked. - */ - s1 = g_dbus_connection_signal_subscribe (c1, - ":1.2", - "org.gtk.GDBus.ExampleInterface", - "Foo", - "/org/gtk/GDBus/ExampleInterface", - NULL, - test_connection_signal_handler, - &count_s1, - NULL); - s2 = g_dbus_connection_signal_subscribe (c1, - NULL, /* match any sender */ - "org.gtk.GDBus.ExampleInterface", - "Foo", - "/org/gtk/GDBus/ExampleInterface", - NULL, - test_connection_signal_handler, - &count_s2, - NULL); - s3 = g_dbus_connection_signal_subscribe (c1, - "org.freedesktop.DBus", /* sender */ - "org.freedesktop.DBus", /* interface */ - "NameOwnerChanged", /* member */ - "/org/freedesktop/DBus", /* path */ - NULL, - test_connection_signal_handler, - &count_name_owner_changed, - NULL); - g_assert (s1 != 0); - g_assert (s2 != 0); - g_assert (s3 != 0); - - count_s1 = 0; - count_s2 = 0; - count_name_owner_changed = 0; - - /* - * Bring up two other connections - */ - c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c2 != NULL); - g_assert (!g_dbus_connection_is_closed (c2)); - g_assert_cmpstr (g_dbus_connection_get_unique_name (c2), ==, ":1.2"); - c3 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c3 != NULL); - g_assert (!g_dbus_connection_is_closed (c3)); - g_assert_cmpstr (g_dbus_connection_get_unique_name (c3), ==, ":1.3"); - - /* - * Make c2 emit "Foo" - we should catch it twice - * - * Note that there is no way to be sure that the signal subscriptions - * on c1 are effective yet - for all we know, the AddMatch() messages - * could sit waiting in a buffer somewhere between this process and - * the message bus. And emitting signals on c2 (a completely other - * socket!) will not necessarily change this. - * - * To ensure this is not the case, do a synchronous call on c1. - */ - result = g_dbus_connection_invoke_method_sync (c1, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, /* parameters */ - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_unref (result); - /* now, emit the signal on c2 */ - ret = g_dbus_connection_emit_signal (c2, - NULL, /* destination bus name */ - "/org/gtk/GDBus/ExampleInterface", - "org.gtk.GDBus.ExampleInterface", - "Foo", - NULL, - &error); - g_assert_no_error (error); - g_assert (ret); - while (!(count_s1 == 1 && count_s2 == 1)) - g_main_loop_run (loop); - g_assert_cmpint (count_s1, ==, 1); - g_assert_cmpint (count_s2, ==, 1); - - /* - * Make c3 emit "Foo" - we should catch it only once - */ - ret = g_dbus_connection_emit_signal (c3, - NULL, /* destination bus name */ - "/org/gtk/GDBus/ExampleInterface", - "org.gtk.GDBus.ExampleInterface", - "Foo", - NULL, - &error); - g_assert_no_error (error); - g_assert (ret); - while (!(count_s1 == 1 && count_s2 == 2)) - g_main_loop_run (loop); - g_assert_cmpint (count_s1, ==, 1); - g_assert_cmpint (count_s2, ==, 2); - - /* - * Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling - * to avoid spinning forever - */ - gboolean quit_mainloop_fired; - guint quit_mainloop_id; - quit_mainloop_fired = FALSE; - quit_mainloop_id = g_timeout_add (5000, test_connection_signal_quit_mainloop, &quit_mainloop_fired); - while (count_name_owner_changed != 2 && !quit_mainloop_fired) - g_main_loop_run (loop); - g_source_remove (quit_mainloop_id); - g_assert_cmpint (count_s1, ==, 1); - g_assert_cmpint (count_s2, ==, 2); - g_assert_cmpint (count_name_owner_changed, ==, 2); - - g_dbus_connection_signal_unsubscribe (c1, s1); - g_dbus_connection_signal_unsubscribe (c1, s2); - g_dbus_connection_signal_unsubscribe (c1, s3); - - _g_object_wait_for_single_ref (c1); - _g_object_wait_for_single_ref (c2); - _g_object_wait_for_single_ref (c3); - - g_object_unref (c1); - g_object_unref (c2); - g_object_unref (c3); - - session_bus_down (); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - guint num_handled; - guint32 serial; -} FilterData; - -static gboolean -filter_func (GDBusConnection *connection, - GDBusMessage *message, - gpointer user_data) -{ - FilterData *data = user_data; - guint32 reply_serial; - - reply_serial = g_dbus_message_get_reply_serial (message); - if (reply_serial == data->serial) - data->num_handled += 1; - - return FALSE; -} - -static void -test_connection_filter (void) -{ - GDBusConnection *c; - FilterData data; - GDBusMessage *m; - GDBusMessage *r; - GError *error; - guint filter_id; - - memset (&data, '\0', sizeof (FilterData)); - - session_bus_up (); - - error = NULL; - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (c != NULL); - - filter_id = g_dbus_connection_add_filter (c, - filter_func, - &data, - NULL); - - m = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ - "/org/freedesktop/DBus", /* path */ - "org.freedesktop.DBus", /* interface */ - "GetNameOwner"); - g_dbus_message_set_body (m, g_variant_new ("(s)", "org.freedesktop.DBus")); - error = NULL; - g_dbus_connection_send_message (c, m, &data.serial, &error); - g_assert_no_error (error); - - while (data.num_handled == 0) - g_thread_yield (); - - g_dbus_connection_send_message (c, m, &data.serial, &error); - g_assert_no_error (error); - - while (data.num_handled == 1) - g_thread_yield (); - - r = g_dbus_connection_send_message_with_reply_sync (c, - m, - -1, - &data.serial, - NULL, /* GCancellable */ - &error); - g_assert_no_error (error); - g_assert (r != NULL); - g_object_unref (r); - g_assert_cmpint (data.num_handled, ==, 3); - - g_dbus_connection_remove_filter (c, filter_id); - - r = g_dbus_connection_send_message_with_reply_sync (c, - m, - -1, - &data.serial, - NULL, /* GCancellable */ - &error); - g_assert_no_error (error); - g_assert (r != NULL); - g_object_unref (r); - g_assert_cmpint (data.num_handled, ==, 3); - - _g_object_wait_for_single_ref (c); - g_object_unref (c); - g_object_unref (m); - - session_bus_down (); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - g_type_init (); - g_test_init (&argc, &argv, NULL); - - /* all the tests rely on a shared main loop */ - loop = g_main_loop_new (NULL, FALSE); - - /* all the tests use a session bus with a well-known address that we can bring up and down - * using session_bus_up() and session_bus_down(). - */ - g_unsetenv ("DISPLAY"); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); - - g_test_add_func ("/gdbus/connection-life-cycle", test_connection_life_cycle); - g_test_add_func ("/gdbus/connection-send", test_connection_send); - g_test_add_func ("/gdbus/connection-signals", test_connection_signals); - g_test_add_func ("/gdbus/connection-filter", test_connection_filter); - return g_test_run(); -} diff --git a/gdbus/tests/error.c b/gdbus/tests/error.c deleted file mode 100644 index d2f8ea2..0000000 --- a/gdbus/tests/error.c +++ /dev/null @@ -1,198 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that registered errors are properly mapped */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -check_registered_error (const gchar *given_dbus_error_name, - GQuark error_domain, - gint error_code) -{ - GError *error; - gchar *dbus_error_name; - - error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); - g_assert_error (error, error_domain, error_code); - g_assert (g_dbus_error_is_remote_error (error)); - g_assert (g_dbus_error_strip_remote_error (error)); - g_assert_cmpstr (error->message, ==, "test message"); - dbus_error_name = g_dbus_error_get_remote_error (error); - g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); - g_free (dbus_error_name); - g_error_free (error); -} - -static void -test_registered_errors (void) -{ - /* Here we check that we are able to map to GError and back for registered - * errors. - * - * For example, if "org.freedesktop.DBus.Error.AddressInUse" is - * associated with (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED), check - * that - * - * - Creating a GError for e.g. "org.freedesktop.DBus.Error.AddressInUse" - * has (error_domain, code) == (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED) - * - * - That it is possible to recover e.g. "org.freedesktop.DBus.Error.AddressInUse" - * as the D-Bus error name when dealing with an error with (error_domain, code) == - * (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED) - * - * We just check a couple of well-known errors. - */ - check_registered_error ("org.freedesktop.DBus.Error.Failed", - G_DBUS_ERROR, - G_DBUS_ERROR_FAILED); - check_registered_error ("org.freedesktop.DBus.Error.AddressInUse", - G_DBUS_ERROR, - G_DBUS_ERROR_ADDRESS_IN_USE); - check_registered_error ("org.freedesktop.DBus.Error.UnknownMethod", - G_DBUS_ERROR, - G_DBUS_ERROR_UNKNOWN_METHOD); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -check_unregistered_error (const gchar *given_dbus_error_name) -{ - GError *error; - gchar *dbus_error_name; - - error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); - g_assert (g_dbus_error_is_remote_error (error)); - dbus_error_name = g_dbus_error_get_remote_error (error); - g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); - g_free (dbus_error_name); - - /* strip the message */ - g_assert (g_dbus_error_strip_remote_error (error)); - g_assert_cmpstr (error->message, ==, "test message"); - - /* check that we can no longer recover the D-Bus error name */ - g_assert (g_dbus_error_get_remote_error (error) == NULL); - - g_error_free (error); - -} - -static void -test_unregistered_errors (void) -{ - /* Here we check that we are able to map to GError and back for unregistered - * errors. - * - * For example, if "com.example.Error.Failed" is not registered, then check - * - * - Creating a GError for e.g. "com.example.Error.Failed" has (error_domain, code) == - * (G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) - * - * - That it is possible to recover e.g. "com.example.Error.Failed" from that - * GError. - * - * We just check a couple of random errors. - */ - - check_unregistered_error ("com.example.Error.Failed"); - check_unregistered_error ("foobar.buh"); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -check_transparent_gerror (GQuark error_domain, - gint error_code) -{ - GError *error; - gchar *given_dbus_error_name; - gchar *dbus_error_name; - - error = g_error_new (error_domain, error_code, "test message"); - given_dbus_error_name = g_dbus_error_encode_gerror (error); - g_assert (g_str_has_prefix (given_dbus_error_name, "org.gtk.GDBus.UnmappedGError.Quark")); - g_error_free (error); - - error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); - g_assert_error (error, error_domain, error_code); - g_assert (g_dbus_error_is_remote_error (error)); - dbus_error_name = g_dbus_error_get_remote_error (error); - g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); - g_free (dbus_error_name); - g_free (given_dbus_error_name); - - /* strip the message */ - g_assert (g_dbus_error_strip_remote_error (error)); - g_assert_cmpstr (error->message, ==, "test message"); - - /* check that we can no longer recover the D-Bus error name */ - g_assert (g_dbus_error_get_remote_error (error) == NULL); - - g_error_free (error); -} - -static void -test_transparent_gerror (void) -{ - /* Here we check that we are able to transparent pass unregistered GError's - * over the wire. - * - * For example, if G_IO_ERROR_FAILED is not registered, then check - * - * - g_dbus_error_encode_gerror() returns something of the form - * org.gtk.GDBus.UnmappedGError.Quark_HEXENCODED_QUARK_NAME_.Code_ERROR_CODE - * - * - mapping back the D-Bus error name gives us G_IO_ERROR_FAILED - * - * - That it is possible to recover the D-Bus error name from the - * GError. - * - * We just check a couple of random errors. - */ - - check_transparent_gerror (G_IO_ERROR, G_IO_ERROR_FAILED); - check_transparent_gerror (G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); -} - - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - g_type_init (); - g_test_init (&argc, &argv, NULL); - - g_test_add_func ("/gdbus/registered-errors", test_registered_errors); - g_test_add_func ("/gdbus/unregistered-errors", test_unregistered_errors); - g_test_add_func ("/gdbus/transparent-gerror", test_transparent_gerror); - - return g_test_run(); -} diff --git a/gdbus/tests/exit-on-close.c b/gdbus/tests/exit-on-close.c deleted file mode 100644 index 6a3a914..0000000 --- a/gdbus/tests/exit-on-close.c +++ /dev/null @@ -1,82 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -#include "tests.h" - -/* all tests rely on a shared mainloop */ -static GMainLoop *loop = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -nuke_session_bus_cb (gpointer data) -{ - g_main_loop_quit (loop); - return FALSE; -} - -static void -test_exit_on_close (void) -{ - if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) - { - GDBusConnection *c; - session_bus_up (); - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c != NULL); - g_assert (!g_dbus_connection_is_closed (c)); - g_timeout_add (50, - nuke_session_bus_cb, - NULL); - g_main_loop_run (loop); - session_bus_down (); - g_main_loop_run (loop); - } - g_test_trap_assert_stdout ("*Remote peer vanished. Exiting.*"); - g_test_trap_assert_failed(); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - g_type_init (); - g_test_init (&argc, &argv, NULL); - - /* all the tests rely on a shared main loop */ - loop = g_main_loop_new (NULL, FALSE); - - /* all the tests use a session bus with a well-known address that we can bring up and down - * using session_bus_up() and session_bus_down(). - */ - g_unsetenv ("DISPLAY"); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); - - g_test_add_func ("/gdbus/exit-on-close", test_exit_on_close); - return g_test_run(); -} diff --git a/gdbus/tests/export.c b/gdbus/tests/export.c deleted file mode 100644 index 61393c7..0000000 --- a/gdbus/tests/export.c +++ /dev/null @@ -1,1410 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -#include "tests.h" - -/* all tests rely on a shared mainloop */ -static GMainLoop *loop = NULL; - -static GDBusConnection *c = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that we can export objects, the hierarchy is correct and the right handlers are invoked */ -/* ---------------------------------------------------------------------------------------------------- */ - -static const GDBusArgInfo foo_method1_in_args = -{ - -1, - "an_input_string", - "s", - NULL -}; -static const GDBusArgInfo * const foo_method1_in_arg_pointers[] = {&foo_method1_in_args, NULL}; - -static const GDBusArgInfo foo_method1_out_args = -{ - -1, - "an_output_string", - "s", - NULL -}; -static const GDBusArgInfo * const foo_method1_out_arg_pointers[] = {&foo_method1_out_args, NULL}; - -static const GDBusMethodInfo foo_method_info_method1 = -{ - -1, - "Method1", - (GDBusArgInfo **) &foo_method1_in_arg_pointers, - (GDBusArgInfo **) &foo_method1_out_arg_pointers, - NULL -}; -static const GDBusMethodInfo foo_method_info_method2 = -{ - -1, - "Method2", - NULL, - NULL, - NULL -}; -static const GDBusMethodInfo * const foo_method_info_pointers[] = {&foo_method_info_method1, &foo_method_info_method2, NULL}; - -static const GDBusSignalInfo foo_signal_info = -{ - -1, - "SignalAlpha", - NULL, - NULL -}; -static const GDBusSignalInfo * const foo_signal_info_pointers[] = {&foo_signal_info, NULL}; - -static const GDBusPropertyInfo foo_property_info[] = -{ - { - -1, - "PropertyUno", - "s", - G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, - NULL - }, - { - -1, - "NotWritable", - "s", - G_DBUS_PROPERTY_INFO_FLAGS_READABLE, - NULL - }, - { - -1, - "NotReadable", - "s", - G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, - NULL - } -}; -static const GDBusPropertyInfo * const foo_property_info_pointers[] = -{ - &foo_property_info[0], - &foo_property_info[1], - &foo_property_info[2], - NULL -}; - -static const GDBusInterfaceInfo foo_interface_info = -{ - -1, - "org.example.Foo", - (GDBusMethodInfo **) &foo_method_info_pointers, - (GDBusSignalInfo **) &foo_signal_info_pointers, - (GDBusPropertyInfo **)&foo_property_info_pointers, - NULL, -}; - -static void -foo_method_call (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - if (g_strcmp0 (method_name, "Method1") == 0) - { - const gchar *input; - gchar *output; - g_variant_get (parameters, "(s)", &input); - output = g_strdup_printf ("You passed the string `%s'. Jolly good!", input); - g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", output)); - g_free (output); - } - else - { - g_dbus_method_invocation_return_dbus_error (invocation, - "org.example.SomeError", - "How do you like them apples, buddy!"); - } -} - -static GVariant * -foo_get_property (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *property_name, - GError **error, - gpointer user_data) -{ - GVariant *ret; - gchar *s; - s = g_strdup_printf ("Property `%s' Is What It Is!", property_name); - ret = g_variant_new_string (s); - g_free (s); - return ret; -} - -static gboolean -foo_set_property (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *property_name, - GVariant *value, - GError **error, - gpointer user_data) -{ - gchar *s; - s = g_variant_print (value, TRUE); - g_set_error (error, - G_DBUS_ERROR, - G_DBUS_ERROR_SPAWN_FILE_INVALID, - "Returning some error instead of writing the value `%s' to the property `%s'", - property_name, s); - g_free (s); - return FALSE; -} - -static const GDBusInterfaceVTable foo_vtable = -{ - foo_method_call, - foo_get_property, - foo_set_property -}; - -/* -------------------- */ - -static const GDBusMethodInfo bar_method_info[] = -{ - { - -1, - "MethodA", - NULL, - NULL, - NULL - }, - { - -1, - "MethodB", - NULL, - NULL, - NULL - } -}; -static const GDBusMethodInfo * const bar_method_info_pointers[] = {&bar_method_info[0], &bar_method_info[1], NULL}; - -static const GDBusSignalInfo bar_signal_info[] = -{ - { - -1, - "SignalMars", - NULL, - NULL - } -}; -static const GDBusSignalInfo * const bar_signal_info_pointers[] = {&bar_signal_info[0], NULL}; - -static const GDBusPropertyInfo bar_property_info[] = -{ - { - -1, - "PropertyDuo", - "s", - G_DBUS_PROPERTY_INFO_FLAGS_READABLE, - NULL - } -}; -static const GDBusPropertyInfo * const bar_property_info_pointers[] = {&bar_property_info[0], NULL}; - -static const GDBusInterfaceInfo bar_interface_info = -{ - -1, - "org.example.Bar", - (GDBusMethodInfo **) bar_method_info_pointers, - (GDBusSignalInfo **) bar_signal_info_pointers, - (GDBusPropertyInfo **) bar_property_info_pointers, - NULL, -}; - -/* -------------------- */ - -static const GDBusMethodInfo dyna_method_info[] = -{ - { - -1, - "DynaCyber", - NULL, - NULL, - NULL - } -}; -static const GDBusMethodInfo * const dyna_method_info_pointers[] = {&dyna_method_info[0], NULL}; - -static const GDBusInterfaceInfo dyna_interface_info = -{ - -1, - "org.example.Dyna", - (GDBusMethodInfo **) dyna_method_info_pointers, - NULL, /* no signals */ - NULL, /* no properties */ - NULL, -}; - -static void -dyna_cyber (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - GPtrArray *data = user_data; - gchar *node_name; - guint n; - - node_name = strrchr (object_path, '/') + 1; - - /* Add new node if it is not already known */ - for (n = 0; n < data->len ; n++) - { - if (g_strcmp0 (g_ptr_array_index (data, n), node_name) == 0) - goto out; - } - g_ptr_array_add (data, g_strdup(node_name)); - - out: - g_dbus_method_invocation_return_value (invocation, NULL); -} - -static const GDBusInterfaceVTable dyna_interface_vtable = -{ - dyna_cyber, - NULL, - NULL -}; - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -introspect_callback (GDBusProxy *proxy, - GAsyncResult *res, - gpointer user_data) -{ - const gchar *s; - gchar **xml_data = user_data; - GVariant *result; - GError *error; - - error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, - res, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_get (result, "(s)", &s); - *xml_data = g_strdup (s); - g_variant_unref (result); - - g_main_loop_quit (loop); -} - -static gchar ** -get_nodes_at (GDBusConnection *c, - const gchar *object_path) -{ - GError *error; - GDBusProxy *proxy; - gchar *xml_data; - GPtrArray *p; - GDBusNodeInfo *node_info; - guint n; - - error = NULL; - proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - g_dbus_connection_get_unique_name (c), - object_path, - "org.freedesktop.DBus.Introspectable", - NULL, - &error); - g_assert_no_error (error); - g_assert (proxy != NULL); - - /* do this async to avoid libdbus-1 deadlocks */ - xml_data = NULL; - g_dbus_proxy_invoke_method (proxy, - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) introspect_callback, - &xml_data); - g_main_loop_run (loop); - g_assert (xml_data != NULL); - - node_info = g_dbus_node_info_new_for_xml (xml_data, &error); - g_assert_no_error (error); - g_assert (node_info != NULL); - - p = g_ptr_array_new (); - for (n = 0; node_info->nodes != NULL && node_info->nodes[n] != NULL; n++) - { - const GDBusNodeInfo *sub_node_info = node_info->nodes[n]; - g_ptr_array_add (p, g_strdup (sub_node_info->path)); - } - g_ptr_array_add (p, NULL); - - g_object_unref (proxy); - g_free (xml_data); - g_dbus_node_info_unref (node_info); - - return (gchar **) g_ptr_array_free (p, FALSE); -} - -static gboolean -has_interface (GDBusConnection *c, - const gchar *object_path, - const gchar *interface_name) -{ - GError *error; - GDBusProxy *proxy; - gchar *xml_data; - GDBusNodeInfo *node_info; - gboolean ret; - - error = NULL; - proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - g_dbus_connection_get_unique_name (c), - object_path, - "org.freedesktop.DBus.Introspectable", - NULL, - &error); - g_assert_no_error (error); - g_assert (proxy != NULL); - - /* do this async to avoid libdbus-1 deadlocks */ - xml_data = NULL; - g_dbus_proxy_invoke_method (proxy, - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) introspect_callback, - &xml_data); - g_main_loop_run (loop); - g_assert (xml_data != NULL); - - node_info = g_dbus_node_info_new_for_xml (xml_data, &error); - g_assert_no_error (error); - g_assert (node_info != NULL); - - ret = (g_dbus_node_info_lookup_interface (node_info, interface_name) != NULL); - - g_object_unref (proxy); - g_free (xml_data); - g_dbus_node_info_unref (node_info); - - return ret; -} - -static guint -count_interfaces (GDBusConnection *c, - const gchar *object_path) -{ - GError *error; - GDBusProxy *proxy; - gchar *xml_data; - GDBusNodeInfo *node_info; - guint ret; - - error = NULL; - proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - g_dbus_connection_get_unique_name (c), - object_path, - "org.freedesktop.DBus.Introspectable", - NULL, - &error); - g_assert_no_error (error); - g_assert (proxy != NULL); - - /* do this async to avoid libdbus-1 deadlocks */ - xml_data = NULL; - g_dbus_proxy_invoke_method (proxy, - "Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) introspect_callback, - &xml_data); - g_main_loop_run (loop); - g_assert (xml_data != NULL); - - node_info = g_dbus_node_info_new_for_xml (xml_data, &error); - g_assert_no_error (error); - g_assert (node_info != NULL); - - ret = 0; - while (node_info->interfaces != NULL && node_info->interfaces[ret] != NULL) - ret++; - - g_object_unref (proxy); - g_free (xml_data); - g_dbus_node_info_unref (node_info); - - return ret; -} - -static void -dyna_create_callback (GDBusProxy *proxy, - GAsyncResult *res, - gpointer user_data) -{ - GVariant *result; - GError *error; - - error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, - res, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_unref (result); - - g_main_loop_quit (loop); -} - -/* Dynamically create @object_name under /foo/dyna */ -static void -dyna_create (GDBusConnection *c, - const gchar *object_name) -{ - GError *error; - GDBusProxy *proxy; - gchar *object_path; - - object_path = g_strconcat ("/foo/dyna/", object_name, NULL); - - error = NULL; - proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - g_dbus_connection_get_unique_name (c), - object_path, - "org.example.Dyna", - NULL, - &error); - g_assert_no_error (error); - g_assert (proxy != NULL); - - /* do this async to avoid libdbus-1 deadlocks */ - g_dbus_proxy_invoke_method (proxy, - "DynaCyber", - g_variant_new ("()"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) dyna_create_callback, - NULL); - g_main_loop_run (loop); - - g_assert_no_error (error); - - g_object_unref (proxy); - g_free (object_path); - - return; -} - -typedef struct -{ - guint num_unregistered_calls; - guint num_unregistered_subtree_calls; - guint num_subtree_nodes; -} ObjectRegistrationData; - -static void -on_object_unregistered (gpointer user_data) -{ - ObjectRegistrationData *data = user_data; - - data->num_unregistered_calls++; -} - -static void -on_subtree_unregistered (gpointer user_data) -{ - ObjectRegistrationData *data = user_data; - - data->num_unregistered_subtree_calls++; -} - -static gboolean -_g_strv_has_string (const gchar* const * haystack, - const gchar *needle) -{ - guint n; - - for (n = 0; haystack != NULL && haystack[n] != NULL; n++) - { - if (g_strcmp0 (haystack[n], needle) == 0) - return TRUE; - } - return FALSE; -} - -/* -------------------- */ - -static gchar ** -subtree_enumerate (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - gpointer user_data) -{ - ObjectRegistrationData *data = user_data; - GPtrArray *p; - gchar **nodes; - guint n; - - p = g_ptr_array_new (); - - for (n = 0; n < data->num_subtree_nodes; n++) - { - g_ptr_array_add (p, g_strdup_printf ("vp%d", n)); - g_ptr_array_add (p, g_strdup_printf ("evp%d", n)); - } - g_ptr_array_add (p, NULL); - - nodes = (gchar **) g_ptr_array_free (p, FALSE); - - return nodes; -} - -/* Only allows certain objects, and aborts on unknowns */ -static GPtrArray * -subtree_introspect (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *node, - gpointer user_data) -{ - GPtrArray *interfaces; - - /* VPs implement the Foo interface, EVPs implement the Bar interface. The root - * does not implement any interfaces - */ - interfaces = g_ptr_array_new (); - if (g_str_has_prefix (node, "vp")) - { - g_ptr_array_add (interfaces, (gpointer) &foo_interface_info); - } - else if (g_str_has_prefix (node, "evp")) - { - g_ptr_array_add (interfaces, (gpointer) &bar_interface_info); - } - else if (g_strcmp0 (node, "/") == 0) - { - /* do nothing */ - } - else - { - g_assert_not_reached (); - } - - return interfaces; -} - -static const GDBusInterfaceVTable * -subtree_dispatch (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *node, - gpointer *out_user_data, - gpointer user_data) -{ - if (g_strcmp0 (interface_name, "org.example.Foo") == 0) - return &foo_vtable; - else - return NULL; -} - -static const GDBusSubtreeVTable subtree_vtable = -{ - subtree_enumerate, - subtree_introspect, - subtree_dispatch -}; - -/* -------------------- */ - -static gchar ** -dynamic_subtree_enumerate (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - gpointer user_data) -{ - GPtrArray *data = user_data; - gchar **nodes = g_new (gchar*, data->len + 1); - guint n; - - for (n = 0; n < data->len; n++) - { - nodes[n] = g_strdup (g_ptr_array_index (data, n)); - } - nodes[data->len] = NULL; - - return nodes; -} - -/* Allow all objects to be introspected */ -static GPtrArray * -dynamic_subtree_introspect (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *node, - gpointer user_data) -{ - GPtrArray *interfaces; - - /* All nodes (including the root node) implements the Dyna interface */ - interfaces = g_ptr_array_new (); - g_ptr_array_add (interfaces, (gpointer) &dyna_interface_info); - - return interfaces; -} - -static const GDBusInterfaceVTable * -dynamic_subtree_dispatch (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *node, - gpointer *out_user_data, - gpointer user_data) -{ - *out_user_data = user_data; - return &dyna_interface_vtable; -} - -static const GDBusSubtreeVTable dynamic_subtree_vtable = -{ - dynamic_subtree_enumerate, - dynamic_subtree_introspect, - dynamic_subtree_dispatch -}; - -/* -------------------- */ - -static gpointer -test_dispatch_thread_func (gpointer user_data) -{ - const gchar *object_path = user_data; - GDBusProxy *foo_proxy; - GVariant *value; - GVariant *inner; - GError *error; - gchar *s; - const gchar *value_str; - - foo_proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, - NULL, - g_dbus_connection_get_unique_name (c), - object_path, - "org.example.Foo", - NULL, - &error); - g_assert (foo_proxy != NULL); - - /* generic interfaces */ - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Peer.Ping", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (value != NULL); - g_variant_unref (value); - - /* user methods */ - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "Method1", - g_variant_new ("(s)", "winwinwin"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (value != NULL); - g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)"))); - g_variant_get (value, "(s)", &value_str); - g_assert_cmpstr (value_str, ==, "You passed the string `winwinwin'. Jolly good!"); - g_variant_unref (value); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "Method2", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.example.SomeError: How do you like them apples, buddy!"); - g_error_free (error); - g_assert (value == NULL); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "Method2", - g_variant_new ("(s)", "failfailfail"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Signature of message, `s', does not match expected signature `'"); - g_error_free (error); - g_assert (value == NULL); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "NonExistantMethod", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method `NonExistantMethod'"); - g_error_free (error); - g_assert (value == NULL); - - /* user properties */ - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Get", - g_variant_new ("(ss)", - "org.example.Foo", - "PropertyUno"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (value != NULL); - g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(v)"))); - g_variant_get (value, "(v)", &inner); - g_assert (g_variant_is_of_type (inner, G_VARIANT_TYPE_STRING)); - g_assert_cmpstr (g_variant_get_string (inner, NULL), ==, "Property `PropertyUno' Is What It Is!"); - g_variant_unref (value); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Get", - g_variant_new ("(ss)", - "org.example.Foo", - "ThisDoesntExist"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert (value == NULL); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: No such property `ThisDoesntExist'"); - g_error_free (error); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Get", - g_variant_new ("(ss)", - "org.example.Foo", - "NotReadable"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert (value == NULL); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotReadable' is not readable"); - g_error_free (error); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Set", - g_variant_new ("(ssv)", - "org.example.Foo", - "NotReadable", - g_variant_new_string ("But Writable you are!")), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert (value == NULL); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value `NotReadable' to the property `'But Writable you are!''"); - g_error_free (error); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.Set", - g_variant_new ("(ssv)", - "org.example.Foo", - "NotWritable", - g_variant_new_uint32 (42)), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert (value == NULL); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotWritable' is not writable"); - g_error_free (error); - - error = NULL; - value = g_dbus_proxy_invoke_method_sync (foo_proxy, - "org.freedesktop.DBus.Properties.GetAll", - g_variant_new ("(s)", - "org.example.Foo"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (value != NULL); - g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(a{sv})"))); - s = g_variant_print (value, TRUE); - g_assert_cmpstr (s, ==, "({'PropertyUno': <\"Property `PropertyUno' Is What It Is!\">, 'NotWritable': <\"Property `NotWritable' Is What It Is!\">},)"); - g_free (s); - g_variant_unref (value); - - g_object_unref (foo_proxy); - - g_main_loop_quit (loop); - return NULL; -} - -static void -test_dispatch (const gchar *object_path) -{ - GThread *thread; - GError *error; - - /* run this in a thread to avoid deadlocks */ - error = NULL; - thread = g_thread_create (test_dispatch_thread_func, - (gpointer) object_path, - TRUE, - &error); - g_assert_no_error (error); - g_assert (thread != NULL); - g_main_loop_run (loop); - g_thread_join (thread); -} - -static void -test_object_registration (void) -{ - GError *error; - ObjectRegistrationData data; - GPtrArray *dyna_data; - gchar **nodes; - guint boss_foo_reg_id; - guint boss_bar_reg_id; - guint worker1_foo_reg_id; - guint worker2_bar_reg_id; - guint intern1_foo_reg_id; - guint intern2_bar_reg_id; - guint intern2_foo_reg_id; - guint intern3_bar_reg_id; - guint registration_id; - guint subtree_registration_id; - guint non_subtree_object_path_foo_reg_id; - guint non_subtree_object_path_bar_reg_id; - guint dyna_subtree_registration_id; - guint num_successful_registrations; - - data.num_unregistered_calls = 0; - data.num_unregistered_subtree_calls = 0; - data.num_subtree_nodes = 0; - - num_successful_registrations = 0; - - error = NULL; - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (c != NULL); - - registration_id = g_dbus_connection_register_object (c, - "/foo/boss", - foo_interface_info.name, - &foo_interface_info, - &foo_vtable, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - boss_foo_reg_id = registration_id; - num_successful_registrations++; - - registration_id = g_dbus_connection_register_object (c, - "/foo/boss", - bar_interface_info.name, - &bar_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - boss_bar_reg_id = registration_id; - num_successful_registrations++; - - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/worker1", - foo_interface_info.name, - &foo_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - worker1_foo_reg_id = registration_id; - num_successful_registrations++; - - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/worker2", - bar_interface_info.name, - &bar_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - worker2_bar_reg_id = registration_id; - num_successful_registrations++; - - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/interns/intern1", - foo_interface_info.name, - &foo_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - intern1_foo_reg_id = registration_id; - num_successful_registrations++; - - /* ... and try again at another path */ - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/interns/intern2", - bar_interface_info.name, - &bar_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - intern2_bar_reg_id = registration_id; - num_successful_registrations++; - - /* register at the same path/interface - this should fail */ - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/interns/intern2", - bar_interface_info.name, - &bar_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); - g_assert (!g_dbus_error_is_remote_error (error)); - g_error_free (error); - error = NULL; - g_assert (registration_id == 0); - - /* register at different interface - shouldn't fail */ - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/interns/intern2", - foo_interface_info.name, - &foo_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - intern2_foo_reg_id = registration_id; - num_successful_registrations++; - - /* unregister it via the id */ - g_assert (g_dbus_connection_unregister_object (c, registration_id)); - g_assert_cmpint (data.num_unregistered_calls, ==, 1); - intern2_foo_reg_id = 0; - - /* register it back */ - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/interns/intern2", - foo_interface_info.name, - &foo_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - intern2_foo_reg_id = registration_id; - num_successful_registrations++; - - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/interns/intern3", - bar_interface_info.name, - &bar_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - intern3_bar_reg_id = registration_id; - num_successful_registrations++; - - /* now register a whole subtree at /foo/boss/executives */ - subtree_registration_id = g_dbus_connection_register_subtree (c, - "/foo/boss/executives", - &subtree_vtable, - G_DBUS_SUBTREE_FLAGS_NONE, - &data, - on_subtree_unregistered, - &error); - g_assert_no_error (error); - g_assert (subtree_registration_id > 0); - /* try registering it again.. this should fail */ - registration_id = g_dbus_connection_register_subtree (c, - "/foo/boss/executives", - &subtree_vtable, - G_DBUS_SUBTREE_FLAGS_NONE, - &data, - on_subtree_unregistered, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); - g_assert (!g_dbus_error_is_remote_error (error)); - g_error_free (error); - error = NULL; - g_assert (registration_id == 0); - - /* unregister it, then register it again */ - g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 0); - g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id)); - g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1); - subtree_registration_id = g_dbus_connection_register_subtree (c, - "/foo/boss/executives", - &subtree_vtable, - G_DBUS_SUBTREE_FLAGS_NONE, - &data, - on_subtree_unregistered, - &error); - g_assert_no_error (error); - g_assert (subtree_registration_id > 0); - - /* try to register something under /foo/boss/executives - this should work - * because registered subtrees and registered objects can coexist. - * - * Make the exported object implement *two* interfaces so we can check - * that the right introspection handler is invoked. - */ - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/executives/non_subtree_object", - bar_interface_info.name, - &bar_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - non_subtree_object_path_bar_reg_id = registration_id; - num_successful_registrations++; - registration_id = g_dbus_connection_register_object (c, - "/foo/boss/executives/non_subtree_object", - foo_interface_info.name, - &foo_interface_info, - NULL, - &data, - on_object_unregistered, - &error); - g_assert_no_error (error); - g_assert (registration_id > 0); - non_subtree_object_path_foo_reg_id = registration_id; - num_successful_registrations++; - - /* now register a dynamic subtree, spawning objects as they are called */ - dyna_data = g_ptr_array_new (); - dyna_subtree_registration_id = g_dbus_connection_register_subtree (c, - "/foo/dyna", - &dynamic_subtree_vtable, - G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES, - dyna_data, - (GDestroyNotify)g_ptr_array_unref, - &error); - g_assert_no_error (error); - g_assert (dyna_subtree_registration_id > 0); - - /* First assert that we have no nodes in the dynamic subtree */ - nodes = get_nodes_at (c, "/foo/dyna"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 0); - g_strfreev (nodes); - g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4); - - /* Install three nodes in the dynamic subtree via the dyna_data backdoor and - * assert that they show up correctly in the introspection data */ - g_ptr_array_add (dyna_data, "lol"); - g_ptr_array_add (dyna_data, "cat"); - g_ptr_array_add (dyna_data, "cheezburger"); - nodes = get_nodes_at (c, "/foo/dyna"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 3); - g_assert_cmpstr (nodes[0], ==, "lol"); - g_assert_cmpstr (nodes[1], ==, "cat"); - g_assert_cmpstr (nodes[2], ==, "cheezburger"); - g_strfreev (nodes); - g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4); - g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4); - g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4); - - /* Call a non-existing object path and assert that it has been created */ - dyna_create (c, "dynamicallycreated"); - nodes = get_nodes_at (c, "/foo/dyna"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 4); - g_assert_cmpstr (nodes[0], ==, "lol"); - g_assert_cmpstr (nodes[1], ==, "cat"); - g_assert_cmpstr (nodes[2], ==, "cheezburger"); - g_assert_cmpstr (nodes[3], ==, "dynamicallycreated"); - g_strfreev (nodes); - g_assert_cmpint (count_interfaces (c, "/foo/dyna/dynamicallycreated"), ==, 4); - - /* now check that the object hierarachy is properly generated... yes, it's a bit - * perverse that we round-trip to the bus to introspect ourselves ;-) - */ - nodes = get_nodes_at (c, "/"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 1); - g_assert_cmpstr (nodes[0], ==, "foo"); - g_strfreev (nodes); - g_assert_cmpint (count_interfaces (c, "/"), ==, 0); - - nodes = get_nodes_at (c, "/foo"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 2); - g_assert_cmpstr (nodes[0], ==, "boss"); - g_assert_cmpstr (nodes[1], ==, "dyna"); - g_strfreev (nodes); - g_assert_cmpint (count_interfaces (c, "/foo"), ==, 0); - - nodes = get_nodes_at (c, "/foo/boss"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 4); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker1")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker2")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "interns")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "executives")); - g_strfreev (nodes); - /* any registered object always implement org.freedesktop.DBus.[Peer,Introspectable,Properties] */ - g_assert_cmpint (count_interfaces (c, "/foo/boss"), ==, 5); - g_assert (has_interface (c, "/foo/boss", foo_interface_info.name)); - g_assert (has_interface (c, "/foo/boss", bar_interface_info.name)); - - /* check subtree nodes - we should have only non_subtree_object in /foo/boss/executives - * because data.num_subtree_nodes is 0 - */ - nodes = get_nodes_at (c, "/foo/boss/executives"); - g_assert (nodes != NULL); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); - g_assert_cmpint (g_strv_length (nodes), ==, 1); - g_strfreev (nodes); - g_assert_cmpint (count_interfaces (c, "/foo/boss/executives"), ==, 0); - - /* now change data.num_subtree_nodes and check */ - data.num_subtree_nodes = 2; - nodes = get_nodes_at (c, "/foo/boss/executives"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 5); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1")); - /* check that /foo/boss/executives/non_subtree_object is not handled by the - * subtree handlers - we can do this because objects from subtree handlers - * has exactly one interface and non_subtree_object has two - */ - g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/non_subtree_object"), ==, 5); - g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", foo_interface_info.name)); - g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", bar_interface_info.name)); - /* check that the vp and evp objects are handled by the subtree handlers */ - g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp0"), ==, 4); - g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp1"), ==, 4); - g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp0"), ==, 4); - g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp1"), ==, 4); - g_assert (has_interface (c, "/foo/boss/executives/vp0", foo_interface_info.name)); - g_assert (has_interface (c, "/foo/boss/executives/vp1", foo_interface_info.name)); - g_assert (has_interface (c, "/foo/boss/executives/evp0", bar_interface_info.name)); - g_assert (has_interface (c, "/foo/boss/executives/evp1", bar_interface_info.name)); - g_strfreev (nodes); - data.num_subtree_nodes = 3; - nodes = get_nodes_at (c, "/foo/boss/executives"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 7); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp2")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1")); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp2")); - g_strfreev (nodes); - - /* check that calls are properly dispatched to the functions in foo_vtable for objects - * implementing the org.example.Foo interface - * - * We do this for both a regular registered object (/foo/boss) and also for an object - * registered through the subtree mechanism. - */ - test_dispatch ("/foo/boss"); - test_dispatch ("/foo/boss/executives/vp0"); - - /* To prevent from exiting and attaching a D-Bus tool like D-Feet; uncomment: */ -#if 0 - g_debug ("Point D-feet or other tool at: %s", session_bus_get_temporary_address()); - g_main_loop_run (loop); -#endif - - /* check that unregistering the subtree handler works */ - g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1); - g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id)); - g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 2); - nodes = get_nodes_at (c, "/foo/boss/executives"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 1); - g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); - g_strfreev (nodes); - - g_assert (g_dbus_connection_unregister_object (c, boss_foo_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, boss_bar_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, worker1_foo_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, worker2_bar_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, intern1_foo_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, intern2_bar_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, intern2_foo_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, intern3_bar_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_bar_reg_id)); - g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_foo_reg_id)); - - g_assert_cmpint (data.num_unregistered_calls, ==, num_successful_registrations); - - /* check that we no longer export any objects - TODO: it looks like there's a bug in - * libdbus-1 here: libdbus still reports the '/foo' object; so disable the test for now - */ -#if 0 - nodes = get_nodes_at (c, "/"); - g_assert (nodes != NULL); - g_assert_cmpint (g_strv_length (nodes), ==, 0); - g_strfreev (nodes); -#endif - - g_object_unref (c); -} - - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - gint ret; - - g_type_init (); - g_test_init (&argc, &argv, NULL); - - /* all the tests rely on a shared main loop */ - loop = g_main_loop_new (NULL, FALSE); - - /* all the tests use a session bus with a well-known address that we can bring up and down - * using session_bus_up() and session_bus_down(). - */ - g_unsetenv ("DISPLAY"); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); - - session_bus_up (); - - /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return - * until one can connect to the bus but that's not how things work right now - */ - usleep (500 * 1000); - - g_test_add_func ("/gdbus/object-registration", test_object_registration); - /* TODO: check that we spit out correct introspection data */ - /* TODO: check that registering a whole subtree works */ - - ret = g_test_run(); - - /* tear down bus */ - session_bus_down (); - - return ret; -} diff --git a/gdbus/tests/gdbus-addresses.c b/gdbus/tests/gdbus-addresses.c new file mode 100644 index 0000000..ae123d9 --- /dev/null +++ b/gdbus/tests/gdbus-addresses.c @@ -0,0 +1,77 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include + +#ifdef G_OS_UNIX +#include +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +#ifdef G_OS_UNIX +static void +test_unix_address (void) +{ + g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar", NULL)); + g_assert (g_dbus_is_supported_address ("unix:path=/tmp/dbus-test", NULL)); + g_assert (g_dbus_is_supported_address ("unix:abstract=/tmp/dbus-another-test", NULL)); + g_assert (g_dbus_is_address ("unix:foo=bar")); + g_assert (!g_dbus_is_supported_address ("unix:foo=bar", NULL)); + g_assert (!g_dbus_is_address ("unix:path=/foo;abstract=/bar")); + g_assert (!g_dbus_is_supported_address ("unix:path=/foo;abstract=/bar", NULL)); + g_assert (g_dbus_is_supported_address ("unix:path=/tmp/concrete;unix:abstract=/tmp/abstract", NULL)); + g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar")); + + g_assert (g_dbus_is_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid")); + g_assert (!g_dbus_is_supported_address ("some-imaginary-transport:foo=bar;unix:path=/this/is/valid", NULL)); +} +#endif + +static void +test_nonce_tcp_address (void) +{ + g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar", NULL)); + g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv6", NULL)); + g_assert (g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=ipv4", NULL)); + + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=42,noncefile=/foo/bar,family=blah", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=localhost,port=420000,noncefile=/foo/bar,family=ipv4", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=x42,noncefile=/foo/bar,family=ipv4", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=42x,noncefile=/foo/bar,family=ipv4", NULL)); + g_assert (!g_dbus_is_supported_address ("nonce-tcp:host=,port=420000,noncefile=/foo/bar,family=ipv4", NULL)); +} + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + +#ifdef G_OS_UNIX + g_test_add_func ("/gdbus/unix-address", test_unix_address); +#endif + g_test_add_func ("/gdbus/nonce-tcp-address", test_nonce_tcp_address); + return g_test_run(); +} + diff --git a/gdbus/tests/gdbus-connection.c b/gdbus/tests/gdbus-connection.c new file mode 100644 index 0000000..d64b9cb --- /dev/null +++ b/gdbus/tests/gdbus-connection.c @@ -0,0 +1,653 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Connection life-cycle testing */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_connection_life_cycle (void) +{ + GDBusConnection *c; + GDBusConnection *c2; + GError *error; + + error = NULL; + + /* + * Check for correct behavior when no bus is present + * + */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + _g_assert_error_domain (error, G_IO_ERROR); + g_assert (!g_dbus_error_is_remote_error (error)); + g_assert (c == NULL); + g_error_free (error); + error = NULL; + + /* + * Check for correct behavior when a bus is present + */ + session_bus_up (); + /* case 1 */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + + /* + * Check that singleton handling work + */ + c2 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (c == c2); + g_object_unref (c2); + + /* + * Check that private connections work + */ + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (c != c2); + g_object_unref (c2); + + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_is_closed (c2)); + g_dbus_connection_close (c2); + _g_assert_signal_received (c2, "closed"); + g_assert (g_dbus_connection_is_closed (c2)); + g_object_unref (c2); + + /* + * Check for correct behavior when the bus goes away + * + */ + g_assert (!g_dbus_connection_is_closed (c)); + g_dbus_connection_set_exit_on_close (c, FALSE); + session_bus_down (); + if (!g_dbus_connection_is_closed (c)) + _g_assert_signal_received (c, "closed"); + g_assert (g_dbus_connection_is_closed (c)); + + _g_object_wait_for_single_ref (c); + g_object_unref (c); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that sending and receiving messages work as expected */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +msg_cb_expect_error_disconnected (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_error_unknown_method (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_success (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_error_cancelled (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +static void +msg_cb_expect_error_cancelled_2 (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_main_loop_quit (loop); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_connection_send (void) +{ + GDBusConnection *c; + GCancellable *ca; + + session_bus_up (); + + /* First, get an unopened connection */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + + /* + * Check that we never actually send a message if the GCancellable + * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED + * when the actual connection is not up. + */ + ca = g_cancellable_new (); + g_cancellable_cancel (ca); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + NULL); + g_main_loop_run (loop); + g_object_unref (ca); + + /* + * Check that we get a reply to the GetId() method call. + */ + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_success, + NULL); + g_main_loop_run (loop); + + /* + * Check that we get an error reply to the NonExistantMethod() method call. + */ + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NonExistantMethod", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_error_unknown_method, + NULL); + g_main_loop_run (loop); + + /* + * Check that cancellation works when the message is already in flight. + */ + ca = g_cancellable_new (); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled_2, + NULL); + g_cancellable_cancel (ca); + g_main_loop_run (loop); + g_object_unref (ca); + + /* + * Check that we get an error when sending to a connection that is disconnected. + */ + g_dbus_connection_set_exit_on_close (c, FALSE); + session_bus_down (); + _g_assert_signal_received (c, "closed"); + g_assert (g_dbus_connection_is_closed (c)); + + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_error_disconnected, + NULL); + g_main_loop_run (loop); + + _g_object_wait_for_single_ref (c); + g_object_unref (c); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Connection signal tests */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_connection_signal_handler (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + gint *counter = user_data; + *counter += 1; + + /*g_debug ("in test_connection_signal_handler (sender=%s path=%s interface=%s member=%s)", + sender_name, + object_path, + interface_name, + signal_name);*/ + + g_main_loop_quit (loop); +} + +static gboolean +test_connection_signal_quit_mainloop (gpointer user_data) +{ + gboolean *quit_mainloop_fired = user_data; + *quit_mainloop_fired = TRUE; + g_main_loop_quit (loop); + return TRUE; +} + +static void +test_connection_signals (void) +{ + GDBusConnection *c1; + GDBusConnection *c2; + GDBusConnection *c3; + guint s1; + guint s2; + guint s3; + gint count_s1; + gint count_s2; + gint count_name_owner_changed; + GError *error; + gboolean ret; + GVariant *result; + + error = NULL; + + /* + * Bring up first separate connections + */ + session_bus_up (); + /* if running with dbus-monitor, it claims the name :1.0 - so if we don't run with the monitor + * emulate this + */ + if (g_getenv ("G_DBUS_MONITOR") == NULL) + { + c1 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c1 != NULL); + g_assert (!g_dbus_connection_is_closed (c1)); + g_object_unref (c1); + } + c1 = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c1 != NULL); + g_assert (!g_dbus_connection_is_closed (c1)); + g_assert_cmpstr (g_dbus_connection_get_unique_name (c1), ==, ":1.1"); + + /* + * Install two signal handlers for the first connection + * + * - Listen to the signal "Foo" from :1.2 (e.g. c2) + * - Listen to the signal "Foo" from anyone (e.g. both c2 and c3) + * + * and then count how many times this signal handler was invoked. + */ + s1 = g_dbus_connection_signal_subscribe (c1, + ":1.2", + "org.gtk.GDBus.ExampleInterface", + "Foo", + "/org/gtk/GDBus/ExampleInterface", + NULL, + test_connection_signal_handler, + &count_s1, + NULL); + s2 = g_dbus_connection_signal_subscribe (c1, + NULL, /* match any sender */ + "org.gtk.GDBus.ExampleInterface", + "Foo", + "/org/gtk/GDBus/ExampleInterface", + NULL, + test_connection_signal_handler, + &count_s2, + NULL); + s3 = g_dbus_connection_signal_subscribe (c1, + "org.freedesktop.DBus", /* sender */ + "org.freedesktop.DBus", /* interface */ + "NameOwnerChanged", /* member */ + "/org/freedesktop/DBus", /* path */ + NULL, + test_connection_signal_handler, + &count_name_owner_changed, + NULL); + g_assert (s1 != 0); + g_assert (s2 != 0); + g_assert (s3 != 0); + + count_s1 = 0; + count_s2 = 0; + count_name_owner_changed = 0; + + /* + * Bring up two other connections + */ + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_is_closed (c2)); + g_assert_cmpstr (g_dbus_connection_get_unique_name (c2), ==, ":1.2"); + c3 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c3 != NULL); + g_assert (!g_dbus_connection_is_closed (c3)); + g_assert_cmpstr (g_dbus_connection_get_unique_name (c3), ==, ":1.3"); + + /* + * Make c2 emit "Foo" - we should catch it twice + * + * Note that there is no way to be sure that the signal subscriptions + * on c1 are effective yet - for all we know, the AddMatch() messages + * could sit waiting in a buffer somewhere between this process and + * the message bus. And emitting signals on c2 (a completely other + * socket!) will not necessarily change this. + * + * To ensure this is not the case, do a synchronous call on c1. + */ + result = g_dbus_connection_invoke_method_sync (c1, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, /* parameters */ + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + /* now, emit the signal on c2 */ + ret = g_dbus_connection_emit_signal (c2, + NULL, /* destination bus name */ + "/org/gtk/GDBus/ExampleInterface", + "org.gtk.GDBus.ExampleInterface", + "Foo", + NULL, + &error); + g_assert_no_error (error); + g_assert (ret); + while (!(count_s1 == 1 && count_s2 == 1)) + g_main_loop_run (loop); + g_assert_cmpint (count_s1, ==, 1); + g_assert_cmpint (count_s2, ==, 1); + + /* + * Make c3 emit "Foo" - we should catch it only once + */ + ret = g_dbus_connection_emit_signal (c3, + NULL, /* destination bus name */ + "/org/gtk/GDBus/ExampleInterface", + "org.gtk.GDBus.ExampleInterface", + "Foo", + NULL, + &error); + g_assert_no_error (error); + g_assert (ret); + while (!(count_s1 == 1 && count_s2 == 2)) + g_main_loop_run (loop); + g_assert_cmpint (count_s1, ==, 1); + g_assert_cmpint (count_s2, ==, 2); + + /* + * Also to check the total amount of NameOwnerChanged signals - use a 5 second ceiling + * to avoid spinning forever + */ + gboolean quit_mainloop_fired; + guint quit_mainloop_id; + quit_mainloop_fired = FALSE; + quit_mainloop_id = g_timeout_add (5000, test_connection_signal_quit_mainloop, &quit_mainloop_fired); + while (count_name_owner_changed != 2 && !quit_mainloop_fired) + g_main_loop_run (loop); + g_source_remove (quit_mainloop_id); + g_assert_cmpint (count_s1, ==, 1); + g_assert_cmpint (count_s2, ==, 2); + g_assert_cmpint (count_name_owner_changed, ==, 2); + + g_dbus_connection_signal_unsubscribe (c1, s1); + g_dbus_connection_signal_unsubscribe (c1, s2); + g_dbus_connection_signal_unsubscribe (c1, s3); + + _g_object_wait_for_single_ref (c1); + _g_object_wait_for_single_ref (c2); + _g_object_wait_for_single_ref (c3); + + g_object_unref (c1); + g_object_unref (c2); + g_object_unref (c3); + + session_bus_down (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + guint num_handled; + guint32 serial; +} FilterData; + +static gboolean +filter_func (GDBusConnection *connection, + GDBusMessage *message, + gpointer user_data) +{ + FilterData *data = user_data; + guint32 reply_serial; + + reply_serial = g_dbus_message_get_reply_serial (message); + if (reply_serial == data->serial) + data->num_handled += 1; + + return FALSE; +} + +static void +test_connection_filter (void) +{ + GDBusConnection *c; + FilterData data; + GDBusMessage *m; + GDBusMessage *r; + GError *error; + guint filter_id; + + memset (&data, '\0', sizeof (FilterData)); + + session_bus_up (); + + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + filter_id = g_dbus_connection_add_filter (c, + filter_func, + &data, + NULL); + + m = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "GetNameOwner"); + g_dbus_message_set_body (m, g_variant_new ("(s)", "org.freedesktop.DBus")); + error = NULL; + g_dbus_connection_send_message (c, m, &data.serial, &error); + g_assert_no_error (error); + + while (data.num_handled == 0) + g_thread_yield (); + + g_dbus_connection_send_message (c, m, &data.serial, &error); + g_assert_no_error (error); + + while (data.num_handled == 1) + g_thread_yield (); + + r = g_dbus_connection_send_message_with_reply_sync (c, + m, + -1, + &data.serial, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (r != NULL); + g_object_unref (r); + g_assert_cmpint (data.num_handled, ==, 3); + + g_dbus_connection_remove_filter (c, filter_id); + + r = g_dbus_connection_send_message_with_reply_sync (c, + m, + -1, + &data.serial, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (r != NULL); + g_object_unref (r); + g_assert_cmpint (data.num_handled, ==, 3); + + _g_object_wait_for_single_ref (c); + g_object_unref (c); + g_object_unref (m); + + session_bus_down (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/connection-life-cycle", test_connection_life_cycle); + g_test_add_func ("/gdbus/connection-send", test_connection_send); + g_test_add_func ("/gdbus/connection-signals", test_connection_signals); + g_test_add_func ("/gdbus/connection-filter", test_connection_filter); + return g_test_run(); +} diff --git a/gdbus/tests/gdbus-error.c b/gdbus/tests/gdbus-error.c new file mode 100644 index 0000000..d2f8ea2 --- /dev/null +++ b/gdbus/tests/gdbus-error.c @@ -0,0 +1,198 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that registered errors are properly mapped */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +check_registered_error (const gchar *given_dbus_error_name, + GQuark error_domain, + gint error_code) +{ + GError *error; + gchar *dbus_error_name; + + error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); + g_assert_error (error, error_domain, error_code); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "test message"); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); + g_free (dbus_error_name); + g_error_free (error); +} + +static void +test_registered_errors (void) +{ + /* Here we check that we are able to map to GError and back for registered + * errors. + * + * For example, if "org.freedesktop.DBus.Error.AddressInUse" is + * associated with (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED), check + * that + * + * - Creating a GError for e.g. "org.freedesktop.DBus.Error.AddressInUse" + * has (error_domain, code) == (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED) + * + * - That it is possible to recover e.g. "org.freedesktop.DBus.Error.AddressInUse" + * as the D-Bus error name when dealing with an error with (error_domain, code) == + * (G_DBUS_ERROR, G_DBUS_ERROR_DBUS_FAILED) + * + * We just check a couple of well-known errors. + */ + check_registered_error ("org.freedesktop.DBus.Error.Failed", + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED); + check_registered_error ("org.freedesktop.DBus.Error.AddressInUse", + G_DBUS_ERROR, + G_DBUS_ERROR_ADDRESS_IN_USE); + check_registered_error ("org.freedesktop.DBus.Error.UnknownMethod", + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +check_unregistered_error (const gchar *given_dbus_error_name) +{ + GError *error; + gchar *dbus_error_name; + + error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); + g_assert (g_dbus_error_is_remote_error (error)); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); + g_free (dbus_error_name); + + /* strip the message */ + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "test message"); + + /* check that we can no longer recover the D-Bus error name */ + g_assert (g_dbus_error_get_remote_error (error) == NULL); + + g_error_free (error); + +} + +static void +test_unregistered_errors (void) +{ + /* Here we check that we are able to map to GError and back for unregistered + * errors. + * + * For example, if "com.example.Error.Failed" is not registered, then check + * + * - Creating a GError for e.g. "com.example.Error.Failed" has (error_domain, code) == + * (G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) + * + * - That it is possible to recover e.g. "com.example.Error.Failed" from that + * GError. + * + * We just check a couple of random errors. + */ + + check_unregistered_error ("com.example.Error.Failed"); + check_unregistered_error ("foobar.buh"); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +check_transparent_gerror (GQuark error_domain, + gint error_code) +{ + GError *error; + gchar *given_dbus_error_name; + gchar *dbus_error_name; + + error = g_error_new (error_domain, error_code, "test message"); + given_dbus_error_name = g_dbus_error_encode_gerror (error); + g_assert (g_str_has_prefix (given_dbus_error_name, "org.gtk.GDBus.UnmappedGError.Quark")); + g_error_free (error); + + error = g_dbus_error_new_for_dbus_error (given_dbus_error_name, "test message"); + g_assert_error (error, error_domain, error_code); + g_assert (g_dbus_error_is_remote_error (error)); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, given_dbus_error_name); + g_free (dbus_error_name); + g_free (given_dbus_error_name); + + /* strip the message */ + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "test message"); + + /* check that we can no longer recover the D-Bus error name */ + g_assert (g_dbus_error_get_remote_error (error) == NULL); + + g_error_free (error); +} + +static void +test_transparent_gerror (void) +{ + /* Here we check that we are able to transparent pass unregistered GError's + * over the wire. + * + * For example, if G_IO_ERROR_FAILED is not registered, then check + * + * - g_dbus_error_encode_gerror() returns something of the form + * org.gtk.GDBus.UnmappedGError.Quark_HEXENCODED_QUARK_NAME_.Code_ERROR_CODE + * + * - mapping back the D-Bus error name gives us G_IO_ERROR_FAILED + * + * - That it is possible to recover the D-Bus error name from the + * GError. + * + * We just check a couple of random errors. + */ + + check_transparent_gerror (G_IO_ERROR, G_IO_ERROR_FAILED); + check_transparent_gerror (G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_PARSE); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gdbus/registered-errors", test_registered_errors); + g_test_add_func ("/gdbus/unregistered-errors", test_unregistered_errors); + g_test_add_func ("/gdbus/transparent-gerror", test_transparent_gerror); + + return g_test_run(); +} diff --git a/gdbus/tests/gdbus-exit-on-close.c b/gdbus/tests/gdbus-exit-on-close.c new file mode 100644 index 0000000..81115ed --- /dev/null +++ b/gdbus/tests/gdbus-exit-on-close.c @@ -0,0 +1,82 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +nuke_session_bus_cb (gpointer data) +{ + g_main_loop_quit (loop); + return FALSE; +} + +static void +test_exit_on_close (void) +{ + if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) + { + GDBusConnection *c; + session_bus_up (); + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + g_timeout_add (50, + nuke_session_bus_cb, + NULL); + g_main_loop_run (loop); + session_bus_down (); + g_main_loop_run (loop); + } + g_test_trap_assert_stdout ("*Remote peer vanished. Exiting.*"); + g_test_trap_assert_failed(); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/exit-on-close", test_exit_on_close); + return g_test_run(); +} diff --git a/gdbus/tests/gdbus-export.c b/gdbus/tests/gdbus-export.c new file mode 100644 index 0000000..4298dd9 --- /dev/null +++ b/gdbus/tests/gdbus-export.c @@ -0,0 +1,1410 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +static GDBusConnection *c = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that we can export objects, the hierarchy is correct and the right handlers are invoked */ +/* ---------------------------------------------------------------------------------------------------- */ + +static const GDBusArgInfo foo_method1_in_args = +{ + -1, + "an_input_string", + "s", + NULL +}; +static const GDBusArgInfo * const foo_method1_in_arg_pointers[] = {&foo_method1_in_args, NULL}; + +static const GDBusArgInfo foo_method1_out_args = +{ + -1, + "an_output_string", + "s", + NULL +}; +static const GDBusArgInfo * const foo_method1_out_arg_pointers[] = {&foo_method1_out_args, NULL}; + +static const GDBusMethodInfo foo_method_info_method1 = +{ + -1, + "Method1", + (GDBusArgInfo **) &foo_method1_in_arg_pointers, + (GDBusArgInfo **) &foo_method1_out_arg_pointers, + NULL +}; +static const GDBusMethodInfo foo_method_info_method2 = +{ + -1, + "Method2", + NULL, + NULL, + NULL +}; +static const GDBusMethodInfo * const foo_method_info_pointers[] = {&foo_method_info_method1, &foo_method_info_method2, NULL}; + +static const GDBusSignalInfo foo_signal_info = +{ + -1, + "SignalAlpha", + NULL, + NULL +}; +static const GDBusSignalInfo * const foo_signal_info_pointers[] = {&foo_signal_info, NULL}; + +static const GDBusPropertyInfo foo_property_info[] = +{ + { + -1, + "PropertyUno", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, + NULL + }, + { + -1, + "NotWritable", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + }, + { + -1, + "NotReadable", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE, + NULL + } +}; +static const GDBusPropertyInfo * const foo_property_info_pointers[] = +{ + &foo_property_info[0], + &foo_property_info[1], + &foo_property_info[2], + NULL +}; + +static const GDBusInterfaceInfo foo_interface_info = +{ + -1, + "org.example.Foo", + (GDBusMethodInfo **) &foo_method_info_pointers, + (GDBusSignalInfo **) &foo_signal_info_pointers, + (GDBusPropertyInfo **)&foo_property_info_pointers, + NULL, +}; + +static void +foo_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + if (g_strcmp0 (method_name, "Method1") == 0) + { + const gchar *input; + gchar *output; + g_variant_get (parameters, "(s)", &input); + output = g_strdup_printf ("You passed the string `%s'. Jolly good!", input); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", output)); + g_free (output); + } + else + { + g_dbus_method_invocation_return_dbus_error (invocation, + "org.example.SomeError", + "How do you like them apples, buddy!"); + } +} + +static GVariant * +foo_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GVariant *ret; + gchar *s; + s = g_strdup_printf ("Property `%s' Is What It Is!", property_name); + ret = g_variant_new_string (s); + g_free (s); + return ret; +} + +static gboolean +foo_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + gchar *s; + s = g_variant_print (value, TRUE); + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_SPAWN_FILE_INVALID, + "Returning some error instead of writing the value `%s' to the property `%s'", + property_name, s); + g_free (s); + return FALSE; +} + +static const GDBusInterfaceVTable foo_vtable = +{ + foo_method_call, + foo_get_property, + foo_set_property +}; + +/* -------------------- */ + +static const GDBusMethodInfo bar_method_info[] = +{ + { + -1, + "MethodA", + NULL, + NULL, + NULL + }, + { + -1, + "MethodB", + NULL, + NULL, + NULL + } +}; +static const GDBusMethodInfo * const bar_method_info_pointers[] = {&bar_method_info[0], &bar_method_info[1], NULL}; + +static const GDBusSignalInfo bar_signal_info[] = +{ + { + -1, + "SignalMars", + NULL, + NULL + } +}; +static const GDBusSignalInfo * const bar_signal_info_pointers[] = {&bar_signal_info[0], NULL}; + +static const GDBusPropertyInfo bar_property_info[] = +{ + { + -1, + "PropertyDuo", + "s", + G_DBUS_PROPERTY_INFO_FLAGS_READABLE, + NULL + } +}; +static const GDBusPropertyInfo * const bar_property_info_pointers[] = {&bar_property_info[0], NULL}; + +static const GDBusInterfaceInfo bar_interface_info = +{ + -1, + "org.example.Bar", + (GDBusMethodInfo **) bar_method_info_pointers, + (GDBusSignalInfo **) bar_signal_info_pointers, + (GDBusPropertyInfo **) bar_property_info_pointers, + NULL, +}; + +/* -------------------- */ + +static const GDBusMethodInfo dyna_method_info[] = +{ + { + -1, + "DynaCyber", + NULL, + NULL, + NULL + } +}; +static const GDBusMethodInfo * const dyna_method_info_pointers[] = {&dyna_method_info[0], NULL}; + +static const GDBusInterfaceInfo dyna_interface_info = +{ + -1, + "org.example.Dyna", + (GDBusMethodInfo **) dyna_method_info_pointers, + NULL, /* no signals */ + NULL, /* no properties */ + NULL, +}; + +static void +dyna_cyber (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GPtrArray *data = user_data; + gchar *node_name; + guint n; + + node_name = strrchr (object_path, '/') + 1; + + /* Add new node if it is not already known */ + for (n = 0; n < data->len ; n++) + { + if (g_strcmp0 (g_ptr_array_index (data, n), node_name) == 0) + goto out; + } + g_ptr_array_add (data, g_strdup(node_name)); + + out: + g_dbus_method_invocation_return_value (invocation, NULL); +} + +static const GDBusInterfaceVTable dyna_interface_vtable = +{ + dyna_cyber, + NULL, + NULL +}; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +introspect_callback (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + const gchar *s; + gchar **xml_data = user_data; + GVariant *result; + GError *error; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(s)", &s); + *xml_data = g_strdup (s); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +static gchar ** +get_nodes_at (GDBusConnection *c, + const gchar *object_path) +{ + GError *error; + GDBusProxy *proxy; + gchar *xml_data; + GPtrArray *p; + GDBusNodeInfo *node_info; + guint n; + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.freedesktop.DBus.Introspectable", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + xml_data = NULL; + g_dbus_proxy_invoke_method (proxy, + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); + g_main_loop_run (loop); + g_assert (xml_data != NULL); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + p = g_ptr_array_new (); + for (n = 0; node_info->nodes != NULL && node_info->nodes[n] != NULL; n++) + { + const GDBusNodeInfo *sub_node_info = node_info->nodes[n]; + g_ptr_array_add (p, g_strdup (sub_node_info->path)); + } + g_ptr_array_add (p, NULL); + + g_object_unref (proxy); + g_free (xml_data); + g_dbus_node_info_unref (node_info); + + return (gchar **) g_ptr_array_free (p, FALSE); +} + +static gboolean +has_interface (GDBusConnection *c, + const gchar *object_path, + const gchar *interface_name) +{ + GError *error; + GDBusProxy *proxy; + gchar *xml_data; + GDBusNodeInfo *node_info; + gboolean ret; + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.freedesktop.DBus.Introspectable", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + xml_data = NULL; + g_dbus_proxy_invoke_method (proxy, + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); + g_main_loop_run (loop); + g_assert (xml_data != NULL); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + ret = (g_dbus_node_info_lookup_interface (node_info, interface_name) != NULL); + + g_object_unref (proxy); + g_free (xml_data); + g_dbus_node_info_unref (node_info); + + return ret; +} + +static guint +count_interfaces (GDBusConnection *c, + const gchar *object_path) +{ + GError *error; + GDBusProxy *proxy; + gchar *xml_data; + GDBusNodeInfo *node_info; + guint ret; + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.freedesktop.DBus.Introspectable", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + xml_data = NULL; + g_dbus_proxy_invoke_method (proxy, + "Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) introspect_callback, + &xml_data); + g_main_loop_run (loop); + g_assert (xml_data != NULL); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + ret = 0; + while (node_info->interfaces != NULL && node_info->interfaces[ret] != NULL) + ret++; + + g_object_unref (proxy); + g_free (xml_data); + g_dbus_node_info_unref (node_info); + + return ret; +} + +static void +dyna_create_callback (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + GVariant *result; + GError *error; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +/* Dynamically create @object_name under /foo/dyna */ +static void +dyna_create (GDBusConnection *c, + const gchar *object_name) +{ + GError *error; + GDBusProxy *proxy; + gchar *object_path; + + object_path = g_strconcat ("/foo/dyna/", object_name, NULL); + + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.example.Dyna", + NULL, + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + + /* do this async to avoid libdbus-1 deadlocks */ + g_dbus_proxy_invoke_method (proxy, + "DynaCyber", + g_variant_new ("()"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) dyna_create_callback, + NULL); + g_main_loop_run (loop); + + g_assert_no_error (error); + + g_object_unref (proxy); + g_free (object_path); + + return; +} + +typedef struct +{ + guint num_unregistered_calls; + guint num_unregistered_subtree_calls; + guint num_subtree_nodes; +} ObjectRegistrationData; + +static void +on_object_unregistered (gpointer user_data) +{ + ObjectRegistrationData *data = user_data; + + data->num_unregistered_calls++; +} + +static void +on_subtree_unregistered (gpointer user_data) +{ + ObjectRegistrationData *data = user_data; + + data->num_unregistered_subtree_calls++; +} + +static gboolean +_g_strv_has_string (const gchar* const * haystack, + const gchar *needle) +{ + guint n; + + for (n = 0; haystack != NULL && haystack[n] != NULL; n++) + { + if (g_strcmp0 (haystack[n], needle) == 0) + return TRUE; + } + return FALSE; +} + +/* -------------------- */ + +static gchar ** +subtree_enumerate (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + gpointer user_data) +{ + ObjectRegistrationData *data = user_data; + GPtrArray *p; + gchar **nodes; + guint n; + + p = g_ptr_array_new (); + + for (n = 0; n < data->num_subtree_nodes; n++) + { + g_ptr_array_add (p, g_strdup_printf ("vp%d", n)); + g_ptr_array_add (p, g_strdup_printf ("evp%d", n)); + } + g_ptr_array_add (p, NULL); + + nodes = (gchar **) g_ptr_array_free (p, FALSE); + + return nodes; +} + +/* Only allows certain objects, and aborts on unknowns */ +static GPtrArray * +subtree_introspect (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *node, + gpointer user_data) +{ + GPtrArray *interfaces; + + /* VPs implement the Foo interface, EVPs implement the Bar interface. The root + * does not implement any interfaces + */ + interfaces = g_ptr_array_new (); + if (g_str_has_prefix (node, "vp")) + { + g_ptr_array_add (interfaces, (gpointer) &foo_interface_info); + } + else if (g_str_has_prefix (node, "evp")) + { + g_ptr_array_add (interfaces, (gpointer) &bar_interface_info); + } + else if (g_strcmp0 (node, "/") == 0) + { + /* do nothing */ + } + else + { + g_assert_not_reached (); + } + + return interfaces; +} + +static const GDBusInterfaceVTable * +subtree_dispatch (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *node, + gpointer *out_user_data, + gpointer user_data) +{ + if (g_strcmp0 (interface_name, "org.example.Foo") == 0) + return &foo_vtable; + else + return NULL; +} + +static const GDBusSubtreeVTable subtree_vtable = +{ + subtree_enumerate, + subtree_introspect, + subtree_dispatch +}; + +/* -------------------- */ + +static gchar ** +dynamic_subtree_enumerate (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + gpointer user_data) +{ + GPtrArray *data = user_data; + gchar **nodes = g_new (gchar*, data->len + 1); + guint n; + + for (n = 0; n < data->len; n++) + { + nodes[n] = g_strdup (g_ptr_array_index (data, n)); + } + nodes[data->len] = NULL; + + return nodes; +} + +/* Allow all objects to be introspected */ +static GPtrArray * +dynamic_subtree_introspect (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *node, + gpointer user_data) +{ + GPtrArray *interfaces; + + /* All nodes (including the root node) implements the Dyna interface */ + interfaces = g_ptr_array_new (); + g_ptr_array_add (interfaces, (gpointer) &dyna_interface_info); + + return interfaces; +} + +static const GDBusInterfaceVTable * +dynamic_subtree_dispatch (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *node, + gpointer *out_user_data, + gpointer user_data) +{ + *out_user_data = user_data; + return &dyna_interface_vtable; +} + +static const GDBusSubtreeVTable dynamic_subtree_vtable = +{ + dynamic_subtree_enumerate, + dynamic_subtree_introspect, + dynamic_subtree_dispatch +}; + +/* -------------------- */ + +static gpointer +test_dispatch_thread_func (gpointer user_data) +{ + const gchar *object_path = user_data; + GDBusProxy *foo_proxy; + GVariant *value; + GVariant *inner; + GError *error; + gchar *s; + const gchar *value_str; + + foo_proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + g_dbus_connection_get_unique_name (c), + object_path, + "org.example.Foo", + NULL, + &error); + g_assert (foo_proxy != NULL); + + /* generic interfaces */ + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Peer.Ping", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_variant_unref (value); + + /* user methods */ + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "Method1", + g_variant_new ("(s)", "winwinwin"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(s)"))); + g_variant_get (value, "(s)", &value_str); + g_assert_cmpstr (value_str, ==, "You passed the string `winwinwin'. Jolly good!"); + g_variant_unref (value); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "Method2", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.example.SomeError: How do you like them apples, buddy!"); + g_error_free (error); + g_assert (value == NULL); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "Method2", + g_variant_new ("(s)", "failfailfail"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Signature of message, `s', does not match expected signature `'"); + g_error_free (error); + g_assert (value == NULL); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "NonExistantMethod", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method `NonExistantMethod'"); + g_error_free (error); + g_assert (value == NULL); + + /* user properties */ + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "PropertyUno"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(v)"))); + g_variant_get (value, "(v)", &inner); + g_assert (g_variant_is_of_type (inner, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (inner, NULL), ==, "Property `PropertyUno' Is What It Is!"); + g_variant_unref (value); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "ThisDoesntExist"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: No such property `ThisDoesntExist'"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new ("(ss)", + "org.example.Foo", + "NotReadable"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotReadable' is not readable"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + "org.example.Foo", + "NotReadable", + g_variant_new_string ("But Writable you are!")), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value `NotReadable' to the property `'But Writable you are!''"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.Set", + g_variant_new ("(ssv)", + "org.example.Foo", + "NotWritable", + g_variant_new_uint32 (42)), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert (value == NULL); + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.InvalidArgs: Property `NotWritable' is not writable"); + g_error_free (error); + + error = NULL; + value = g_dbus_proxy_invoke_method_sync (foo_proxy, + "org.freedesktop.DBus.Properties.GetAll", + g_variant_new ("(s)", + "org.example.Foo"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (value != NULL); + g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("(a{sv})"))); + s = g_variant_print (value, TRUE); + g_assert_cmpstr (s, ==, "({'PropertyUno': <\"Property `PropertyUno' Is What It Is!\">, 'NotWritable': <\"Property `NotWritable' Is What It Is!\">},)"); + g_free (s); + g_variant_unref (value); + + g_object_unref (foo_proxy); + + g_main_loop_quit (loop); + return NULL; +} + +static void +test_dispatch (const gchar *object_path) +{ + GThread *thread; + GError *error; + + /* run this in a thread to avoid deadlocks */ + error = NULL; + thread = g_thread_create (test_dispatch_thread_func, + (gpointer) object_path, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread != NULL); + g_main_loop_run (loop); + g_thread_join (thread); +} + +static void +test_object_registration (void) +{ + GError *error; + ObjectRegistrationData data; + GPtrArray *dyna_data; + gchar **nodes; + guint boss_foo_reg_id; + guint boss_bar_reg_id; + guint worker1_foo_reg_id; + guint worker2_bar_reg_id; + guint intern1_foo_reg_id; + guint intern2_bar_reg_id; + guint intern2_foo_reg_id; + guint intern3_bar_reg_id; + guint registration_id; + guint subtree_registration_id; + guint non_subtree_object_path_foo_reg_id; + guint non_subtree_object_path_bar_reg_id; + guint dyna_subtree_registration_id; + guint num_successful_registrations; + + data.num_unregistered_calls = 0; + data.num_unregistered_subtree_calls = 0; + data.num_subtree_nodes = 0; + + num_successful_registrations = 0; + + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss", + foo_interface_info.name, + &foo_interface_info, + &foo_vtable, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + boss_foo_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + boss_bar_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/worker1", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + worker1_foo_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/worker2", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + worker2_bar_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern1", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern1_foo_reg_id = registration_id; + num_successful_registrations++; + + /* ... and try again at another path */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern2_bar_reg_id = registration_id; + num_successful_registrations++; + + /* register at the same path/interface - this should fail */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + error = NULL; + g_assert (registration_id == 0); + + /* register at different interface - shouldn't fail */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern2_foo_reg_id = registration_id; + num_successful_registrations++; + + /* unregister it via the id */ + g_assert (g_dbus_connection_unregister_object (c, registration_id)); + g_assert_cmpint (data.num_unregistered_calls, ==, 1); + intern2_foo_reg_id = 0; + + /* register it back */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern2", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern2_foo_reg_id = registration_id; + num_successful_registrations++; + + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/interns/intern3", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + intern3_bar_reg_id = registration_id; + num_successful_registrations++; + + /* now register a whole subtree at /foo/boss/executives */ + subtree_registration_id = g_dbus_connection_register_subtree (c, + "/foo/boss/executives", + &subtree_vtable, + G_DBUS_SUBTREE_FLAGS_NONE, + &data, + on_subtree_unregistered, + &error); + g_assert_no_error (error); + g_assert (subtree_registration_id > 0); + /* try registering it again.. this should fail */ + registration_id = g_dbus_connection_register_subtree (c, + "/foo/boss/executives", + &subtree_vtable, + G_DBUS_SUBTREE_FLAGS_NONE, + &data, + on_subtree_unregistered, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + error = NULL; + g_assert (registration_id == 0); + + /* unregister it, then register it again */ + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 0); + g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id)); + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1); + subtree_registration_id = g_dbus_connection_register_subtree (c, + "/foo/boss/executives", + &subtree_vtable, + G_DBUS_SUBTREE_FLAGS_NONE, + &data, + on_subtree_unregistered, + &error); + g_assert_no_error (error); + g_assert (subtree_registration_id > 0); + + /* try to register something under /foo/boss/executives - this should work + * because registered subtrees and registered objects can coexist. + * + * Make the exported object implement *two* interfaces so we can check + * that the right introspection handler is invoked. + */ + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/executives/non_subtree_object", + bar_interface_info.name, + &bar_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + non_subtree_object_path_bar_reg_id = registration_id; + num_successful_registrations++; + registration_id = g_dbus_connection_register_object (c, + "/foo/boss/executives/non_subtree_object", + foo_interface_info.name, + &foo_interface_info, + NULL, + &data, + on_object_unregistered, + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + non_subtree_object_path_foo_reg_id = registration_id; + num_successful_registrations++; + + /* now register a dynamic subtree, spawning objects as they are called */ + dyna_data = g_ptr_array_new (); + dyna_subtree_registration_id = g_dbus_connection_register_subtree (c, + "/foo/dyna", + &dynamic_subtree_vtable, + G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES, + dyna_data, + (GDestroyNotify)g_ptr_array_unref, + &error); + g_assert_no_error (error); + g_assert (dyna_subtree_registration_id > 0); + + /* First assert that we have no nodes in the dynamic subtree */ + nodes = get_nodes_at (c, "/foo/dyna"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 0); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/dyna"), ==, 4); + + /* Install three nodes in the dynamic subtree via the dyna_data backdoor and + * assert that they show up correctly in the introspection data */ + g_ptr_array_add (dyna_data, "lol"); + g_ptr_array_add (dyna_data, "cat"); + g_ptr_array_add (dyna_data, "cheezburger"); + nodes = get_nodes_at (c, "/foo/dyna"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 3); + g_assert_cmpstr (nodes[0], ==, "lol"); + g_assert_cmpstr (nodes[1], ==, "cat"); + g_assert_cmpstr (nodes[2], ==, "cheezburger"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/lol"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/cat"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/cheezburger"), ==, 4); + + /* Call a non-existing object path and assert that it has been created */ + dyna_create (c, "dynamicallycreated"); + nodes = get_nodes_at (c, "/foo/dyna"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 4); + g_assert_cmpstr (nodes[0], ==, "lol"); + g_assert_cmpstr (nodes[1], ==, "cat"); + g_assert_cmpstr (nodes[2], ==, "cheezburger"); + g_assert_cmpstr (nodes[3], ==, "dynamicallycreated"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/dyna/dynamicallycreated"), ==, 4); + + /* now check that the object hierarachy is properly generated... yes, it's a bit + * perverse that we round-trip to the bus to introspect ourselves ;-) + */ + nodes = get_nodes_at (c, "/"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 1); + g_assert_cmpstr (nodes[0], ==, "foo"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/"), ==, 0); + + nodes = get_nodes_at (c, "/foo"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 2); + g_assert_cmpstr (nodes[0], ==, "boss"); + g_assert_cmpstr (nodes[1], ==, "dyna"); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo"), ==, 0); + + nodes = get_nodes_at (c, "/foo/boss"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 4); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "worker2")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "interns")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "executives")); + g_strfreev (nodes); + /* any registered object always implement org.freedesktop.DBus.[Peer,Introspectable,Properties] */ + g_assert_cmpint (count_interfaces (c, "/foo/boss"), ==, 5); + g_assert (has_interface (c, "/foo/boss", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss", bar_interface_info.name)); + + /* check subtree nodes - we should have only non_subtree_object in /foo/boss/executives + * because data.num_subtree_nodes is 0 + */ + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_assert_cmpint (g_strv_length (nodes), ==, 1); + g_strfreev (nodes); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives"), ==, 0); + + /* now change data.num_subtree_nodes and check */ + data.num_subtree_nodes = 2; + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 5); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1")); + /* check that /foo/boss/executives/non_subtree_object is not handled by the + * subtree handlers - we can do this because objects from subtree handlers + * has exactly one interface and non_subtree_object has two + */ + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/non_subtree_object"), ==, 5); + g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/non_subtree_object", bar_interface_info.name)); + /* check that the vp and evp objects are handled by the subtree handlers */ + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp0"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/vp1"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp0"), ==, 4); + g_assert_cmpint (count_interfaces (c, "/foo/boss/executives/evp1"), ==, 4); + g_assert (has_interface (c, "/foo/boss/executives/vp0", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/vp1", foo_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/evp0", bar_interface_info.name)); + g_assert (has_interface (c, "/foo/boss/executives/evp1", bar_interface_info.name)); + g_strfreev (nodes); + data.num_subtree_nodes = 3; + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 7); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "vp2")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp0")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp1")); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "evp2")); + g_strfreev (nodes); + + /* check that calls are properly dispatched to the functions in foo_vtable for objects + * implementing the org.example.Foo interface + * + * We do this for both a regular registered object (/foo/boss) and also for an object + * registered through the subtree mechanism. + */ + test_dispatch ("/foo/boss"); + test_dispatch ("/foo/boss/executives/vp0"); + + /* To prevent from exiting and attaching a D-Bus tool like D-Feet; uncomment: */ +#if 0 + g_debug ("Point D-feet or other tool at: %s", session_bus_get_temporary_address()); + g_main_loop_run (loop); +#endif + + /* check that unregistering the subtree handler works */ + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 1); + g_assert (g_dbus_connection_unregister_subtree (c, subtree_registration_id)); + g_assert_cmpint (data.num_unregistered_subtree_calls, ==, 2); + nodes = get_nodes_at (c, "/foo/boss/executives"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 1); + g_assert (_g_strv_has_string ((const gchar* const *) nodes, "non_subtree_object")); + g_strfreev (nodes); + + g_assert (g_dbus_connection_unregister_object (c, boss_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, boss_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, worker1_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, worker2_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern1_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern2_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern2_foo_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, intern3_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_bar_reg_id)); + g_assert (g_dbus_connection_unregister_object (c, non_subtree_object_path_foo_reg_id)); + + g_assert_cmpint (data.num_unregistered_calls, ==, num_successful_registrations); + + /* check that we no longer export any objects - TODO: it looks like there's a bug in + * libdbus-1 here: libdbus still reports the '/foo' object; so disable the test for now + */ +#if 0 + nodes = get_nodes_at (c, "/"); + g_assert (nodes != NULL); + g_assert_cmpint (g_strv_length (nodes), ==, 0); + g_strfreev (nodes); +#endif + + g_object_unref (c); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + session_bus_up (); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + + g_test_add_func ("/gdbus/object-registration", test_object_registration); + /* TODO: check that we spit out correct introspection data */ + /* TODO: check that registering a whole subtree works */ + + ret = g_test_run(); + + /* tear down bus */ + session_bus_down (); + + return ret; +} diff --git a/gdbus/tests/gdbus-introspection.c b/gdbus/tests/gdbus-introspection.c new file mode 100644 index 0000000..84f3038 --- /dev/null +++ b/gdbus/tests/gdbus-introspection.c @@ -0,0 +1,169 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test introspection parser */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +introspection_on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + GError *error; + const gchar *xml_data; + GDBusNodeInfo *node_info; + const GDBusInterfaceInfo *interface_info; + const GDBusMethodInfo *method_info; + const GDBusSignalInfo *signal_info; + GVariant *result; + + error = NULL; + + /* + * Invoke Introspect(), then parse the output. + */ + result = g_dbus_proxy_invoke_method_sync (proxy, + "org.freedesktop.DBus.Introspectable.Introspect", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(s)", &xml_data); + + node_info = g_dbus_node_info_new_for_xml (xml_data, &error); + g_assert_no_error (error); + g_assert (node_info != NULL); + + /* for now we only check a couple of things. TODO: check more things */ + + interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.NonExistantInterface"); + g_assert (interface_info == NULL); + + interface_info = g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.DBus.Introspectable"); + g_assert (interface_info != NULL); + method_info = g_dbus_interface_info_lookup_method (interface_info, "NonExistantMethod"); + g_assert (method_info == NULL); + method_info = g_dbus_interface_info_lookup_method (interface_info, "Introspect"); + g_assert (method_info != NULL); + g_assert (method_info->in_args == NULL); + g_assert (method_info->out_args != NULL); + g_assert (method_info->out_args[0] != NULL); + g_assert (method_info->out_args[1] == NULL); + g_assert_cmpstr (method_info->out_args[0]->signature, ==, "s"); + + interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.Frob"); + g_assert (interface_info != NULL); + signal_info = g_dbus_interface_info_lookup_signal (interface_info, "TestSignal"); + g_assert (signal_info != NULL); + g_assert (signal_info->args != NULL); + g_assert (signal_info->args[0] != NULL); + g_assert_cmpstr (signal_info->args[0]->signature, ==, "s"); + g_assert (signal_info->args[1] != NULL); + g_assert_cmpstr (signal_info->args[1]->signature, ==, "o"); + g_assert (signal_info->args[2] != NULL); + g_assert_cmpstr (signal_info->args[2]->signature, ==, "v"); + g_assert (signal_info->args[3] == NULL); + + g_dbus_node_info_unref (node_info); + g_variant_unref (result); + + g_main_loop_quit (loop); +} + +static void +introspection_on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +test_introspection_parser (void) +{ + guint watcher_id; + + session_bus_up (); + + watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, + "com.example.TestService", + G_BUS_NAME_WATCHER_FLAGS_NONE, + "/com/example/TestObject", + "com.example.Frob", + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + introspection_on_proxy_appeared, + introspection_on_proxy_vanished, + NULL, + NULL); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async ("./testserver.py", NULL)); + + g_main_loop_run (loop); + + g_bus_unwatch_proxy (watcher_id); + + /* tear down bus */ + session_bus_down (); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/introspection-parser", test_introspection_parser); + return g_test_run(); +} diff --git a/gdbus/tests/gdbus-names.c b/gdbus/tests/gdbus-names.c new file mode 100644 index 0000000..9df2de1 --- /dev/null +++ b/gdbus/tests/gdbus-names.c @@ -0,0 +1,749 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that g_bus_own_name() works correctly */ +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean expect_null_connection; + guint num_bus_acquired; + guint num_acquired; + guint num_lost; + guint num_free_func; +} OwnNameData; + +static void +own_name_data_free_func (OwnNameData *data) +{ + data->num_free_func++; + g_main_loop_quit (loop); +} + +static void +bus_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + OwnNameData *data = user_data; + g_dbus_connection_set_exit_on_close (connection, FALSE); + data->num_bus_acquired += 1; + g_main_loop_quit (loop); +} + +static void +name_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + OwnNameData *data = user_data; + data->num_acquired += 1; + g_main_loop_quit (loop); +} + +static void +name_lost_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + OwnNameData *data = user_data; + if (data->expect_null_connection) + { + g_assert (connection == NULL); + } + else + { + g_assert (connection != NULL); + g_dbus_connection_set_exit_on_close (connection, FALSE); + } + data->num_lost += 1; + g_main_loop_quit (loop); +} + +static void +test_bus_own_name (void) +{ + guint id; + guint id2; + OwnNameData data; + OwnNameData data2; + const gchar *name; + GDBusConnection *c; + GError *error; + gboolean name_has_owner_reply; + GDBusConnection *c2; + GVariant *result; + + error = NULL; + name = "org.gtk.GDBus.Name1"; + + /* + * First check that name_lost_handler() is invoked if there is no bus. + * + * Also make sure name_lost_handler() isn't invoked when unowning the name. + */ + data.num_bus_acquired = 0; + data.num_free_func = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = TRUE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 1); + g_bus_unown_name (id); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 1); + + /* + * Bring up a bus, then own a name and check bus_acquired_handler() then name_acquired_handler() is invoked. + */ + session_bus_up (); + data.num_bus_acquired = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + + /* + * Check that the name was actually acquired. + */ + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c != NULL); + g_assert (!g_dbus_connection_is_closed (c)); + result = g_dbus_connection_invoke_method_sync (c, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NameHasOwner", /* method name */ + g_variant_new ("(s)", name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(b)", &name_has_owner_reply); + g_assert (name_has_owner_reply); + g_variant_unref (result); + + /* + * Stop owning the name - this should invoke our free func + */ + g_bus_unown_name (id); + g_assert_cmpint (data.num_free_func, ==, 2); + + /* + * Check that the name was actually released. + */ + result = g_dbus_connection_invoke_method_sync (c, + "org.freedesktop.DBus", /* bus name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "NameHasOwner", /* method name */ + g_variant_new ("(s)", name), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_get (result, "(b)", &name_has_owner_reply); + g_assert (!name_has_owner_reply); + g_variant_unref (result); + + /* + * Own the name again. + */ + data.num_bus_acquired = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + + /* + * Try owning the name with another object on the same connection - this should + * fail because we already own the name. + */ + data2.num_free_func = 0; + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + id2 = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + + /* + * Create a secondary (e.g. private) connection and try owning the name on that + * connection. This should fail both with and without _REPLACE because we + * didn't specify ALLOW_REPLACEMENT. + */ + c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_is_closed (c2)); + /* first without _REPLACE */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + /* then with _REPLACE */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_REPLACE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + + /* + * Stop owning the name and grab it again with _ALLOW_REPLACEMENT. + */ + data.expect_null_connection = FALSE; + g_bus_unown_name (id); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 3); + /* grab it again */ + data.num_bus_acquired = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + id = g_bus_own_name (G_BUS_TYPE_SESSION, + name, + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, + bus_acquired_handler, + name_acquired_handler, + name_lost_handler, + &data, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_bus_acquired, ==, 0); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 0); + g_assert_cmpint (data.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_bus_acquired, ==, 1); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + + /* + * Now try to grab the name from the secondary connection. + * + */ + /* first without _REPLACE - this won't make us acquire the name */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_NONE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 1); + g_assert_cmpint (data2.num_free_func, ==, 1); + /* then with _REPLACE - here we should acquire the name - e.g. owner should lose it + * and owner2 should acquire it */ + data2.num_bus_acquired = 0; + data2.num_acquired = 0; + data2.num_lost = 0; + data2.expect_null_connection = FALSE; + data2.num_free_func = 0; + id2 = g_bus_own_name_on_connection (c2, + name, + G_BUS_NAME_OWNER_FLAGS_REPLACE, + name_acquired_handler, + name_lost_handler, + &data2, + (GDestroyNotify) own_name_data_free_func); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + g_assert_cmpint (data2.num_acquired, ==, 0); + g_assert_cmpint (data2.num_lost, ==, 0); + /* wait for handlers for both owner and owner2 to fire */ + while (data.num_lost == 0 || data2.num_acquired == 0) + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 1); + g_assert_cmpint (data2.num_acquired, ==, 1); + g_assert_cmpint (data2.num_lost, ==, 0); + g_assert_cmpint (data2.num_bus_acquired, ==, 0); + /* ok, make owner2 release the name - then wait for owner to automagically reacquire it */ + g_bus_unown_name (id2); + g_assert_cmpint (data2.num_free_func, ==, 1); + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 2); + g_assert_cmpint (data.num_lost, ==, 1); + + /* + * Finally, nuke the bus and check name_lost_handler() is invoked. + * + */ + data.expect_null_connection = TRUE; + session_bus_down (); + while (data.num_lost != 2) + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 2); + g_assert_cmpint (data.num_lost, ==, 2); + g_bus_unown_name (id); + g_assert_cmpint (data.num_free_func, ==, 4); + + g_object_unref (c); + g_object_unref (c2); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that g_bus_watch_name() works correctly */ +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + gboolean expect_null_connection; + guint num_acquired; + guint num_lost; + guint num_appeared; + guint num_vanished; + guint num_free_func; +} WatchNameData; + +static void +watch_name_data_free_func (WatchNameData *data) +{ + data->num_free_func++; + g_main_loop_quit (loop); +} + +static void +w_bus_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +w_name_acquired_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WatchNameData *data = user_data; + data->num_acquired += 1; + g_main_loop_quit (loop); +} + +static void +w_name_lost_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WatchNameData *data = user_data; + data->num_lost += 1; + g_main_loop_quit (loop); +} + +static void +name_appeared_handler (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + WatchNameData *data = user_data; + if (data->expect_null_connection) + { + g_assert (connection == NULL); + } + else + { + g_assert (connection != NULL); + g_dbus_connection_set_exit_on_close (connection, FALSE); + } + data->num_appeared += 1; + g_main_loop_quit (loop); +} + +static void +name_vanished_handler (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + WatchNameData *data = user_data; + if (data->expect_null_connection) + { + g_assert (connection == NULL); + } + else + { + g_assert (connection != NULL); + g_dbus_connection_set_exit_on_close (connection, FALSE); + } + data->num_vanished += 1; + g_main_loop_quit (loop); +} + +static void +test_bus_watch_name (void) +{ + WatchNameData data; + guint id; + guint owner_id; + + /* + * First check that name_vanished_handler() is invoked if there is no bus. + * + * Also make sure name_vanished_handler() isn't invoked when unwatching the name. + */ + data.num_free_func = 0; + data.num_appeared = 0; + data.num_vanished = 0; + data.expect_null_connection = TRUE; + id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_handler, + name_vanished_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 1); + g_bus_unwatch_name (id); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 1); + + /* + * Now bring up a bus, own a name, and then start watching it. + */ + session_bus_up (); + /* own the name */ + data.num_free_func = 0; + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_OWNER_FLAGS_NONE, + w_bus_acquired_handler, + w_name_acquired_handler, + w_name_lost_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + /* now watch the name */ + data.num_appeared = 0; + data.num_vanished = 0; + id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_handler, + name_vanished_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_appeared, ==, 1); + g_assert_cmpint (data.num_vanished, ==, 0); + + /* + * Unwatch the name. + */ + g_bus_unwatch_name (id); + g_assert_cmpint (data.num_free_func, ==, 1); + + /* unown the name */ + g_bus_unown_name (owner_id); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_free_func, ==, 2); + + /* + * Create a watcher and then make a name be owned. + * + * This should trigger name_appeared_handler() ... + */ + /* watch the name */ + data.num_appeared = 0; + data.num_vanished = 0; + data.num_free_func = 0; + id = g_bus_watch_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_handler, + name_vanished_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 0); + g_main_loop_run (loop); + g_assert_cmpint (data.num_appeared, ==, 0); + g_assert_cmpint (data.num_vanished, ==, 1); + + /* own the name */ + data.num_acquired = 0; + data.num_lost = 0; + data.expect_null_connection = FALSE; + owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.gtk.GDBus.Name1", + G_BUS_NAME_OWNER_FLAGS_NONE, + w_bus_acquired_handler, + w_name_acquired_handler, + w_name_lost_handler, + &data, + (GDestroyNotify) watch_name_data_free_func); + while (data.num_acquired == 0 || data.num_appeared == 0) + g_main_loop_run (loop); + g_assert_cmpint (data.num_acquired, ==, 1); + g_assert_cmpint (data.num_lost, ==, 0); + g_assert_cmpint (data.num_appeared, ==, 1); + g_assert_cmpint (data.num_vanished, ==, 1); + + /* + * Nuke the bus and check that the name vanishes and is lost. + */ + data.expect_null_connection = TRUE; + session_bus_down (); + g_main_loop_run (loop); + g_assert_cmpint (data.num_lost, ==, 1); + g_assert_cmpint (data.num_vanished, ==, 2); + + g_bus_unwatch_name (id); + g_assert_cmpint (data.num_free_func, ==, 1); + + g_bus_unown_name (owner_id); + g_assert_cmpint (data.num_free_func, ==, 2); + +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_validate_names (void) +{ + guint n; + static const struct + { + gboolean name; + gboolean unique; + gboolean interface; + const gchar *string; + } names[] = { + { 1, 0, 1, "valid.well_known.name"}, + { 1, 0, 0, "valid.well-known.name"}, + { 1, 1, 0, ":valid.unique.name"}, + { 0, 0, 0, "invalid.5well_known.name"}, + { 0, 0, 0, "4invalid.5well_known.name"}, + { 1, 1, 0, ":4valid.5unique.name"}, + { 0, 0, 0, ""}, + { 1, 0, 1, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name1"}, /* 255 */ + { 0, 0, 0, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name12"}, /* 256 - too long! */ + { 0, 0, 0, ".starts.with.a.dot"}, + { 0, 0, 0, "contains.invalid;.characters"}, + { 0, 0, 0, "contains.inva/lid.characters"}, + { 0, 0, 0, "contains.inva[lid.characters"}, + { 0, 0, 0, "contains.inva]lid.characters"}, + { 0, 0, 0, "contains.inva_æøå_lid.characters"}, + { 1, 1, 0, ":1.1"}, + }; + + for (n = 0; n < G_N_ELEMENTS (names); n++) + { + if (names[n].name) + g_assert (g_dbus_is_name (names[n].string)); + else + g_assert (!g_dbus_is_name (names[n].string)); + + if (names[n].unique) + g_assert (g_dbus_is_unique_name (names[n].string)); + else + g_assert (!g_dbus_is_unique_name (names[n].string)); + + if (names[n].interface) + g_assert (g_dbus_is_interface_name (names[n].string)); + else + g_assert (!g_dbus_is_interface_name (names[n].string)); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/validate-names", test_validate_names); + g_test_add_func ("/gdbus/bus-own-name", test_bus_own_name); + g_test_add_func ("/gdbus/bus-watch-name", test_bus_watch_name); + + ret = g_test_run(); + + g_main_loop_unref (loop); + + return ret; +} diff --git a/gdbus/tests/gdbus-peer.c b/gdbus/tests/gdbus-peer.c new file mode 100644 index 0000000..327144d --- /dev/null +++ b/gdbus/tests/gdbus-peer.c @@ -0,0 +1,746 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +/* for open(2) */ +#include +#include +#include + +#include + +#include "gdbus-tests.h" + + +#ifdef G_OS_UNIX +static gboolean is_unix = TRUE; +#else +static gboolean is_unix = FALSE; +#endif + +static gchar *test_guid = NULL; +static GMainLoop *service_loop = NULL; +static GDBusServer *server = NULL; +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that peer-to-peer connections work */ +/* ---------------------------------------------------------------------------------------------------- */ + + +typedef struct +{ + gboolean accept_connection; + gint num_connection_attempts; + GPtrArray *current_connections; + guint num_method_calls; + gboolean signal_received; +} PeerData; + +static const gchar *test_interface_introspection_xml = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; +static const GDBusInterfaceInfo *test_interface_introspection_data = NULL; + +static void +test_interface_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + PeerData *data = user_data; + + data->num_method_calls++; + + g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject"); + g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface"); + + if (g_strcmp0 (method_name, "HelloPeer") == 0) + { + const gchar *greeting; + gchar *response; + + g_variant_get (parameters, "(s)", &greeting); + + response = g_strdup_printf ("You greeted me with '%s'.", + greeting); + g_dbus_method_invocation_return_value (invocation, + g_variant_new ("(s)", response)); + g_free (response); + } + else if (g_strcmp0 (method_name, "EmitSignal") == 0) + { + GError *error; + + error = NULL; + g_dbus_connection_emit_signal (connection, + NULL, + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + "PeerSignal", + NULL, + &error); + g_assert_no_error (error); + g_dbus_method_invocation_return_value (invocation, NULL); + } + else if (g_strcmp0 (method_name, "OpenFile") == 0) + { + const gchar *path; + GDBusMessage *reply; + GError *error; + gint fd; + GUnixFDList *fd_list; + + g_variant_get (parameters, "(s)", &path); + + fd_list = g_unix_fd_list_new (); + + error = NULL; + + fd = open (path, O_RDONLY); + g_unix_fd_list_append (fd_list, fd, &error); + g_assert_no_error (error); + close (fd); + + reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation)); + g_dbus_message_set_unix_fd_list (reply, fd_list); + g_object_unref (invocation); + + error = NULL; + g_dbus_connection_send_message (connection, + reply, + NULL, /* out_serial */ + &error); + g_assert_no_error (error); + g_object_unref (reply); + } + else + { + g_assert_not_reached (); + } +} + +static GVariant * +test_interface_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject"); + g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface"); + g_assert_cmpstr (property_name, ==, "PeerProperty"); + + return g_variant_new_string ("ThePropertyValue"); +} + + +static const GDBusInterfaceVTable test_interface_vtable = +{ + test_interface_method_call, + test_interface_get_property, + NULL /* set_property */ +}; + +static void +on_proxy_signal_received (GDBusProxy *proxy, + gchar *sender_name, + gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + PeerData *data = user_data; + + data->signal_received = TRUE; + + g_assert (sender_name == NULL); + g_assert_cmpstr (signal_name, ==, "PeerSignal"); + g_main_loop_quit (loop); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +on_deny_authenticated_peer (GDBusAuthObserver *observer, + GIOStream *stream, + GCredentials *credentials, + gpointer user_data) +{ + PeerData *data = user_data; + gboolean deny_peer; + + data->num_connection_attempts++; + + deny_peer = FALSE; + if (!data->accept_connection) + { + deny_peer = TRUE; + g_main_loop_quit (loop); + } + + return deny_peer; +} + +/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ +static void +on_new_connection (GDBusServer *server, + GDBusConnection *connection, + gpointer user_data) +{ + PeerData *data = user_data; + GError *error; + guint reg_id; + + //g_print ("Client connected.\n" + // "Negotiated capabilities: unix-fd-passing=%d\n", + // g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); + + g_ptr_array_add (data->current_connections, g_object_ref (connection)); + + /* export object on the newly established connection */ + error = NULL; + reg_id = g_dbus_connection_register_object (connection, + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + test_interface_introspection_data, + &test_interface_vtable, + data, + NULL, /* GDestroyNotify for data */ + &error); + g_assert_no_error (error); + g_assert (reg_id > 0); + + g_main_loop_quit (loop); +} + +static gpointer +service_thread_func (gpointer user_data) +{ + PeerData *data = user_data; + GMainContext *service_context; + GDBusAuthObserver *observer; + GError *error; + + service_context = g_main_context_new (); + g_main_context_push_thread_default (service_context); + + error = NULL; + observer = g_dbus_auth_observer_new (); + server = g_dbus_server_new_sync (is_unix ? "unix:tmpdir=/tmp/gdbus-test-" : "nonce-tcp:", + G_DBUS_SERVER_FLAGS_NONE, + test_guid, + observer, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + + g_signal_connect (server, + "new-connection", + G_CALLBACK (on_new_connection), + data); + g_signal_connect (observer, + "deny-authenticated-peer", + G_CALLBACK (on_deny_authenticated_peer), + data); + g_object_unref (observer); + + g_dbus_server_start (server); + + service_loop = g_main_loop_new (service_context, FALSE); + g_main_loop_run (service_loop); + + g_main_context_pop_thread_default (service_context); + + g_main_loop_unref (service_loop); + g_main_context_unref (service_context); + + /* test code specifically unrefs the server - see below */ + g_assert (server == NULL); + + return NULL; +} + +#if 0 +static gboolean +on_incoming_connection (GSocketService *service, + GSocketConnection *socket_connection, + GObject *source_object, + gpointer user_data) +{ + PeerData *data = user_data; + + if (data->accept_connection) + { + GError *error; + guint reg_id; + GDBusConnection *connection; + + error = NULL; + connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), + test_guid, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + + g_ptr_array_add (data->current_connections, connection); + + /* export object on the newly established connection */ + error = NULL; + reg_id = g_dbus_connection_register_object (connection, + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + &test_interface_introspection_data, + &test_interface_vtable, + data, + NULL, /* GDestroyNotify for data */ + &error); + g_assert_no_error (error); + g_assert (reg_id > 0); + + } + else + { + /* don't do anything */ + } + + data->num_connection_attempts++; + + g_main_loop_quit (loop); + + /* stops other signal handlers from being invoked */ + return TRUE; +} + +static gpointer +service_thread_func (gpointer data) +{ + GMainContext *service_context; + gchar *socket_path; + GSocketAddress *address; + GError *error; + + service_context = g_main_context_new (); + g_main_context_push_thread_default (service_context); + + socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d", getpid ()); + address = g_unix_socket_address_new (socket_path); + + service = g_socket_service_new (); + error = NULL; + g_socket_listener_add_address (G_SOCKET_LISTENER (service), + address, + G_SOCKET_TYPE_STREAM, + G_SOCKET_PROTOCOL_DEFAULT, + NULL, /* source_object */ + NULL, /* effective_address */ + &error); + g_assert_no_error (error); + g_signal_connect (service, + "incoming", + G_CALLBACK (on_incoming_connection), + data); + g_socket_service_start (service); + + service_loop = g_main_loop_new (service_context, FALSE); + g_main_loop_run (service_loop); + + g_main_context_pop_thread_default (service_context); + + g_main_loop_unref (service_loop); + g_main_context_unref (service_context); + + g_object_unref (address); + g_free (socket_path); + return NULL; +} +#endif + +/* ---------------------------------------------------------------------------------------------------- */ + +#if 0 +static gboolean +check_connection (gpointer user_data) +{ + PeerData *data = user_data; + guint n; + + for (n = 0; n < data->current_connections->len; n++) + { + GDBusConnection *c; + GIOStream *stream; + + c = G_DBUS_CONNECTION (data->current_connections->pdata[n]); + stream = g_dbus_connection_get_stream (c); + + g_debug ("In check_connection for %d: connection %p, stream %p", n, c, stream); + g_debug ("closed = %d", g_io_stream_is_closed (stream)); + + GSocket *socket; + socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream)); + g_debug ("socket_closed = %d", g_socket_is_closed (socket)); + g_debug ("socket_condition_check = %d", g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP)); + + gchar buf[128]; + GError *error; + gssize num_read; + error = NULL; + num_read = g_input_stream_read (g_io_stream_get_input_stream (stream), + buf, + 128, + NULL, + &error); + if (num_read < 0) + { + g_debug ("error: %s", error->message); + g_error_free (error); + } + else + { + g_debug ("no error, read %d bytes", (gint) num_read); + } + } + + return FALSE; +} + +static gboolean +on_do_disconnect_in_idle (gpointer data) +{ + GDBusConnection *c = G_DBUS_CONNECTION (data); + g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count); + g_dbus_connection_disconnect (c); + g_object_unref (c); + return FALSE; +} +#endif + +static void +test_peer (void) +{ + GDBusConnection *c; + GDBusConnection *c2; + GDBusProxy *proxy; + GError *error; + PeerData data; + GVariant *value; + GVariant *result; + const gchar *s; + GThread *service_thread; + + memset (&data, '\0', sizeof (PeerData)); + data.current_connections = g_ptr_array_new_with_free_func (g_object_unref); + + /* first try to connect when there is no server */ + error = NULL; + c = g_dbus_connection_new_for_address_sync (is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" : + /* NOTE: Even if something is listening on port 12345 the connection + * will fail because the nonce file doesn't exist */ + "nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus", + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + _g_assert_error_domain (error, G_IO_ERROR); + g_assert (!g_dbus_error_is_remote_error (error)); + g_clear_error (&error); + g_assert (c == NULL); + + /* bring up a server - we run the server in a different thread to avoid deadlocks */ + error = NULL; + service_thread = g_thread_create (service_thread_func, + &data, + TRUE, + &error); + while (service_loop == NULL) + g_thread_yield (); + g_assert (server != NULL); + + /* bring up a connection and accept it */ + data.accept_connection = TRUE; + error = NULL; + c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (c != NULL); + while (data.current_connections->len < 1) + g_main_loop_run (loop); + g_assert_cmpint (data.current_connections->len, ==, 1); + g_assert_cmpint (data.num_connection_attempts, ==, 1); + //g_assert (g_dbus_connection_get_bus_type (c) == G_BUS_TYPE_NONE); + g_assert (g_dbus_connection_get_unique_name (c) == NULL); + g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid); + + /* check that we create a proxy, read properties, receive signals and invoke + * the HelloPeer() method. Since the server runs in another thread it's fine + * to use synchronous blocking API here. + */ + error = NULL; + proxy = g_dbus_proxy_new_sync (c, + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + NULL, /* bus_name */ + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_assert (proxy != NULL); + error = NULL; + value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error); + g_assert_no_error (error); + g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue"); + + /* try invoking a method */ + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloPeer", + g_variant_new ("(s)", "Hey Peer!"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (result, "(s)", &s); + g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'."); + g_variant_unref (result); + g_assert_cmpint (data.num_method_calls, ==, 1); + + /* make the other peer emit a signal - catch it */ + g_signal_connect (proxy, + "g-signal", + G_CALLBACK (on_proxy_signal_received), + &data); + g_assert (!data.signal_received); + g_dbus_proxy_invoke_method (proxy, + "EmitSignal", + NULL, /* no arguments */ + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + NULL, /* GAsyncReadyCallback - we don't care about the result */ + NULL); /* user_data */ + g_main_loop_run (loop); + g_assert (data.signal_received); + g_assert_cmpint (data.num_method_calls, ==, 2); + + /* check for UNIX fd passing */ +#ifdef G_OS_UNIX + { + GDBusMessage *method_call_message; + GDBusMessage *method_reply_message; + GUnixFDList *fd_list; + gint fd; + gchar buf[1024]; + gssize len; + gchar *buf2; + gsize len2; + + method_call_message = g_dbus_message_new_method_call (NULL, /* name */ + "/org/gtk/GDBus/PeerTestObject", + "org.gtk.GDBus.PeerTestInterface", + "OpenFile"); + g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", "/etc/hosts")); + error = NULL; + method_reply_message = g_dbus_connection_send_message_with_reply_sync (c, + method_call_message, + -1, + NULL, /* out_serial */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); + fd_list = g_dbus_message_get_unix_fd_list (method_reply_message); + g_assert (fd_list != NULL); + g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1); + error = NULL; + fd = g_unix_fd_list_get (fd_list, 0, &error); + g_assert_no_error (error); + g_object_unref (method_call_message); + g_object_unref (method_reply_message); + + memset (buf, '\0', sizeof (buf)); + len = read (fd, buf, sizeof (buf) - 1); + close (fd); + + error = NULL; + g_file_get_contents ("/etc/hosts", + &buf2, + &len2, + &error); + g_assert_no_error (error); + if (len2 > sizeof (buf)) + buf2[sizeof (buf)] = '\0'; + g_assert_cmpstr (buf, ==, buf2); + g_free (buf2); + } +#endif /* G_OS_UNIX */ + + + /* bring up a connection - don't accept it - this should fail + */ + data.accept_connection = FALSE; + error = NULL; + c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + _g_assert_error_domain (error, G_IO_ERROR); + g_assert (c2 == NULL); + +#if 0 + /* TODO: THIS TEST DOESN'T WORK YET */ + + /* bring up a connection - accept it.. then disconnect from the client side - check + * that the server side gets the disconnect signal. + */ + error = NULL; + data.accept_connection = TRUE; + c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert (c2 != NULL); + g_assert (!g_dbus_connection_get_is_disconnected (c2)); + while (data.num_connection_attempts < 3) + g_main_loop_run (loop); + g_assert_cmpint (data.current_connections->len, ==, 2); + g_assert_cmpint (data.num_connection_attempts, ==, 3); + g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); + g_idle_add (on_do_disconnect_in_idle, c2); + g_debug ("=================================================="); + g_debug ("=================================================="); + g_debug ("=================================================="); + g_debug ("waiting for disconnect on connection %p, stream %p", + data.current_connections->pdata[1], + g_dbus_connection_get_stream (data.current_connections->pdata[1])); + + g_timeout_add (2000, check_connection, &data); + //_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed"); + g_main_loop_run (loop); + g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); + g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */ +#endif + + /* unref the server and stop listening for new connections + * + * This won't bring down the established connections - check that c is still connected + * by invoking a method + */ + //g_socket_service_stop (service); + //g_object_unref (service); + g_dbus_server_stop (server); + g_object_unref (server); + server = NULL; + + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloPeer", + g_variant_new ("(s)", "Hey Again Peer!"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + g_variant_get (result, "(s)", &s); + g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'."); + g_variant_unref (result); + g_assert_cmpint (data.num_method_calls, ==, 4); + +#if 0 + /* TODO: THIS TEST DOESN'T WORK YET */ + + /* now disconnect from the server side - check that the client side gets the signal */ + g_assert_cmpint (data.current_connections->len, ==, 1); + g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c); + g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0])); + if (!g_dbus_connection_get_is_disconnected (c)) + _g_assert_signal_received (c, "closed"); + g_assert (g_dbus_connection_get_is_disconnected (c)); +#endif + + g_object_unref (c); + g_ptr_array_unref (data.current_connections); + g_object_unref (proxy); + + g_main_loop_quit (service_loop); + g_thread_join (service_thread); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + GDBusNodeInfo *introspection_data = NULL; + + g_type_init (); + g_thread_init (NULL); + g_test_init (&argc, &argv, NULL); + + introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL); + g_assert (introspection_data != NULL); + test_interface_introspection_data = introspection_data->interfaces[0]; + + test_guid = g_dbus_generate_guid (); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + g_test_add_func ("/gdbus/peer-to-peer", test_peer); + + ret = g_test_run(); + + g_main_loop_unref (loop); + g_free (test_guid); + g_dbus_node_info_unref (introspection_data); + + return ret; +} diff --git a/gdbus/tests/gdbus-proxy.c b/gdbus/tests/gdbus-proxy.c new file mode 100644 index 0000000..8e2f55c --- /dev/null +++ b/gdbus/tests/gdbus-proxy.c @@ -0,0 +1,455 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that the method aspects of GDBusProxy works */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_methods (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GVariant *result; + GError *error; + const gchar *str; + gchar *dbus_error_name; + + /* check that we can invoke a method */ + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloWorld", + g_variant_new ("(s)", "Hey"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)"); + g_variant_get (result, "(s)", &str); + g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!"); + g_variant_unref (result); + + /* Check that we can completely recover the returned error */ + result = g_dbus_proxy_invoke_method_sync (proxy, + "HelloWorld", + g_variant_new ("(s)", "Yo"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + dbus_error_name = g_dbus_error_get_remote_error (error); + g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException"); + g_free (dbus_error_name); + g_assert (g_dbus_error_strip_remote_error (error)); + g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting"); + g_clear_error (&error); + + /* Check that we get a timeout if the method handling is taking longer than timeout */ + error = NULL; + result = g_dbus_proxy_invoke_method_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + 100 /* msec */, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); + g_assert (!g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + g_clear_error (&error); + + /* Check that proxy-default timeouts work. */ + g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1); + + /* the default timeout is 25000 msec so this should work */ + result = g_dbus_proxy_invoke_method_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */ + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + + /* now set the proxy-default timeout to 250 msec and try the 500 msec call - this should FAIL */ + g_dbus_proxy_set_default_timeout (proxy, 250); + g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250); + result = g_dbus_proxy_invoke_method_sync (proxy, + "Sleep", + g_variant_new ("(i)", 500 /* msec */), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, /* use proxy default (e.g. 250 msec) */ + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); + g_assert (!g_dbus_error_is_remote_error (error)); + g_assert (result == NULL); + g_clear_error (&error); + + /* clean up after ourselves */ + g_dbus_proxy_set_default_timeout (proxy, -1); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that the property aspects of GDBusProxy works */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_properties (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GError *error; + GVariant *variant; + GVariant *variant2; + GVariant *result; + + error = NULL; + + /* + * Check that we can read cached properties. + * + * No need to test all properties - GVariant has already been tested + */ + variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); + g_assert_no_error (error); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 1); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (proxy, "o", &error); + g_assert_no_error (error); + g_assert (variant != NULL); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path"); + g_variant_unref (variant); + + /* + * Now ask the service to change a property and check that #GDBusProxy::g-property-changed + * is received. Also check that the cache is updated. + */ + variant2 = g_variant_new_byte (42); + result = g_dbus_proxy_invoke_method_sync (proxy, + "FrobSetProperty", + g_variant_new ("(sv)", + "y", + variant2), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + _g_assert_signal_received (proxy, "g-properties-changed"); + variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); + g_assert_no_error (error); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 42); + g_variant_unref (variant); +} + +/* ---------------------------------------------------------------------------------------------------- */ +/* Test that the signal aspects of GDBusProxy works */ +/* ---------------------------------------------------------------------------------------------------- */ + +static void +test_proxy_signals_on_signal (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GString *s = user_data; + + g_assert_cmpstr (signal_name, ==, "TestSignal"); + g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(sov)"); + + g_variant_print_string (parameters, s, TRUE); +} + +typedef struct +{ + GMainLoop *internal_loop; + GString *s; +} TestSignalData; + +static void +test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + TestSignalData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + + /* check that the signal was recieved before we got the method result */ + g_assert (strlen (data->s->str) > 0); + + /* break out of the loop */ + g_main_loop_quit (data->internal_loop); +} + +static void +test_signals (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GError *error; + GString *s; + gulong signal_handler_id; + TestSignalData data; + GVariant *result; + + error = NULL; + + /* + * Ask the service to emit a signal and check that we receive it. + * + * Note that blocking calls don't block in the mainloop so wait for the signal (which + * is dispatched before the method reply) + */ + s = g_string_new (NULL); + signal_handler_id = g_signal_connect (proxy, + "g-signal", + G_CALLBACK (test_proxy_signals_on_signal), + s); + + result = g_dbus_proxy_invoke_method_sync (proxy, + "EmitSignal", + g_variant_new ("(so)", + "Accept the next proposition you hear", + "/some/path"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + /* check that we haven't received the signal just yet */ + g_assert (strlen (s->str) == 0); + /* and now wait for the signal */ + _g_assert_signal_received (proxy, "g-signal"); + g_assert_cmpstr (s->str, + ==, + "('Accept the next proposition you hear .. in bed!', objectpath '/some/path/in/bed', <'a variant'>)"); + g_signal_handler_disconnect (proxy, signal_handler_id); + g_string_free (s, TRUE); + + /* + * Now do this async to check the signal is received before the method returns. + */ + s = g_string_new (NULL); + data.internal_loop = g_main_loop_new (NULL, FALSE); + data.s = s; + signal_handler_id = g_signal_connect (proxy, + "g-signal", + G_CALLBACK (test_proxy_signals_on_signal), + s); + g_dbus_proxy_invoke_method (proxy, + "EmitSignal", + g_variant_new ("(so)", + "You will make a great programmer", + "/some/other/path"), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb, + &data); + g_main_loop_run (data.internal_loop); + g_main_loop_unref (data.internal_loop); + g_assert_cmpstr (s->str, + ==, + "('You will make a great programmer .. in bed!', objectpath '/some/other/path/in/bed', <'a variant'>)"); + g_signal_handler_disconnect (proxy, signal_handler_id); + g_string_free (s, TRUE); +} + +static void +test_bogus_method_return (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy) +{ + GError *error = NULL; + GVariant *result; + + result = g_dbus_proxy_invoke_method_sync (proxy, + "PairReturn", + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); + g_assert (result == NULL); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static const gchar *frob_dbus_interface_xml = + "" + " " + /* Deliberately different from testserver.py's definition */ + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; +static GDBusInterfaceInfo *frob_dbus_interface_info; + +static void +on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + test_methods (connection, name, name_owner, proxy); + test_properties (connection, name, name_owner, proxy); + test_signals (connection, name, name_owner, proxy); + + /* Now repeat the method tests, with an expected interface set */ + g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info); + test_methods (connection, name, name_owner, proxy); + + /* And now one more test where we deliberately set the expected + * interface definition incorrectly + */ + test_bogus_method_return (connection, name, name_owner, proxy); + + g_main_loop_quit (loop); +} + +static void +on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +test_proxy (void) +{ + guint watcher_id; + + session_bus_up (); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + + watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, + "com.example.TestService", + G_BUS_NAME_WATCHER_FLAGS_NONE, + "/com/example/TestObject", + "com.example.Frob", + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + on_proxy_appeared, + on_proxy_vanished, + NULL, + NULL); + + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async ("./testserver.py", NULL)); + + g_main_loop_run (loop); + + g_bus_unwatch_proxy (watcher_id); + + /* tear down bus */ + session_bus_down (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + GDBusNodeInfo *introspection_data = NULL; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + introspection_data = g_dbus_node_info_new_for_xml (frob_dbus_interface_xml, NULL); + g_assert (introspection_data != NULL); + frob_dbus_interface_info = introspection_data->interfaces[0]; + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/proxy", test_proxy); + + ret = g_test_run(); + + g_dbus_node_info_unref (introspection_data); + return ret; +} diff --git a/gdbus/tests/gdbus-serialization.c b/gdbus/tests/gdbus-serialization.c new file mode 100644 index 0000000..b081992 --- /dev/null +++ b/gdbus/tests/gdbus-serialization.c @@ -0,0 +1,650 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include + +#include +#include +#include + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +hexdump (const guchar *str, gsize len) +{ + const guchar *data = (const guchar *) str; + guint n, m; + + for (n = 0; n < len; n += 16) + { + g_printerr ("%04x: ", n); + + for (m = n; m < n + 16; m++) + { + if (m > n && (m%4) == 0) + g_printerr (" "); + if (m < len) + g_printerr ("%02x ", data[m]); + else + g_printerr (" "); + } + + g_printerr (" "); + + for (m = n; m < len && m < n + 16; m++) + g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.'); + + g_printerr ("\n"); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +append_gv_to_dbus_iter (DBusMessageIter *iter, + GVariant *value, + GError **error) +{ + const GVariantType *type; + + type = g_variant_get_type (value); + if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) + { + dbus_bool_t v = g_variant_get_boolean (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE)) + { + guint8 v = g_variant_get_byte (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16)) + { + gint16 v = g_variant_get_int16 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16)) + { + guint16 v = g_variant_get_uint16 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) + { + gint32 v = g_variant_get_int32 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) + { + guint32 v = g_variant_get_uint32 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64)) + { + gint64 v = g_variant_get_int64 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64)) + { + guint64 v = g_variant_get_uint64 (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE)) + { + gdouble v = g_variant_get_double (value); + dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) + { + const gchar *v = g_variant_get_string (value, NULL); + dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH)) + { + const gchar *v = g_variant_get_string (value, NULL); + dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v); + } + else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE)) + { + const gchar *v = g_variant_get_string (value, NULL); + dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v); + } + else if (g_variant_type_is_variant (type)) + { + DBusMessageIter sub; + GVariant *child; + + child = g_variant_get_child_value (value, 0); + dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, + g_variant_get_type_string (child), + &sub); + if (!append_gv_to_dbus_iter (&sub, child, error)) + { + g_variant_unref (child); + goto fail; + } + dbus_message_iter_close_container (iter, &sub); + g_variant_unref (child); + } + else if (g_variant_type_is_array (type)) + { + DBusMessageIter dbus_iter; + const gchar *type_string; + GVariantIter gv_iter; + GVariant *item; + + type_string = g_variant_get_type_string (value); + type_string++; /* skip the 'a' */ + + dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, + type_string, &dbus_iter); + g_variant_iter_init (&gv_iter, value); + + while ((item = g_variant_iter_next_value (&gv_iter))) + { + if (!append_gv_to_dbus_iter (&dbus_iter, item, error)) + { + goto fail; + } + } + + dbus_message_iter_close_container (iter, &dbus_iter); + } + else if (g_variant_type_is_tuple (type)) + { + DBusMessageIter dbus_iter; + GVariantIter gv_iter; + GVariant *item; + + dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, + NULL, &dbus_iter); + g_variant_iter_init (&gv_iter, value); + + while ((item = g_variant_iter_next_value (&gv_iter))) + { + if (!append_gv_to_dbus_iter (&dbus_iter, item, error)) + goto fail; + } + + dbus_message_iter_close_container (iter, &dbus_iter); + } + else if (g_variant_type_is_dict_entry (type)) + { + DBusMessageIter dbus_iter; + GVariant *key, *val; + + dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, + NULL, &dbus_iter); + key = g_variant_get_child_value (value, 0); + if (!append_gv_to_dbus_iter (&dbus_iter, key, error)) + { + g_variant_unref (key); + goto fail; + } + g_variant_unref (key); + + val = g_variant_get_child_value (value, 1); + if (!append_gv_to_dbus_iter (&dbus_iter, val, error)) + { + g_variant_unref (val); + goto fail; + } + g_variant_unref (val); + + dbus_message_iter_close_container (iter, &dbus_iter); + } + else + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Error serializing GVariant with type-string `%s' to a D-Bus message", + g_variant_get_type_string (value)); + goto fail; + } + + return TRUE; + + fail: + return FALSE; +} + +static gboolean +append_gv_to_dbus_message (DBusMessage *message, + GVariant *value, + GError **error) +{ + gboolean ret; + guint n; + + ret = FALSE; + + if (value != NULL) + { + DBusMessageIter iter; + GVariantIter gv_iter; + GVariant *item; + + dbus_message_iter_init_append (message, &iter); + + g_variant_iter_init (&gv_iter, value); + n = 0; + while ((item = g_variant_iter_next_value (&gv_iter))) + { + if (!append_gv_to_dbus_iter (&iter, item, error)) + { + g_prefix_error (error, + "Error encoding in-arg %d: ", + n); + goto out; + } + n++; + } + } + + ret = TRUE; + + out: + return ret; +} + +static void +print_gv_dbus_message (GVariant *value) +{ + DBusMessage *message; + char *blob; + int blob_len; + GError *error; + + message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL); + dbus_message_set_serial (message, 0x41); + dbus_message_set_path (message, "/foo/bar"); + dbus_message_set_member (message, "Member"); + + error = NULL; + if (!append_gv_to_dbus_message (message, value, &error)) + { + g_printerr ("Error printing GVariant as DBusMessage: %s", error->message); + g_error_free (error); + goto out; + } + + dbus_message_marshal (message, &blob, &blob_len); + g_printerr ("\n"); + hexdump ((guchar *) blob, blob_len); + out: + dbus_message_unref (message); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +dbus_1_message_append (GString *s, + guint indent, + DBusMessageIter *iter) +{ + gint arg_type; + DBusMessageIter sub; + + g_string_append_printf (s, "%*s", indent, ""); + + arg_type = dbus_message_iter_get_arg_type (iter); + switch (arg_type) + { + case DBUS_TYPE_BOOLEAN: + { + dbus_bool_t value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "bool: %s\n", value ? "true" : "false"); + break; + } + + case DBUS_TYPE_BYTE: + { + guchar value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "byte: 0x%02x\n", (guint) value); + break; + } + + case DBUS_TYPE_INT16: + { + gint16 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_UINT16: + { + guint16 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_INT32: + { + gint32 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_UINT32: + { + guint32 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_INT64: + { + gint64 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_UINT64: + { + guint64 value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value); + break; + } + + case DBUS_TYPE_DOUBLE: + { + gdouble value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "double: %f\n", value); + break; + } + + case DBUS_TYPE_STRING: + { + const gchar *value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "string: `%s'\n", value); + break; + } + + case DBUS_TYPE_OBJECT_PATH: + { + const gchar *value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "object_path: `%s'\n", value); + break; + } + + case DBUS_TYPE_SIGNATURE: + { + const gchar *value; + dbus_message_iter_get_basic (iter, &value); + g_string_append_printf (s, "signature: `%s'\n", value); + break; + } + + case DBUS_TYPE_VARIANT: + g_string_append_printf (s, "variant:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + case DBUS_TYPE_ARRAY: + g_string_append_printf (s, "array:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + case DBUS_TYPE_STRUCT: + g_string_append_printf (s, "struct:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + case DBUS_TYPE_DICT_ENTRY: + g_string_append_printf (s, "dict_entry:\n"); + dbus_message_iter_recurse (iter, &sub); + while (dbus_message_iter_get_arg_type (&sub)) + { + dbus_1_message_append (s, indent + 2, &sub); + dbus_message_iter_next (&sub); + } + break; + + default: + g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)", + arg_type, + arg_type); + g_assert_not_reached (); + break; + } +} + +static gchar * +dbus_1_message_print (DBusMessage *message) +{ + GString *s; + guint n; + DBusMessageIter iter; + + s = g_string_new (NULL); + n = 0; + dbus_message_iter_init (message, &iter); + while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) + { + g_string_append_printf (s, "value %d: ", n); + dbus_1_message_append (s, 2, &iter); + dbus_message_iter_next (&iter); + n++; + } + + return g_string_free (s, FALSE); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static gchar * +get_body_signature (GVariant *value) +{ + const gchar *s; + gsize len; + gchar *ret; + + s = g_variant_get_type_string (value); + len = strlen (s); + g_assert (len>=2); + + ret = g_strndup (s + 1, len - 2); + + return ret; +} + +static void +check_serialization (GVariant *value, + const gchar *expected_dbus_1_output) +{ + guchar *blob; + gsize blob_size; + DBusMessage *dbus_1_message; + GDBusMessage *message; + GDBusMessage *recovered_message; + GError *error; + DBusError dbus_error; + gchar *s; + gchar *s1; + + message = g_dbus_message_new (); + g_dbus_message_set_body (message, value); + g_dbus_message_set_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL); + g_dbus_message_set_serial (message, 0x41); + s = get_body_signature (value); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar")); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member")); + g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s)); + g_free (s); + + /* First check that the serialization to the D-Bus wire format is correct */ + + error = NULL; + blob = g_dbus_message_to_blob (message, + &blob_size, + &error); + g_assert_no_error (error); + g_assert (blob != NULL); + + dbus_error_init (&dbus_error); + dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error); + if (dbus_error_is_set (&dbus_error)) + { + g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n", + dbus_error.name, + dbus_error.message); + hexdump (blob, blob_size); + dbus_error_free (&dbus_error); + + s = g_variant_print (value, TRUE); + g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s); + g_free (s); + + g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n"); + print_gv_dbus_message (value); + + g_assert_not_reached (); + } + + s = dbus_1_message_print (dbus_1_message); + dbus_message_unref (dbus_1_message); + + g_assert_cmpstr (s, ==, expected_dbus_1_output); + g_free (s); + + /* Then serialize back and check that the body is identical */ + + error = NULL; + recovered_message = g_dbus_message_new_from_blob (blob, blob_size, &error); + g_assert_no_error (error); + g_assert (recovered_message != NULL); + g_assert (g_dbus_message_get_body (recovered_message) != NULL); + + if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value)) + { + s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE); + s1 = g_variant_print (value, TRUE); + g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n", + s, + s1); + g_free (s); + g_free (s1); + g_assert_not_reached (); + } + g_object_unref (message); + g_object_unref (recovered_message); +} + +static void +message_serialize_basic (void) +{ + check_serialization (g_variant_new ("(sogybnqiuxtd)", + "this is a string", + "/this/is/a/path", + "sad", + 42, + TRUE, + -42, + 60000, + -44, + 100000, + -G_GINT64_CONSTANT(2)<<34, + G_GUINT64_CONSTANT(0xffffffffffffffff), + 42.5), + "value 0: string: `this is a string'\n" + "value 1: object_path: `/this/is/a/path'\n" + "value 2: signature: `sad'\n" + "value 3: byte: 0x2a\n" + "value 4: bool: true\n" + "value 5: int16: -42\n" + "value 6: uint16: 60000\n" + "value 7: int32: -44\n" + "value 8: uint32: 100000\n" + "value 9: int64: -34359738368\n" + "value 10: uint64: 18446744073709551615\n" + "value 11: double: 42.500000\n"); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +message_serialize_complex (void) +{ + GError *error; + GVariant *value; + + error = NULL; + value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"), + "([1, 2, 3], {'one': 'white', 'two': 'black'})", + NULL, NULL, &error); + g_assert_no_error (error); + g_assert (value != NULL); + check_serialization (value, + "value 0: array:\n" + " int32: 1\n" + " int32: 2\n" + " int32: 3\n" + "value 1: array:\n" + " dict_entry:\n" + " string: `one'\n" + " string: `white'\n" + " dict_entry:\n" + " string: `two'\n" + " string: `black'\n"); +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + g_type_init (); + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic); + g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex); + return g_test_run(); +} + diff --git a/gdbus/tests/gdbus-sessionbus.c b/gdbus/tests/gdbus-sessionbus.c new file mode 100644 index 0000000..1e16edd --- /dev/null +++ b/gdbus/tests/gdbus-sessionbus.c @@ -0,0 +1,342 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "gdbus-sessionbus.h" + +/* ---------------------------------------------------------------------------------------------------- */ +/* Utilities for bringing up and tearing down session message bus instances */ + +static void +watch_parent (gint fd) +{ + GPollFD fds[1]; + gint num_events; + gchar buf[512]; + guint bytes_read; + GArray *buses_to_kill_array; + + fds[0].fd = fd; + fds[0].events = G_IO_HUP | G_IO_IN; + fds[0].revents = 0; + + buses_to_kill_array = g_array_new (FALSE, TRUE, sizeof (guint)); + + do + { + guint pid; + guint n; + + num_events = g_poll (fds, 1, -1); + if (num_events == 0) + continue; + + if (fds[0].revents == G_IO_HUP) + { + for (n = 0; n < buses_to_kill_array->len; n++) + { + pid = g_array_index (buses_to_kill_array, guint, n); + g_print ("cleaning up bus with pid %d\n", pid); + kill (pid, SIGTERM); + } + g_array_free (buses_to_kill_array, TRUE); + exit (0); + } + + //g_debug ("data from parent"); + + memset (buf, '\0', sizeof buf); + again: + bytes_read = read (fds[0].fd, buf, sizeof buf); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR)) + goto again; + + if (sscanf (buf, "add %d\n", &pid) == 1) + { + g_array_append_val (buses_to_kill_array, pid); + } + else if (sscanf (buf, "remove %d\n", &pid) == 1) + { + for (n = 0; n < buses_to_kill_array->len; n++) + { + if (g_array_index (buses_to_kill_array, guint, n) == pid) + { + g_array_remove_index (buses_to_kill_array, n); + pid = 0; + break; + } + } + if (pid != 0) + { + g_warning ("unknown pid %d to remove", pid); + } + } + else + { + g_warning ("unknown command from parent '%s'", buf); + } + } + while (TRUE); + +} + +static GHashTable *session_bus_address_to_pid = NULL; +static gint pipe_fds[2]; + +const gchar * +session_bus_up_with_address (const gchar *given_address) +{ + gchar *address; + int stdout_fd; + GError *error; + gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL}; + GPid pid; + gchar buf[512]; + ssize_t bytes_read; + gchar *config_file_name; + gint config_file_fd; + GString *config_file_contents; + + address = NULL; + error = NULL; + config_file_name = NULL; + config_file_fd = -1; + argv[2] = NULL; + + config_file_fd = g_file_open_tmp ("g-dbus-tests-XXXXXX", + &config_file_name, + &error); + if (config_file_fd < 0) + { + g_warning ("Error creating temporary config file: %s", error->message); + g_error_free (error); + goto out; + } + + config_file_contents = g_string_new (NULL); + g_string_append (config_file_contents, "\n"); + g_string_append (config_file_contents, " session\n"); + g_string_append_printf (config_file_contents, " %s\n", given_address); + g_string_append (config_file_contents, + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n"); + g_string_append (config_file_contents, "\n"); + + if (write (config_file_fd, config_file_contents->str, config_file_contents->len) != (gssize) config_file_contents->len) + { + g_warning ("Error writing %d bytes to config file: %m", (gint) config_file_contents->len); + g_string_free (config_file_contents, TRUE); + goto out; + } + g_string_free (config_file_contents, TRUE); + + argv[2] = g_strdup_printf ("--config-file=%s", config_file_name); + + if (session_bus_address_to_pid == NULL) + { + /* keep a mapping from session bus address to the pid */ + session_bus_address_to_pid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + /* fork a child to clean up session buses when we are killed */ + if (pipe (pipe_fds) != 0) + { + g_warning ("pipe() failed: %m"); + g_assert_not_reached (); + } + switch (fork ()) + { + case -1: + g_warning ("fork() failed: %m"); + g_assert_not_reached (); + break; + + case 0: + /* child */ + close (pipe_fds[1]); + watch_parent (pipe_fds[0]); + break; + + default: + /* parent */ + close (pipe_fds[0]); + break; + } + + //atexit (cleanup_session_buses); + /* TODO: need to handle the cases where we crash */ + } + else + { + /* check if we already have a bus running for this address */ + if (g_hash_table_lookup (session_bus_address_to_pid, given_address) != NULL) + { + g_warning ("Already have a bus instance for the given address %s", given_address); + goto out; + } + } + + if (!g_spawn_async_with_pipes (NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH, + NULL, + NULL, + &pid, + NULL, + &stdout_fd, + NULL, + &error)) + { + g_warning ("Error spawning dbus-daemon: %s", error->message); + g_error_free (error); + goto out; + } + + memset (buf, '\0', sizeof buf); + again: + bytes_read = read (stdout_fd, buf, sizeof buf); + if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR)) + goto again; + close (stdout_fd); + + if (bytes_read == 0 || bytes_read == sizeof buf) + { + g_warning ("Error reading address from dbus daemon, %d bytes read", (gint) bytes_read); + kill (SIGTERM, pid); + goto out; + } + + address = g_strdup (buf); + g_strstrip (address); + + /* write the pid to the child so it can kill it when we die */ + g_snprintf (buf, sizeof buf, "add %d\n", (guint) pid); + write (pipe_fds[1], buf, strlen (buf)); + + /* start dbus-monitor */ + if (g_getenv ("G_DBUS_MONITOR") != NULL) + { + g_spawn_command_line_async ("dbus-monitor --session", NULL); + usleep (500 * 1000); + } + + g_hash_table_insert (session_bus_address_to_pid, address, GUINT_TO_POINTER (pid)); + + out: + if (config_file_fd > 0) + { + if (close (config_file_fd) != 0) + { + g_warning ("Error closing fd for config file %s: %m", config_file_name); + } + g_assert (config_file_name != NULL); + if (unlink (config_file_name) != 0) + { + g_warning ("Error unlinking config file %s: %m", config_file_name); + } + } + g_free (argv[2]); + g_free (config_file_name); + return address; +} + +void +session_bus_down_with_address (const gchar *address) +{ + gpointer value; + GPid pid; + gchar buf[512]; + + g_assert (address != NULL); + g_assert (session_bus_address_to_pid != NULL); + + value = g_hash_table_lookup (session_bus_address_to_pid, address); + g_assert (value != NULL); + + pid = GPOINTER_TO_UINT (g_hash_table_lookup (session_bus_address_to_pid, address)); + + kill (pid, SIGTERM); + + /* write the pid to the child so it won't kill it when we die */ + g_snprintf (buf, sizeof buf, "remove %d\n", (guint) pid); + write (pipe_fds[1], buf, strlen (buf)); + + g_hash_table_remove (session_bus_address_to_pid, address); +} + +static gchar *temporary_address = NULL; +static gchar *temporary_address_used_by_bus = NULL; + +const gchar * +session_bus_get_temporary_address (void) +{ + if (temporary_address == NULL) + { + /* TODO: maybe use a more random name etc etc */ + temporary_address = g_strdup_printf ("unix:path=/tmp/g-dbus-tests-pid-%d", getpid ()); + } + + return temporary_address; +} + +const gchar * +session_bus_up (void) +{ + if (temporary_address_used_by_bus != NULL) + { + g_warning ("There is already a session bus up"); + goto out; + } + + temporary_address_used_by_bus = g_strdup (session_bus_up_with_address (session_bus_get_temporary_address ())); + + out: + return temporary_address_used_by_bus; +} + +void +session_bus_down (void) +{ + if (temporary_address_used_by_bus == NULL) + { + g_warning ("There is not a session bus up"); + } + else + { + session_bus_down_with_address (temporary_address_used_by_bus); + g_free (temporary_address_used_by_bus); + temporary_address_used_by_bus = NULL; + } +} diff --git a/gdbus/tests/gdbus-sessionbus.h b/gdbus/tests/gdbus-sessionbus.h new file mode 100644 index 0000000..38229a6 --- /dev/null +++ b/gdbus/tests/gdbus-sessionbus.h @@ -0,0 +1,38 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#ifndef __SESSION_BUS_H__ +#define __SESSION_BUS_H__ + +#include + +G_BEGIN_DECLS + +const gchar *session_bus_up_with_address (const gchar *given_address); +void session_bus_down_with_address (const gchar *address); +const gchar *session_bus_get_temporary_address (void); +const gchar *session_bus_up (void); +void session_bus_down (void); + +G_END_DECLS + +#endif /* __SESSION_BUS_H__ */ diff --git a/gdbus/tests/gdbus-tests.c b/gdbus/tests/gdbus-tests.c new file mode 100644 index 0000000..3b127e2 --- /dev/null +++ b/gdbus/tests/gdbus-tests.c @@ -0,0 +1,218 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include + +#include "gdbus-tests.h" + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean timed_out; +} PropertyNotifyData; + +static void +on_property_notify (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + PropertyNotifyData *data = user_data; + g_main_loop_quit (data->loop); +} + +static gboolean +on_property_notify_timeout (gpointer user_data) +{ + PropertyNotifyData *data = user_data; + data->timed_out = TRUE; + g_main_loop_quit (data->loop); + return TRUE; +} + +gboolean +_g_assert_property_notify_run (gpointer object, + const gchar *property_name) +{ + gchar *s; + gulong handler_id; + guint timeout_id; + PropertyNotifyData data; + + data.loop = g_main_loop_new (NULL, FALSE); + data.timed_out = FALSE; + s = g_strdup_printf ("notify::%s", property_name); + handler_id = g_signal_connect (object, + s, + G_CALLBACK (on_property_notify), + &data); + g_free (s); + timeout_id = g_timeout_add (5 * 1000, + on_property_notify_timeout, + &data); + g_main_loop_run (data.loop); + g_signal_handler_disconnect (object, handler_id); + g_source_remove (timeout_id); + g_main_loop_unref (data.loop); + + return data.timed_out; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean timed_out; +} SignalReceivedData; + +static void +on_signal_received (gpointer user_data) +{ + SignalReceivedData *data = user_data; + g_main_loop_quit (data->loop); +} + +static gboolean +on_signal_received_timeout (gpointer user_data) +{ + SignalReceivedData *data = user_data; + data->timed_out = TRUE; + g_main_loop_quit (data->loop); + return TRUE; +} + +gboolean +_g_assert_signal_received_run (gpointer object, + const gchar *signal_name) +{ + gulong handler_id; + guint timeout_id; + SignalReceivedData data; + + data.loop = g_main_loop_new (NULL, FALSE); + data.timed_out = FALSE; + handler_id = g_signal_connect_swapped (object, + signal_name, + G_CALLBACK (on_signal_received), + &data); + timeout_id = g_timeout_add (5 * 1000, + on_signal_received_timeout, + &data); + g_main_loop_run (data.loop); + g_signal_handler_disconnect (object, handler_id); + g_source_remove (timeout_id); + g_main_loop_unref (data.loop); + + return data.timed_out; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +GDBusConnection * +_g_bus_get_priv (GBusType bus_type, + GCancellable *cancellable, + GError **error) +{ + gchar *address; + GDBusConnection *ret; + + ret = NULL; + + address = g_dbus_address_get_for_bus_sync (bus_type, cancellable, error); + if (address == NULL) + goto out; + + ret = g_dbus_connection_new_for_address_sync (address, + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + cancellable, + error); + g_free (address); + + out: + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GMainLoop *loop; + gboolean timed_out; +} WaitSingleRefData; + +static gboolean +on_wait_single_ref_timeout (gpointer user_data) +{ + WaitSingleRefData *data = user_data; + data->timed_out = TRUE; + g_main_loop_quit (data->loop); + return TRUE; +} + +static void +on_wait_for_single_ref_toggled (gpointer user_data, + GObject *object, + gboolean is_last_ref) +{ + WaitSingleRefData *data = user_data; + g_main_loop_quit (data->loop); +} + +gboolean +_g_object_wait_for_single_ref_do (gpointer object) +{ + WaitSingleRefData data; + guint timeout_id; + + data.timed_out = FALSE; + + if (G_OBJECT (object)->ref_count == 1) + goto out; + + data.loop = g_main_loop_new (NULL, FALSE); + timeout_id = g_timeout_add (5 * 1000, + on_wait_single_ref_timeout, + &data); + + g_object_add_toggle_ref (G_OBJECT (object), + on_wait_for_single_ref_toggled, + &data); + g_object_unref (object); + + g_main_loop_run (data.loop); + + g_object_ref (object); + g_object_remove_toggle_ref (object, + on_wait_for_single_ref_toggled, + &data); + + g_source_remove (timeout_id); + g_main_loop_unref (data.loop); + out: + return data.timed_out; +} + +/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gdbus/tests/gdbus-tests.h b/gdbus/tests/gdbus-tests.h new file mode 100644 index 0000000..3d7640f --- /dev/null +++ b/gdbus/tests/gdbus-tests.h @@ -0,0 +1,146 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#ifndef __TESTS_H__ +#define __TESTS_H__ + +#include +#include "gdbus-sessionbus.h" + +G_BEGIN_DECLS + +/* TODO: clean up and move to gtestutils.c + * + * This is needed because libdbus-1 does not give predictable error messages - e.g. you + * get a different error message on connecting to a bus if the socket file is there vs + * if the socket file is missing. + */ + +#define _g_assert_error_domain(err, dom) do { if (!err || (err)->domain != dom) \ + g_assertion_message_error (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ + #err, err, dom, -1); } while (0) + +#define _g_assert_property_notify(object, property_name) \ + do \ + { \ + if (!G_IS_OBJECT (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Not a GObject instance"); \ + } \ + if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), \ + property_name) == NULL) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Property " property_name " does not " \ + "exist on object"); \ + } \ + if (_g_assert_property_notify_run (object, property_name)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Timed out waiting for notification " \ + "on property " property_name); \ + } \ + } \ + while (FALSE) + +#define _g_assert_signal_received(object, signal_name) \ + do \ + { \ + if (!G_IS_OBJECT (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Not a GObject instance"); \ + } \ + if (g_signal_lookup (signal_name, \ + G_TYPE_FROM_INSTANCE (object)) == 0) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Signal `" signal_name "' does not " \ + "exist on object"); \ + } \ + if (_g_assert_signal_received_run (object, signal_name)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Timed out waiting for signal `" \ + signal_name "'"); \ + } \ + } \ + while (FALSE) + +gboolean _g_assert_property_notify_run (gpointer object, + const gchar *property_name); + + +gboolean _g_assert_signal_received_run (gpointer object, + const gchar *signal_name); + +GDBusConnection *_g_bus_get_priv (GBusType bus_type, + GCancellable *cancellable, + GError **error); + + +#define _g_object_wait_for_single_ref(object) \ + do \ + { \ + if (!G_IS_OBJECT (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Not a GObject instance"); \ + } \ + if (_g_object_wait_for_single_ref_do (object)) \ + { \ + g_assertion_message (G_LOG_DOMAIN, \ + __FILE__, \ + __LINE__, \ + G_STRFUNC, \ + "Timed out waiting for single ref"); \ + } \ + } \ + while (FALSE) + +gboolean _g_object_wait_for_single_ref_do (gpointer object); + +G_END_DECLS + +#endif /* __TESTS_H__ */ diff --git a/gdbus/tests/gdbus-threading.c b/gdbus/tests/gdbus-threading.c new file mode 100644 index 0000000..2c08567 --- /dev/null +++ b/gdbus/tests/gdbus-threading.c @@ -0,0 +1,532 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a global connection */ +static GDBusConnection *c = NULL; + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ +/* Ensure that signal and method replies are delivered in the right thread */ +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct { + GThread *thread; + GMainLoop *thread_loop; + guint signal_count; +} DeliveryData; + +static void +msg_cb_expect_success (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + DeliveryData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_variant_unref (result); + + g_assert (g_thread_self () == data->thread); + + g_main_loop_quit (data->thread_loop); +} + +static void +msg_cb_expect_error_cancelled (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + DeliveryData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_invoke_method_finish (connection, + res, + &error); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); + g_assert (!g_dbus_error_is_remote_error (error)); + g_error_free (error); + g_assert (result == NULL); + + g_assert (g_thread_self () == data->thread); + + g_main_loop_quit (data->thread_loop); +} + +static void +signal_handler (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + DeliveryData *data = user_data; + + g_assert (g_thread_self () == data->thread); + + data->signal_count++; + + g_main_loop_quit (data->thread_loop); +} + +static gpointer +test_delivery_in_thread_func (gpointer _data) +{ + GMainLoop *thread_loop; + GMainContext *thread_context; + DeliveryData data; + GCancellable *ca; + guint subscription_id; + GDBusConnection *priv_c; + GError *error; + + error = NULL; + + thread_context = g_main_context_new (); + thread_loop = g_main_loop_new (thread_context, FALSE); + g_main_context_push_thread_default (thread_context); + + data.thread = g_thread_self (); + data.thread_loop = thread_loop; + data.signal_count = 0; + + /* ---------------------------------------------------------------------------------------------------- */ + + /* + * Check that we get a reply to the GetId() method call. + */ + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) msg_cb_expect_success, + &data); + g_main_loop_run (thread_loop); + + /* + * Check that we never actually send a message if the GCancellable + * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED + * when the actual connection is not up. + */ + ca = g_cancellable_new (); + g_cancellable_cancel (ca); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + &data); + g_main_loop_run (thread_loop); + g_object_unref (ca); + + /* + * Check that cancellation works when the message is already in flight. + */ + ca = g_cancellable_new (); + g_dbus_connection_invoke_method (c, + "org.freedesktop.DBus", /* bus_name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface name */ + "GetId", /* method name */ + NULL, + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + ca, + (GAsyncReadyCallback) msg_cb_expect_error_cancelled, + &data); + g_cancellable_cancel (ca); + g_main_loop_run (thread_loop); + g_object_unref (ca); + + /* + * Check that signals are delivered to the correct thread. + * + * First we subscribe to the signal, then we create a a private + * connection. This should cause a NameOwnerChanged message from + * the message bus. + */ + subscription_id = g_dbus_connection_signal_subscribe (c, + "org.freedesktop.DBus", /* sender */ + "org.freedesktop.DBus", /* interface */ + "NameOwnerChanged", /* member */ + "/org/freedesktop/DBus", /* path */ + NULL, + signal_handler, + &data, + NULL); + g_assert (subscription_id != 0); + g_assert (data.signal_count == 0); + + priv_c = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (priv_c != NULL); + + g_main_loop_run (thread_loop); + g_assert (data.signal_count == 1); + + g_object_unref (priv_c); + + g_dbus_connection_signal_unsubscribe (c, subscription_id); + + /* ---------------------------------------------------------------------------------------------------- */ + + g_main_context_pop_thread_default (thread_context); + g_main_loop_unref (thread_loop); + g_main_context_unref (thread_context); + + g_main_loop_quit (loop); + + return NULL; +} + +static void +test_delivery_in_thread (void) +{ + GError *error; + GThread *thread; + + error = NULL; + thread = g_thread_create (test_delivery_in_thread_func, + NULL, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread != NULL); + + /* run the event loop - it is needed to dispatch D-Bus messages */ + g_main_loop_run (loop); + + g_thread_join (thread); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct { + GDBusProxy *proxy; + gint msec; + guint num; + gboolean async; + + GMainLoop *thread_loop; + GThread *thread; + + gboolean done; +} SyncThreadData; + +static void +sleep_cb (GDBusProxy *proxy, + GAsyncResult *res, + gpointer user_data) +{ + SyncThreadData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_proxy_invoke_method_finish (proxy, + res, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + + g_assert (data->thread == g_thread_self ()); + + g_main_loop_quit (data->thread_loop); + + //g_debug ("async cb (%p)", g_thread_self ()); +} + +static gpointer +test_sleep_in_thread_func (gpointer _data) +{ + SyncThreadData *data = _data; + GMainContext *thread_context; + guint n; + + thread_context = g_main_context_new (); + data->thread_loop = g_main_loop_new (thread_context, FALSE); + g_main_context_push_thread_default (thread_context); + + data->thread = g_thread_self (); + + for (n = 0; n < data->num; n++) + { + if (data->async) + { + //g_debug ("invoking async (%p)", g_thread_self ()); + g_dbus_proxy_invoke_method (data->proxy, + "Sleep", + g_variant_new ("(i)", data->msec), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + (GAsyncReadyCallback) sleep_cb, + data); + g_main_loop_run (data->thread_loop); + g_print ("A"); + //g_debug ("done invoking async (%p)", g_thread_self ()); + } + else + { + GError *error; + GVariant *result; + + error = NULL; + //g_debug ("invoking sync (%p)", g_thread_self ()); + result = g_dbus_proxy_invoke_method_sync (data->proxy, + "Sleep", + g_variant_new ("(i)", data->msec), + G_DBUS_INVOKE_METHOD_FLAGS_NONE, + -1, + NULL, + &error); + g_print ("S"); + //g_debug ("done invoking sync (%p)", g_thread_self ()); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + } + } + + g_main_context_pop_thread_default (thread_context); + g_main_loop_unref (data->thread_loop); + g_main_context_unref (thread_context); + + data->done = TRUE; + g_main_loop_quit (loop); + + return NULL; +} + +static void +on_proxy_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + GDBusProxy *proxy, + gpointer user_data) +{ + guint n; + + /* + * Check that multiple threads can do calls without interferring with + * each other. We do this by creating three threads that call the + * Sleep() method on the server (which handles it asynchronously, e.g. + * it won't block other requests) with different sleep durations and + * a number of times. We do this so each set of calls add up to 4000 + * milliseconds. + * + * We run this test twice - first with async calls in each thread, then + * again with sync calls + */ + + for (n = 0; n < 2; n++) + { + gboolean do_async; + GThread *thread1; + GThread *thread2; + GThread *thread3; + SyncThreadData data1; + SyncThreadData data2; + SyncThreadData data3; + GError *error; + GTimeVal start_time; + GTimeVal end_time; + guint elapsed_msec; + + error = NULL; + do_async = (n == 0); + + g_get_current_time (&start_time); + + data1.proxy = proxy; + data1.msec = 40; + data1.num = 100; + data1.async = do_async; + data1.done = FALSE; + thread1 = g_thread_create (test_sleep_in_thread_func, + &data1, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread1 != NULL); + + data2.proxy = proxy; + data2.msec = 20; + data2.num = 200; + data2.async = do_async; + data2.done = FALSE; + thread2 = g_thread_create (test_sleep_in_thread_func, + &data2, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread2 != NULL); + + data3.proxy = proxy; + data3.msec = 100; + data3.num = 40; + data3.async = do_async; + data3.done = FALSE; + thread3 = g_thread_create (test_sleep_in_thread_func, + &data3, + TRUE, + &error); + g_assert_no_error (error); + g_assert (thread3 != NULL); + + /* we handle messages in the main loop - threads will quit it when they are done */ + while (!(data1.done && data2.done && data3.done)) + g_main_loop_run (loop); + + g_thread_join (thread1); + g_thread_join (thread2); + g_thread_join (thread3); + + g_get_current_time (&end_time); + + elapsed_msec = ((end_time.tv_sec * G_USEC_PER_SEC + end_time.tv_usec) - + (start_time.tv_sec * G_USEC_PER_SEC + start_time.tv_usec)) / 1000; + + //g_debug ("Elapsed time for %s = %d msec", n == 0 ? "async" : "sync", elapsed_msec); + + /* elapsed_msec should be 4000 msec + change for overhead */ + g_assert_cmpint (elapsed_msec, >=, 4000); + g_assert_cmpint (elapsed_msec, <, 5000); + + g_print (" "); + } + + g_main_loop_quit (loop); +} + +static void +on_proxy_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ +} + +static void +test_method_calls_in_thread (void) +{ + guint watcher_id; + + watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, + "com.example.TestService", + G_BUS_NAME_WATCHER_FLAGS_NONE, + "/com/example/TestObject", + "com.example.Frob", + G_TYPE_DBUS_PROXY, + G_DBUS_PROXY_FLAGS_NONE, + on_proxy_appeared, + on_proxy_vanished, + NULL, + NULL); + + g_main_loop_run (loop); + + g_bus_unwatch_proxy (watcher_id); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + GError *error; + gint ret; + + g_type_init (); + g_thread_init (NULL); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + session_bus_up (); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async ("./testserver.py", NULL)); + + /* wait for the service to come up */ + usleep (500 * 1000); + + /* Create the connection in the main thread */ + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + g_test_add_func ("/gdbus/delivery-in-thread", test_delivery_in_thread); + g_test_add_func ("/gdbus/method-calls-in-thread", test_method_calls_in_thread); + + ret = g_test_run(); + + g_object_unref (c); + + /* tear down bus */ + session_bus_down (); + + return ret; +} diff --git a/gdbus/tests/introspection.c b/gdbus/tests/introspection.c deleted file mode 100644 index 5fe84ed..0000000 --- a/gdbus/tests/introspection.c +++ /dev/null @@ -1,169 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -#include "tests.h" - -/* all tests rely on a shared mainloop */ -static GMainLoop *loop = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test introspection parser */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -introspection_on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) -{ - GError *error; - const gchar *xml_data; - GDBusNodeInfo *node_info; - const GDBusInterfaceInfo *interface_info; - const GDBusMethodInfo *method_info; - const GDBusSignalInfo *signal_info; - GVariant *result; - - error = NULL; - - /* - * Invoke Introspect(), then parse the output. - */ - result = g_dbus_proxy_invoke_method_sync (proxy, - "org.freedesktop.DBus.Introspectable.Introspect", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_get (result, "(s)", &xml_data); - - node_info = g_dbus_node_info_new_for_xml (xml_data, &error); - g_assert_no_error (error); - g_assert (node_info != NULL); - - /* for now we only check a couple of things. TODO: check more things */ - - interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.NonExistantInterface"); - g_assert (interface_info == NULL); - - interface_info = g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.DBus.Introspectable"); - g_assert (interface_info != NULL); - method_info = g_dbus_interface_info_lookup_method (interface_info, "NonExistantMethod"); - g_assert (method_info == NULL); - method_info = g_dbus_interface_info_lookup_method (interface_info, "Introspect"); - g_assert (method_info != NULL); - g_assert (method_info->in_args == NULL); - g_assert (method_info->out_args != NULL); - g_assert (method_info->out_args[0] != NULL); - g_assert (method_info->out_args[1] == NULL); - g_assert_cmpstr (method_info->out_args[0]->signature, ==, "s"); - - interface_info = g_dbus_node_info_lookup_interface (node_info, "com.example.Frob"); - g_assert (interface_info != NULL); - signal_info = g_dbus_interface_info_lookup_signal (interface_info, "TestSignal"); - g_assert (signal_info != NULL); - g_assert (signal_info->args != NULL); - g_assert (signal_info->args[0] != NULL); - g_assert_cmpstr (signal_info->args[0]->signature, ==, "s"); - g_assert (signal_info->args[1] != NULL); - g_assert_cmpstr (signal_info->args[1]->signature, ==, "o"); - g_assert (signal_info->args[2] != NULL); - g_assert_cmpstr (signal_info->args[2]->signature, ==, "v"); - g_assert (signal_info->args[3] == NULL); - - g_dbus_node_info_unref (node_info); - g_variant_unref (result); - - g_main_loop_quit (loop); -} - -static void -introspection_on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ -} - -static void -test_introspection_parser (void) -{ - guint watcher_id; - - session_bus_up (); - - watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, - "com.example.TestService", - G_BUS_NAME_WATCHER_FLAGS_NONE, - "/com/example/TestObject", - "com.example.Frob", - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_NONE, - introspection_on_proxy_appeared, - introspection_on_proxy_vanished, - NULL, - NULL); - - /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return - * until one can connect to the bus but that's not how things work right now - */ - usleep (500 * 1000); - /* this is safe; testserver will exit once the bus goes away */ - g_assert (g_spawn_command_line_async ("./testserver.py", NULL)); - - g_main_loop_run (loop); - - g_bus_unwatch_proxy (watcher_id); - - /* tear down bus */ - session_bus_down (); -} - - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - g_type_init (); - g_test_init (&argc, &argv, NULL); - - /* all the tests rely on a shared main loop */ - loop = g_main_loop_new (NULL, FALSE); - - /* all the tests use a session bus with a well-known address that we can bring up and down - * using session_bus_up() and session_bus_down(). - */ - g_unsetenv ("DISPLAY"); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); - - g_test_add_func ("/gdbus/introspection-parser", test_introspection_parser); - return g_test_run(); -} diff --git a/gdbus/tests/names.c b/gdbus/tests/names.c deleted file mode 100644 index 29e8dbc..0000000 --- a/gdbus/tests/names.c +++ /dev/null @@ -1,749 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include - -#include "tests.h" - -/* all tests rely on a shared mainloop */ -static GMainLoop *loop; - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that g_bus_own_name() works correctly */ -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - GMainLoop *loop; - gboolean expect_null_connection; - guint num_bus_acquired; - guint num_acquired; - guint num_lost; - guint num_free_func; -} OwnNameData; - -static void -own_name_data_free_func (OwnNameData *data) -{ - data->num_free_func++; - g_main_loop_quit (loop); -} - -static void -bus_acquired_handler (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - OwnNameData *data = user_data; - g_dbus_connection_set_exit_on_close (connection, FALSE); - data->num_bus_acquired += 1; - g_main_loop_quit (loop); -} - -static void -name_acquired_handler (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - OwnNameData *data = user_data; - data->num_acquired += 1; - g_main_loop_quit (loop); -} - -static void -name_lost_handler (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - OwnNameData *data = user_data; - if (data->expect_null_connection) - { - g_assert (connection == NULL); - } - else - { - g_assert (connection != NULL); - g_dbus_connection_set_exit_on_close (connection, FALSE); - } - data->num_lost += 1; - g_main_loop_quit (loop); -} - -static void -test_bus_own_name (void) -{ - guint id; - guint id2; - OwnNameData data; - OwnNameData data2; - const gchar *name; - GDBusConnection *c; - GError *error; - gboolean name_has_owner_reply; - GDBusConnection *c2; - GVariant *result; - - error = NULL; - name = "org.gtk.GDBus.Name1"; - - /* - * First check that name_lost_handler() is invoked if there is no bus. - * - * Also make sure name_lost_handler() isn't invoked when unowning the name. - */ - data.num_bus_acquired = 0; - data.num_free_func = 0; - data.num_acquired = 0; - data.num_lost = 0; - data.expect_null_connection = TRUE; - id = g_bus_own_name (G_BUS_TYPE_SESSION, - name, - G_BUS_NAME_OWNER_FLAGS_NONE, - bus_acquired_handler, - name_acquired_handler, - name_lost_handler, - &data, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data.num_bus_acquired, ==, 0); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_bus_acquired, ==, 0); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 1); - g_bus_unown_name (id); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 1); - g_assert_cmpint (data.num_free_func, ==, 1); - - /* - * Bring up a bus, then own a name and check bus_acquired_handler() then name_acquired_handler() is invoked. - */ - session_bus_up (); - data.num_bus_acquired = 0; - data.num_acquired = 0; - data.num_lost = 0; - data.expect_null_connection = FALSE; - id = g_bus_own_name (G_BUS_TYPE_SESSION, - name, - G_BUS_NAME_OWNER_FLAGS_NONE, - bus_acquired_handler, - name_acquired_handler, - name_lost_handler, - &data, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data.num_bus_acquired, ==, 0); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_bus_acquired, ==, 1); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_bus_acquired, ==, 1); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_lost, ==, 0); - - /* - * Check that the name was actually acquired. - */ - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c != NULL); - g_assert (!g_dbus_connection_is_closed (c)); - result = g_dbus_connection_invoke_method_sync (c, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "NameHasOwner", /* method name */ - g_variant_new ("(s)", name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_get (result, "(b)", &name_has_owner_reply); - g_assert (name_has_owner_reply); - g_variant_unref (result); - - /* - * Stop owning the name - this should invoke our free func - */ - g_bus_unown_name (id); - g_assert_cmpint (data.num_free_func, ==, 2); - - /* - * Check that the name was actually released. - */ - result = g_dbus_connection_invoke_method_sync (c, - "org.freedesktop.DBus", /* bus name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "NameHasOwner", /* method name */ - g_variant_new ("(s)", name), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_get (result, "(b)", &name_has_owner_reply); - g_assert (!name_has_owner_reply); - g_variant_unref (result); - - /* - * Own the name again. - */ - data.num_bus_acquired = 0; - data.num_acquired = 0; - data.num_lost = 0; - data.expect_null_connection = FALSE; - id = g_bus_own_name (G_BUS_TYPE_SESSION, - name, - G_BUS_NAME_OWNER_FLAGS_NONE, - bus_acquired_handler, - name_acquired_handler, - name_lost_handler, - &data, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data.num_bus_acquired, ==, 0); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_bus_acquired, ==, 1); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_bus_acquired, ==, 1); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_lost, ==, 0); - - /* - * Try owning the name with another object on the same connection - this should - * fail because we already own the name. - */ - data2.num_free_func = 0; - data2.num_bus_acquired = 0; - data2.num_acquired = 0; - data2.num_lost = 0; - data2.expect_null_connection = FALSE; - id2 = g_bus_own_name (G_BUS_TYPE_SESSION, - name, - G_BUS_NAME_OWNER_FLAGS_NONE, - bus_acquired_handler, - name_acquired_handler, - name_lost_handler, - &data2, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data2.num_bus_acquired, ==, 1); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data2.num_bus_acquired, ==, 1); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_bus_unown_name (id2); - g_assert_cmpint (data2.num_bus_acquired, ==, 1); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_assert_cmpint (data2.num_free_func, ==, 1); - - /* - * Create a secondary (e.g. private) connection and try owning the name on that - * connection. This should fail both with and without _REPLACE because we - * didn't specify ALLOW_REPLACEMENT. - */ - c2 = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, NULL); - g_assert (c2 != NULL); - g_assert (!g_dbus_connection_is_closed (c2)); - /* first without _REPLACE */ - data2.num_bus_acquired = 0; - data2.num_acquired = 0; - data2.num_lost = 0; - data2.expect_null_connection = FALSE; - data2.num_free_func = 0; - id2 = g_bus_own_name_on_connection (c2, - name, - G_BUS_NAME_OWNER_FLAGS_NONE, - name_acquired_handler, - name_lost_handler, - &data2, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_bus_unown_name (id2); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_assert_cmpint (data2.num_free_func, ==, 1); - /* then with _REPLACE */ - data2.num_bus_acquired = 0; - data2.num_acquired = 0; - data2.num_lost = 0; - data2.expect_null_connection = FALSE; - data2.num_free_func = 0; - id2 = g_bus_own_name_on_connection (c2, - name, - G_BUS_NAME_OWNER_FLAGS_REPLACE, - name_acquired_handler, - name_lost_handler, - &data2, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_bus_unown_name (id2); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_assert_cmpint (data2.num_free_func, ==, 1); - - /* - * Stop owning the name and grab it again with _ALLOW_REPLACEMENT. - */ - data.expect_null_connection = FALSE; - g_bus_unown_name (id); - g_assert_cmpint (data.num_bus_acquired, ==, 1); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_free_func, ==, 3); - /* grab it again */ - data.num_bus_acquired = 0; - data.num_acquired = 0; - data.num_lost = 0; - data.expect_null_connection = FALSE; - id = g_bus_own_name (G_BUS_TYPE_SESSION, - name, - G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, - bus_acquired_handler, - name_acquired_handler, - name_lost_handler, - &data, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data.num_bus_acquired, ==, 0); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_bus_acquired, ==, 1); - g_assert_cmpint (data.num_acquired, ==, 0); - g_assert_cmpint (data.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_bus_acquired, ==, 1); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_lost, ==, 0); - - /* - * Now try to grab the name from the secondary connection. - * - */ - /* first without _REPLACE - this won't make us acquire the name */ - data2.num_bus_acquired = 0; - data2.num_acquired = 0; - data2.num_lost = 0; - data2.expect_null_connection = FALSE; - data2.num_free_func = 0; - id2 = g_bus_own_name_on_connection (c2, - name, - G_BUS_NAME_OWNER_FLAGS_NONE, - name_acquired_handler, - name_lost_handler, - &data2, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_bus_unown_name (id2); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 1); - g_assert_cmpint (data2.num_free_func, ==, 1); - /* then with _REPLACE - here we should acquire the name - e.g. owner should lose it - * and owner2 should acquire it */ - data2.num_bus_acquired = 0; - data2.num_acquired = 0; - data2.num_lost = 0; - data2.expect_null_connection = FALSE; - data2.num_free_func = 0; - id2 = g_bus_own_name_on_connection (c2, - name, - G_BUS_NAME_OWNER_FLAGS_REPLACE, - name_acquired_handler, - name_lost_handler, - &data2, - (GDestroyNotify) own_name_data_free_func); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_lost, ==, 0); - g_assert_cmpint (data2.num_acquired, ==, 0); - g_assert_cmpint (data2.num_lost, ==, 0); - /* wait for handlers for both owner and owner2 to fire */ - while (data.num_lost == 0 || data2.num_acquired == 0) - g_main_loop_run (loop); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_lost, ==, 1); - g_assert_cmpint (data2.num_acquired, ==, 1); - g_assert_cmpint (data2.num_lost, ==, 0); - g_assert_cmpint (data2.num_bus_acquired, ==, 0); - /* ok, make owner2 release the name - then wait for owner to automagically reacquire it */ - g_bus_unown_name (id2); - g_assert_cmpint (data2.num_free_func, ==, 1); - g_main_loop_run (loop); - g_assert_cmpint (data.num_acquired, ==, 2); - g_assert_cmpint (data.num_lost, ==, 1); - - /* - * Finally, nuke the bus and check name_lost_handler() is invoked. - * - */ - data.expect_null_connection = TRUE; - session_bus_down (); - while (data.num_lost != 2) - g_main_loop_run (loop); - g_assert_cmpint (data.num_acquired, ==, 2); - g_assert_cmpint (data.num_lost, ==, 2); - g_bus_unown_name (id); - g_assert_cmpint (data.num_free_func, ==, 4); - - g_object_unref (c); - g_object_unref (c2); -} - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that g_bus_watch_name() works correctly */ -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - gboolean expect_null_connection; - guint num_acquired; - guint num_lost; - guint num_appeared; - guint num_vanished; - guint num_free_func; -} WatchNameData; - -static void -watch_name_data_free_func (WatchNameData *data) -{ - data->num_free_func++; - g_main_loop_quit (loop); -} - -static void -w_bus_acquired_handler (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ -} - -static void -w_name_acquired_handler (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - WatchNameData *data = user_data; - data->num_acquired += 1; - g_main_loop_quit (loop); -} - -static void -w_name_lost_handler (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - WatchNameData *data = user_data; - data->num_lost += 1; - g_main_loop_quit (loop); -} - -static void -name_appeared_handler (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - gpointer user_data) -{ - WatchNameData *data = user_data; - if (data->expect_null_connection) - { - g_assert (connection == NULL); - } - else - { - g_assert (connection != NULL); - g_dbus_connection_set_exit_on_close (connection, FALSE); - } - data->num_appeared += 1; - g_main_loop_quit (loop); -} - -static void -name_vanished_handler (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - WatchNameData *data = user_data; - if (data->expect_null_connection) - { - g_assert (connection == NULL); - } - else - { - g_assert (connection != NULL); - g_dbus_connection_set_exit_on_close (connection, FALSE); - } - data->num_vanished += 1; - g_main_loop_quit (loop); -} - -static void -test_bus_watch_name (void) -{ - WatchNameData data; - guint id; - guint owner_id; - - /* - * First check that name_vanished_handler() is invoked if there is no bus. - * - * Also make sure name_vanished_handler() isn't invoked when unwatching the name. - */ - data.num_free_func = 0; - data.num_appeared = 0; - data.num_vanished = 0; - data.expect_null_connection = TRUE; - id = g_bus_watch_name (G_BUS_TYPE_SESSION, - "org.gtk.GDBus.Name1", - G_BUS_NAME_WATCHER_FLAGS_NONE, - name_appeared_handler, - name_vanished_handler, - &data, - (GDestroyNotify) watch_name_data_free_func); - g_assert_cmpint (data.num_appeared, ==, 0); - g_assert_cmpint (data.num_vanished, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_appeared, ==, 0); - g_assert_cmpint (data.num_vanished, ==, 1); - g_bus_unwatch_name (id); - g_assert_cmpint (data.num_appeared, ==, 0); - g_assert_cmpint (data.num_vanished, ==, 1); - g_assert_cmpint (data.num_free_func, ==, 1); - - /* - * Now bring up a bus, own a name, and then start watching it. - */ - session_bus_up (); - /* own the name */ - data.num_free_func = 0; - data.num_acquired = 0; - data.num_lost = 0; - data.expect_null_connection = FALSE; - owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, - "org.gtk.GDBus.Name1", - G_BUS_NAME_OWNER_FLAGS_NONE, - w_bus_acquired_handler, - w_name_acquired_handler, - w_name_lost_handler, - &data, - (GDestroyNotify) watch_name_data_free_func); - g_main_loop_run (loop); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_lost, ==, 0); - /* now watch the name */ - data.num_appeared = 0; - data.num_vanished = 0; - id = g_bus_watch_name (G_BUS_TYPE_SESSION, - "org.gtk.GDBus.Name1", - G_BUS_NAME_WATCHER_FLAGS_NONE, - name_appeared_handler, - name_vanished_handler, - &data, - (GDestroyNotify) watch_name_data_free_func); - g_assert_cmpint (data.num_appeared, ==, 0); - g_assert_cmpint (data.num_vanished, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_appeared, ==, 1); - g_assert_cmpint (data.num_vanished, ==, 0); - - /* - * Unwatch the name. - */ - g_bus_unwatch_name (id); - g_assert_cmpint (data.num_free_func, ==, 1); - - /* unown the name */ - g_bus_unown_name (owner_id); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_free_func, ==, 2); - - /* - * Create a watcher and then make a name be owned. - * - * This should trigger name_appeared_handler() ... - */ - /* watch the name */ - data.num_appeared = 0; - data.num_vanished = 0; - data.num_free_func = 0; - id = g_bus_watch_name (G_BUS_TYPE_SESSION, - "org.gtk.GDBus.Name1", - G_BUS_NAME_WATCHER_FLAGS_NONE, - name_appeared_handler, - name_vanished_handler, - &data, - (GDestroyNotify) watch_name_data_free_func); - g_assert_cmpint (data.num_appeared, ==, 0); - g_assert_cmpint (data.num_vanished, ==, 0); - g_main_loop_run (loop); - g_assert_cmpint (data.num_appeared, ==, 0); - g_assert_cmpint (data.num_vanished, ==, 1); - - /* own the name */ - data.num_acquired = 0; - data.num_lost = 0; - data.expect_null_connection = FALSE; - owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, - "org.gtk.GDBus.Name1", - G_BUS_NAME_OWNER_FLAGS_NONE, - w_bus_acquired_handler, - w_name_acquired_handler, - w_name_lost_handler, - &data, - (GDestroyNotify) watch_name_data_free_func); - while (data.num_acquired == 0 || data.num_appeared == 0) - g_main_loop_run (loop); - g_assert_cmpint (data.num_acquired, ==, 1); - g_assert_cmpint (data.num_lost, ==, 0); - g_assert_cmpint (data.num_appeared, ==, 1); - g_assert_cmpint (data.num_vanished, ==, 1); - - /* - * Nuke the bus and check that the name vanishes and is lost. - */ - data.expect_null_connection = TRUE; - session_bus_down (); - g_main_loop_run (loop); - g_assert_cmpint (data.num_lost, ==, 1); - g_assert_cmpint (data.num_vanished, ==, 2); - - g_bus_unwatch_name (id); - g_assert_cmpint (data.num_free_func, ==, 1); - - g_bus_unown_name (owner_id); - g_assert_cmpint (data.num_free_func, ==, 2); - -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -test_validate_names (void) -{ - guint n; - static const struct - { - gboolean name; - gboolean unique; - gboolean interface; - const gchar *string; - } names[] = { - { 1, 0, 1, "valid.well_known.name"}, - { 1, 0, 0, "valid.well-known.name"}, - { 1, 1, 0, ":valid.unique.name"}, - { 0, 0, 0, "invalid.5well_known.name"}, - { 0, 0, 0, "4invalid.5well_known.name"}, - { 1, 1, 0, ":4valid.5unique.name"}, - { 0, 0, 0, ""}, - { 1, 0, 1, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name1"}, /* 255 */ - { 0, 0, 0, "very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.very.long.name12"}, /* 256 - too long! */ - { 0, 0, 0, ".starts.with.a.dot"}, - { 0, 0, 0, "contains.invalid;.characters"}, - { 0, 0, 0, "contains.inva/lid.characters"}, - { 0, 0, 0, "contains.inva[lid.characters"}, - { 0, 0, 0, "contains.inva]lid.characters"}, - { 0, 0, 0, "contains.inva_æøå_lid.characters"}, - { 1, 1, 0, ":1.1"}, - }; - - for (n = 0; n < G_N_ELEMENTS (names); n++) - { - if (names[n].name) - g_assert (g_dbus_is_name (names[n].string)); - else - g_assert (!g_dbus_is_name (names[n].string)); - - if (names[n].unique) - g_assert (g_dbus_is_unique_name (names[n].string)); - else - g_assert (!g_dbus_is_unique_name (names[n].string)); - - if (names[n].interface) - g_assert (g_dbus_is_interface_name (names[n].string)); - else - g_assert (!g_dbus_is_interface_name (names[n].string)); - } -} - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - gint ret; - - g_type_init (); - g_test_init (&argc, &argv, NULL); - - loop = g_main_loop_new (NULL, FALSE); - - /* all the tests use a session bus with a well-known address that we can bring up and down - * using session_bus_up() and session_bus_down(). - */ - g_unsetenv ("DISPLAY"); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); - - g_test_add_func ("/gdbus/validate-names", test_validate_names); - g_test_add_func ("/gdbus/bus-own-name", test_bus_own_name); - g_test_add_func ("/gdbus/bus-watch-name", test_bus_watch_name); - - ret = g_test_run(); - - g_main_loop_unref (loop); - - return ret; -} diff --git a/gdbus/tests/peer.c b/gdbus/tests/peer.c deleted file mode 100644 index d8ddfb1..0000000 --- a/gdbus/tests/peer.c +++ /dev/null @@ -1,746 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -/* for open(2) */ -#include -#include -#include - -#include - -#include "tests.h" - - -#ifdef G_OS_UNIX -static gboolean is_unix = TRUE; -#else -static gboolean is_unix = FALSE; -#endif - -static gchar *test_guid = NULL; -static GMainLoop *service_loop = NULL; -static GDBusServer *server = NULL; -static GMainLoop *loop = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that peer-to-peer connections work */ -/* ---------------------------------------------------------------------------------------------------- */ - - -typedef struct -{ - gboolean accept_connection; - gint num_connection_attempts; - GPtrArray *current_connections; - guint num_method_calls; - gboolean signal_received; -} PeerData; - -static const gchar *test_interface_introspection_xml = - "" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; -static const GDBusInterfaceInfo *test_interface_introspection_data = NULL; - -static void -test_interface_method_call (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) -{ - PeerData *data = user_data; - - data->num_method_calls++; - - g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject"); - g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface"); - - if (g_strcmp0 (method_name, "HelloPeer") == 0) - { - const gchar *greeting; - gchar *response; - - g_variant_get (parameters, "(s)", &greeting); - - response = g_strdup_printf ("You greeted me with '%s'.", - greeting); - g_dbus_method_invocation_return_value (invocation, - g_variant_new ("(s)", response)); - g_free (response); - } - else if (g_strcmp0 (method_name, "EmitSignal") == 0) - { - GError *error; - - error = NULL; - g_dbus_connection_emit_signal (connection, - NULL, - "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", - "PeerSignal", - NULL, - &error); - g_assert_no_error (error); - g_dbus_method_invocation_return_value (invocation, NULL); - } - else if (g_strcmp0 (method_name, "OpenFile") == 0) - { - const gchar *path; - GDBusMessage *reply; - GError *error; - gint fd; - GUnixFDList *fd_list; - - g_variant_get (parameters, "(s)", &path); - - fd_list = g_unix_fd_list_new (); - - error = NULL; - - fd = open (path, O_RDONLY); - g_unix_fd_list_append (fd_list, fd, &error); - g_assert_no_error (error); - close (fd); - - reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation)); - g_dbus_message_set_unix_fd_list (reply, fd_list); - g_object_unref (invocation); - - error = NULL; - g_dbus_connection_send_message (connection, - reply, - NULL, /* out_serial */ - &error); - g_assert_no_error (error); - g_object_unref (reply); - } - else - { - g_assert_not_reached (); - } -} - -static GVariant * -test_interface_get_property (GDBusConnection *connection, - const gchar *sender, - const gchar *object_path, - const gchar *interface_name, - const gchar *property_name, - GError **error, - gpointer user_data) -{ - g_assert_cmpstr (object_path, ==, "/org/gtk/GDBus/PeerTestObject"); - g_assert_cmpstr (interface_name, ==, "org.gtk.GDBus.PeerTestInterface"); - g_assert_cmpstr (property_name, ==, "PeerProperty"); - - return g_variant_new_string ("ThePropertyValue"); -} - - -static const GDBusInterfaceVTable test_interface_vtable = -{ - test_interface_method_call, - test_interface_get_property, - NULL /* set_property */ -}; - -static void -on_proxy_signal_received (GDBusProxy *proxy, - gchar *sender_name, - gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - PeerData *data = user_data; - - data->signal_received = TRUE; - - g_assert (sender_name == NULL); - g_assert_cmpstr (signal_name, ==, "PeerSignal"); - g_main_loop_quit (loop); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -on_deny_authenticated_peer (GDBusAuthObserver *observer, - GIOStream *stream, - GCredentials *credentials, - gpointer user_data) -{ - PeerData *data = user_data; - gboolean deny_peer; - - data->num_connection_attempts++; - - deny_peer = FALSE; - if (!data->accept_connection) - { - deny_peer = TRUE; - g_main_loop_quit (loop); - } - - return deny_peer; -} - -/* Runs in thread we created GDBusServer in (since we didn't pass G_DBUS_SERVER_FLAGS_RUN_IN_THREAD) */ -static void -on_new_connection (GDBusServer *server, - GDBusConnection *connection, - gpointer user_data) -{ - PeerData *data = user_data; - GError *error; - guint reg_id; - - //g_print ("Client connected.\n" - // "Negotiated capabilities: unix-fd-passing=%d\n", - // g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); - - g_ptr_array_add (data->current_connections, g_object_ref (connection)); - - /* export object on the newly established connection */ - error = NULL; - reg_id = g_dbus_connection_register_object (connection, - "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", - test_interface_introspection_data, - &test_interface_vtable, - data, - NULL, /* GDestroyNotify for data */ - &error); - g_assert_no_error (error); - g_assert (reg_id > 0); - - g_main_loop_quit (loop); -} - -static gpointer -service_thread_func (gpointer user_data) -{ - PeerData *data = user_data; - GMainContext *service_context; - GDBusAuthObserver *observer; - GError *error; - - service_context = g_main_context_new (); - g_main_context_push_thread_default (service_context); - - error = NULL; - observer = g_dbus_auth_observer_new (); - server = g_dbus_server_new_sync (is_unix ? "unix:tmpdir=/tmp/gdbus-test-" : "nonce-tcp:", - G_DBUS_SERVER_FLAGS_NONE, - test_guid, - observer, - NULL, /* cancellable */ - &error); - g_assert_no_error (error); - - g_signal_connect (server, - "new-connection", - G_CALLBACK (on_new_connection), - data); - g_signal_connect (observer, - "deny-authenticated-peer", - G_CALLBACK (on_deny_authenticated_peer), - data); - g_object_unref (observer); - - g_dbus_server_start (server); - - service_loop = g_main_loop_new (service_context, FALSE); - g_main_loop_run (service_loop); - - g_main_context_pop_thread_default (service_context); - - g_main_loop_unref (service_loop); - g_main_context_unref (service_context); - - /* test code specifically unrefs the server - see below */ - g_assert (server == NULL); - - return NULL; -} - -#if 0 -static gboolean -on_incoming_connection (GSocketService *service, - GSocketConnection *socket_connection, - GObject *source_object, - gpointer user_data) -{ - PeerData *data = user_data; - - if (data->accept_connection) - { - GError *error; - guint reg_id; - GDBusConnection *connection; - - error = NULL; - connection = g_dbus_connection_new_sync (G_IO_STREAM (socket_connection), - test_guid, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, - NULL, /* cancellable */ - &error); - g_assert_no_error (error); - - g_ptr_array_add (data->current_connections, connection); - - /* export object on the newly established connection */ - error = NULL; - reg_id = g_dbus_connection_register_object (connection, - "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", - &test_interface_introspection_data, - &test_interface_vtable, - data, - NULL, /* GDestroyNotify for data */ - &error); - g_assert_no_error (error); - g_assert (reg_id > 0); - - } - else - { - /* don't do anything */ - } - - data->num_connection_attempts++; - - g_main_loop_quit (loop); - - /* stops other signal handlers from being invoked */ - return TRUE; -} - -static gpointer -service_thread_func (gpointer data) -{ - GMainContext *service_context; - gchar *socket_path; - GSocketAddress *address; - GError *error; - - service_context = g_main_context_new (); - g_main_context_push_thread_default (service_context); - - socket_path = g_strdup_printf ("/tmp/gdbus-test-pid-%d", getpid ()); - address = g_unix_socket_address_new (socket_path); - - service = g_socket_service_new (); - error = NULL; - g_socket_listener_add_address (G_SOCKET_LISTENER (service), - address, - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - NULL, /* source_object */ - NULL, /* effective_address */ - &error); - g_assert_no_error (error); - g_signal_connect (service, - "incoming", - G_CALLBACK (on_incoming_connection), - data); - g_socket_service_start (service); - - service_loop = g_main_loop_new (service_context, FALSE); - g_main_loop_run (service_loop); - - g_main_context_pop_thread_default (service_context); - - g_main_loop_unref (service_loop); - g_main_context_unref (service_context); - - g_object_unref (address); - g_free (socket_path); - return NULL; -} -#endif - -/* ---------------------------------------------------------------------------------------------------- */ - -#if 0 -static gboolean -check_connection (gpointer user_data) -{ - PeerData *data = user_data; - guint n; - - for (n = 0; n < data->current_connections->len; n++) - { - GDBusConnection *c; - GIOStream *stream; - - c = G_DBUS_CONNECTION (data->current_connections->pdata[n]); - stream = g_dbus_connection_get_stream (c); - - g_debug ("In check_connection for %d: connection %p, stream %p", n, c, stream); - g_debug ("closed = %d", g_io_stream_is_closed (stream)); - - GSocket *socket; - socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream)); - g_debug ("socket_closed = %d", g_socket_is_closed (socket)); - g_debug ("socket_condition_check = %d", g_socket_condition_check (socket, G_IO_IN|G_IO_OUT|G_IO_ERR|G_IO_HUP)); - - gchar buf[128]; - GError *error; - gssize num_read; - error = NULL; - num_read = g_input_stream_read (g_io_stream_get_input_stream (stream), - buf, - 128, - NULL, - &error); - if (num_read < 0) - { - g_debug ("error: %s", error->message); - g_error_free (error); - } - else - { - g_debug ("no error, read %d bytes", (gint) num_read); - } - } - - return FALSE; -} - -static gboolean -on_do_disconnect_in_idle (gpointer data) -{ - GDBusConnection *c = G_DBUS_CONNECTION (data); - g_debug ("GDC %p has ref_count %d", c, G_OBJECT (c)->ref_count); - g_dbus_connection_disconnect (c); - g_object_unref (c); - return FALSE; -} -#endif - -static void -test_peer (void) -{ - GDBusConnection *c; - GDBusConnection *c2; - GDBusProxy *proxy; - GError *error; - PeerData data; - GVariant *value; - GVariant *result; - const gchar *s; - GThread *service_thread; - - memset (&data, '\0', sizeof (PeerData)); - data.current_connections = g_ptr_array_new_with_free_func (g_object_unref); - - /* first try to connect when there is no server */ - error = NULL; - c = g_dbus_connection_new_for_address_sync (is_unix ? "unix:path=/tmp/gdbus-test-does-not-exist-pid" : - /* NOTE: Even if something is listening on port 12345 the connection - * will fail because the nonce file doesn't exist */ - "nonce-tcp:host=localhost,port=12345,noncefile=this-does-not-exist-gdbus", - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, - NULL, /* cancellable */ - &error); - _g_assert_error_domain (error, G_IO_ERROR); - g_assert (!g_dbus_error_is_remote_error (error)); - g_clear_error (&error); - g_assert (c == NULL); - - /* bring up a server - we run the server in a different thread to avoid deadlocks */ - error = NULL; - service_thread = g_thread_create (service_thread_func, - &data, - TRUE, - &error); - while (service_loop == NULL) - g_thread_yield (); - g_assert (server != NULL); - - /* bring up a connection and accept it */ - data.accept_connection = TRUE; - error = NULL; - c = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, - NULL, /* cancellable */ - &error); - g_assert_no_error (error); - g_assert (c != NULL); - while (data.current_connections->len < 1) - g_main_loop_run (loop); - g_assert_cmpint (data.current_connections->len, ==, 1); - g_assert_cmpint (data.num_connection_attempts, ==, 1); - //g_assert (g_dbus_connection_get_bus_type (c) == G_BUS_TYPE_NONE); - g_assert (g_dbus_connection_get_unique_name (c) == NULL); - g_assert_cmpstr (g_dbus_connection_get_guid (c), ==, test_guid); - - /* check that we create a proxy, read properties, receive signals and invoke - * the HelloPeer() method. Since the server runs in another thread it's fine - * to use synchronous blocking API here. - */ - error = NULL; - proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_NONE, - NULL, - NULL, /* bus_name */ - "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", - NULL, /* GCancellable */ - &error); - g_assert_no_error (error); - g_assert (proxy != NULL); - error = NULL; - value = g_dbus_proxy_get_cached_property (proxy, "PeerProperty", &error); - g_assert_no_error (error); - g_assert_cmpstr (g_variant_get_string (value, NULL), ==, "ThePropertyValue"); - - /* try invoking a method */ - error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloPeer", - g_variant_new ("(s)", "Hey Peer!"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - &error); - g_assert_no_error (error); - g_variant_get (result, "(s)", &s); - g_assert_cmpstr (s, ==, "You greeted me with 'Hey Peer!'."); - g_variant_unref (result); - g_assert_cmpint (data.num_method_calls, ==, 1); - - /* make the other peer emit a signal - catch it */ - g_signal_connect (proxy, - "g-signal", - G_CALLBACK (on_proxy_signal_received), - &data); - g_assert (!data.signal_received); - g_dbus_proxy_invoke_method (proxy, - "EmitSignal", - NULL, /* no arguments */ - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - NULL, /* GAsyncReadyCallback - we don't care about the result */ - NULL); /* user_data */ - g_main_loop_run (loop); - g_assert (data.signal_received); - g_assert_cmpint (data.num_method_calls, ==, 2); - - /* check for UNIX fd passing */ -#ifdef G_OS_UNIX - { - GDBusMessage *method_call_message; - GDBusMessage *method_reply_message; - GUnixFDList *fd_list; - gint fd; - gchar buf[1024]; - gssize len; - gchar *buf2; - gsize len2; - - method_call_message = g_dbus_message_new_method_call (NULL, /* name */ - "/org/gtk/GDBus/PeerTestObject", - "org.gtk.GDBus.PeerTestInterface", - "OpenFile"); - g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", "/etc/hosts")); - error = NULL; - method_reply_message = g_dbus_connection_send_message_with_reply_sync (c, - method_call_message, - -1, - NULL, /* out_serial */ - NULL, /* cancellable */ - &error); - g_assert_no_error (error); - g_assert (g_dbus_message_get_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN); - fd_list = g_dbus_message_get_unix_fd_list (method_reply_message); - g_assert (fd_list != NULL); - g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1); - error = NULL; - fd = g_unix_fd_list_get (fd_list, 0, &error); - g_assert_no_error (error); - g_object_unref (method_call_message); - g_object_unref (method_reply_message); - - memset (buf, '\0', sizeof (buf)); - len = read (fd, buf, sizeof (buf) - 1); - close (fd); - - error = NULL; - g_file_get_contents ("/etc/hosts", - &buf2, - &len2, - &error); - g_assert_no_error (error); - if (len2 > sizeof (buf)) - buf2[sizeof (buf)] = '\0'; - g_assert_cmpstr (buf, ==, buf2); - g_free (buf2); - } -#endif /* G_OS_UNIX */ - - - /* bring up a connection - don't accept it - this should fail - */ - data.accept_connection = FALSE; - error = NULL; - c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, - NULL, /* cancellable */ - &error); - _g_assert_error_domain (error, G_IO_ERROR); - g_assert (c2 == NULL); - -#if 0 - /* TODO: THIS TEST DOESN'T WORK YET */ - - /* bring up a connection - accept it.. then disconnect from the client side - check - * that the server side gets the disconnect signal. - */ - error = NULL; - data.accept_connection = TRUE; - c2 = g_dbus_connection_new_for_address_sync (g_dbus_server_get_client_address (server), - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, - NULL, /* cancellable */ - &error); - g_assert_no_error (error); - g_assert (c2 != NULL); - g_assert (!g_dbus_connection_get_is_disconnected (c2)); - while (data.num_connection_attempts < 3) - g_main_loop_run (loop); - g_assert_cmpint (data.current_connections->len, ==, 2); - g_assert_cmpint (data.num_connection_attempts, ==, 3); - g_assert (!g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); - g_idle_add (on_do_disconnect_in_idle, c2); - g_debug ("=================================================="); - g_debug ("=================================================="); - g_debug ("=================================================="); - g_debug ("waiting for disconnect on connection %p, stream %p", - data.current_connections->pdata[1], - g_dbus_connection_get_stream (data.current_connections->pdata[1])); - - g_timeout_add (2000, check_connection, &data); - //_g_assert_signal_received (G_DBUS_CONNECTION (data.current_connections->pdata[1]), "closed"); - g_main_loop_run (loop); - g_assert (g_dbus_connection_get_is_disconnected (G_DBUS_CONNECTION (data.current_connections->pdata[1]))); - g_ptr_array_set_size (data.current_connections, 1); /* remove disconnected connection object */ -#endif - - /* unref the server and stop listening for new connections - * - * This won't bring down the established connections - check that c is still connected - * by invoking a method - */ - //g_socket_service_stop (service); - //g_object_unref (service); - g_dbus_server_stop (server); - g_object_unref (server); - server = NULL; - - error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloPeer", - g_variant_new ("(s)", "Hey Again Peer!"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, /* GCancellable */ - &error); - g_assert_no_error (error); - g_variant_get (result, "(s)", &s); - g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'."); - g_variant_unref (result); - g_assert_cmpint (data.num_method_calls, ==, 4); - -#if 0 - /* TODO: THIS TEST DOESN'T WORK YET */ - - /* now disconnect from the server side - check that the client side gets the signal */ - g_assert_cmpint (data.current_connections->len, ==, 1); - g_assert (G_DBUS_CONNECTION (data.current_connections->pdata[0]) != c); - g_dbus_connection_disconnect (G_DBUS_CONNECTION (data.current_connections->pdata[0])); - if (!g_dbus_connection_get_is_disconnected (c)) - _g_assert_signal_received (c, "closed"); - g_assert (g_dbus_connection_get_is_disconnected (c)); -#endif - - g_object_unref (c); - g_ptr_array_unref (data.current_connections); - g_object_unref (proxy); - - g_main_loop_quit (service_loop); - g_thread_join (service_thread); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - gint ret; - GDBusNodeInfo *introspection_data = NULL; - - g_type_init (); - g_thread_init (NULL); - g_test_init (&argc, &argv, NULL); - - introspection_data = g_dbus_node_info_new_for_xml (test_interface_introspection_xml, NULL); - g_assert (introspection_data != NULL); - test_interface_introspection_data = introspection_data->interfaces[0]; - - test_guid = g_dbus_generate_guid (); - - /* all the tests rely on a shared main loop */ - loop = g_main_loop_new (NULL, FALSE); - - g_test_add_func ("/gdbus/peer-to-peer", test_peer); - - ret = g_test_run(); - - g_main_loop_unref (loop); - g_free (test_guid); - g_dbus_node_info_unref (introspection_data); - - return ret; -} diff --git a/gdbus/tests/proxy.c b/gdbus/tests/proxy.c deleted file mode 100644 index 6991ca0..0000000 --- a/gdbus/tests/proxy.c +++ /dev/null @@ -1,455 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -#include "tests.h" - -/* all tests rely on a shared mainloop */ -static GMainLoop *loop = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that the method aspects of GDBusProxy works */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -test_methods (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) -{ - GVariant *result; - GError *error; - const gchar *str; - gchar *dbus_error_name; - - /* check that we can invoke a method */ - error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloWorld", - g_variant_new ("(s)", "Hey"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_assert_cmpstr (g_variant_get_type_string (result), ==, "(s)"); - g_variant_get (result, "(s)", &str); - g_assert_cmpstr (str, ==, "You greeted me with 'Hey'. Thanks!"); - g_variant_unref (result); - - /* Check that we can completely recover the returned error */ - result = g_dbus_proxy_invoke_method_sync (proxy, - "HelloWorld", - g_variant_new ("(s)", "Yo"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); - g_assert (g_dbus_error_is_remote_error (error)); - g_assert (g_dbus_error_is_remote_error (error)); - g_assert (result == NULL); - dbus_error_name = g_dbus_error_get_remote_error (error); - g_assert_cmpstr (dbus_error_name, ==, "com.example.TestException"); - g_free (dbus_error_name); - g_assert (g_dbus_error_strip_remote_error (error)); - g_assert_cmpstr (error->message, ==, "Yo is not a proper greeting"); - g_clear_error (&error); - - /* Check that we get a timeout if the method handling is taking longer than timeout */ - error = NULL; - result = g_dbus_proxy_invoke_method_sync (proxy, - "Sleep", - g_variant_new ("(i)", 500 /* msec */), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - 100 /* msec */, - NULL, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); - g_assert (!g_dbus_error_is_remote_error (error)); - g_assert (result == NULL); - g_clear_error (&error); - - /* Check that proxy-default timeouts work. */ - g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, -1); - - /* the default timeout is 25000 msec so this should work */ - result = g_dbus_proxy_invoke_method_sync (proxy, - "Sleep", - g_variant_new ("(i)", 500 /* msec */), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, /* use proxy default (e.g. -1 -> e.g. 25000 msec) */ - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); - g_variant_unref (result); - - /* now set the proxy-default timeout to 250 msec and try the 500 msec call - this should FAIL */ - g_dbus_proxy_set_default_timeout (proxy, 250); - g_assert_cmpint (g_dbus_proxy_get_default_timeout (proxy), ==, 250); - result = g_dbus_proxy_invoke_method_sync (proxy, - "Sleep", - g_variant_new ("(i)", 500 /* msec */), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, /* use proxy default (e.g. 250 msec) */ - NULL, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT); - g_assert (!g_dbus_error_is_remote_error (error)); - g_assert (result == NULL); - g_clear_error (&error); - - /* clean up after ourselves */ - g_dbus_proxy_set_default_timeout (proxy, -1); -} - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that the property aspects of GDBusProxy works */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -test_properties (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) -{ - GError *error; - GVariant *variant; - GVariant *variant2; - GVariant *result; - - error = NULL; - - /* - * Check that we can read cached properties. - * - * No need to test all properties - GVariant has already been tested - */ - variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); - g_assert_no_error (error); - g_assert (variant != NULL); - g_assert_cmpint (g_variant_get_byte (variant), ==, 1); - g_variant_unref (variant); - variant = g_dbus_proxy_get_cached_property (proxy, "o", &error); - g_assert_no_error (error); - g_assert (variant != NULL); - g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "/some/path"); - g_variant_unref (variant); - - /* - * Now ask the service to change a property and check that #GDBusProxy::g-property-changed - * is received. Also check that the cache is updated. - */ - variant2 = g_variant_new_byte (42); - result = g_dbus_proxy_invoke_method_sync (proxy, - "FrobSetProperty", - g_variant_new ("(sv)", - "y", - variant2), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); - g_variant_unref (result); - _g_assert_signal_received (proxy, "g-properties-changed"); - variant = g_dbus_proxy_get_cached_property (proxy, "y", &error); - g_assert_no_error (error); - g_assert (variant != NULL); - g_assert_cmpint (g_variant_get_byte (variant), ==, 42); - g_variant_unref (variant); -} - -/* ---------------------------------------------------------------------------------------------------- */ -/* Test that the signal aspects of GDBusProxy works */ -/* ---------------------------------------------------------------------------------------------------- */ - -static void -test_proxy_signals_on_signal (GDBusProxy *proxy, - const gchar *sender_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - GString *s = user_data; - - g_assert_cmpstr (signal_name, ==, "TestSignal"); - g_assert_cmpstr (g_variant_get_type_string (parameters), ==, "(sov)"); - - g_variant_print_string (parameters, s, TRUE); -} - -typedef struct -{ - GMainLoop *internal_loop; - GString *s; -} TestSignalData; - -static void -test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy, - GAsyncResult *res, - gpointer user_data) -{ - TestSignalData *data = user_data; - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, - res, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); - g_variant_unref (result); - - /* check that the signal was recieved before we got the method result */ - g_assert (strlen (data->s->str) > 0); - - /* break out of the loop */ - g_main_loop_quit (data->internal_loop); -} - -static void -test_signals (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) -{ - GError *error; - GString *s; - gulong signal_handler_id; - TestSignalData data; - GVariant *result; - - error = NULL; - - /* - * Ask the service to emit a signal and check that we receive it. - * - * Note that blocking calls don't block in the mainloop so wait for the signal (which - * is dispatched before the method reply) - */ - s = g_string_new (NULL); - signal_handler_id = g_signal_connect (proxy, - "g-signal", - G_CALLBACK (test_proxy_signals_on_signal), - s); - - result = g_dbus_proxy_invoke_method_sync (proxy, - "EmitSignal", - g_variant_new ("(so)", - "Accept the next proposition you hear", - "/some/path"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); - g_variant_unref (result); - /* check that we haven't received the signal just yet */ - g_assert (strlen (s->str) == 0); - /* and now wait for the signal */ - _g_assert_signal_received (proxy, "g-signal"); - g_assert_cmpstr (s->str, - ==, - "('Accept the next proposition you hear .. in bed!', objectpath '/some/path/in/bed', <'a variant'>)"); - g_signal_handler_disconnect (proxy, signal_handler_id); - g_string_free (s, TRUE); - - /* - * Now do this async to check the signal is received before the method returns. - */ - s = g_string_new (NULL); - data.internal_loop = g_main_loop_new (NULL, FALSE); - data.s = s; - signal_handler_id = g_signal_connect (proxy, - "g-signal", - G_CALLBACK (test_proxy_signals_on_signal), - s); - g_dbus_proxy_invoke_method (proxy, - "EmitSignal", - g_variant_new ("(so)", - "You will make a great programmer", - "/some/other/path"), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) test_proxy_signals_on_emit_signal_cb, - &data); - g_main_loop_run (data.internal_loop); - g_main_loop_unref (data.internal_loop); - g_assert_cmpstr (s->str, - ==, - "('You will make a great programmer .. in bed!', objectpath '/some/other/path/in/bed', <'a variant'>)"); - g_signal_handler_disconnect (proxy, signal_handler_id); - g_string_free (s, TRUE); -} - -static void -test_bogus_method_return (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) -{ - GError *error = NULL; - GVariant *result; - - result = g_dbus_proxy_invoke_method_sync (proxy, - "PairReturn", - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT); - g_assert (result == NULL); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static const gchar *frob_dbus_interface_xml = - "" - " " - /* Deliberately different from testserver.py's definition */ - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - ""; -static GDBusInterfaceInfo *frob_dbus_interface_info; - -static void -on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) -{ - test_methods (connection, name, name_owner, proxy); - test_properties (connection, name, name_owner, proxy); - test_signals (connection, name, name_owner, proxy); - - /* Now repeat the method tests, with an expected interface set */ - g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info); - test_methods (connection, name, name_owner, proxy); - - /* And now one more test where we deliberately set the expected - * interface definition incorrectly - */ - test_bogus_method_return (connection, name, name_owner, proxy); - - g_main_loop_quit (loop); -} - -static void -on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ -} - -static void -test_proxy (void) -{ - guint watcher_id; - - session_bus_up (); - - /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return - * until one can connect to the bus but that's not how things work right now - */ - usleep (500 * 1000); - - watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, - "com.example.TestService", - G_BUS_NAME_WATCHER_FLAGS_NONE, - "/com/example/TestObject", - "com.example.Frob", - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_NONE, - on_proxy_appeared, - on_proxy_vanished, - NULL, - NULL); - - /* this is safe; testserver will exit once the bus goes away */ - g_assert (g_spawn_command_line_async ("./testserver.py", NULL)); - - g_main_loop_run (loop); - - g_bus_unwatch_proxy (watcher_id); - - /* tear down bus */ - session_bus_down (); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - gint ret; - GDBusNodeInfo *introspection_data = NULL; - - g_type_init (); - g_test_init (&argc, &argv, NULL); - - introspection_data = g_dbus_node_info_new_for_xml (frob_dbus_interface_xml, NULL); - g_assert (introspection_data != NULL); - frob_dbus_interface_info = introspection_data->interfaces[0]; - - /* all the tests rely on a shared main loop */ - loop = g_main_loop_new (NULL, FALSE); - - /* all the tests use a session bus with a well-known address that we can bring up and down - * using session_bus_up() and session_bus_down(). - */ - g_unsetenv ("DISPLAY"); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); - - g_test_add_func ("/gdbus/proxy", test_proxy); - - ret = g_test_run(); - - g_dbus_node_info_unref (introspection_data); - return ret; -} diff --git a/gdbus/tests/serialization.c b/gdbus/tests/serialization.c deleted file mode 100644 index b081992..0000000 --- a/gdbus/tests/serialization.c +++ /dev/null @@ -1,650 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include - -#include -#include -#include - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -hexdump (const guchar *str, gsize len) -{ - const guchar *data = (const guchar *) str; - guint n, m; - - for (n = 0; n < len; n += 16) - { - g_printerr ("%04x: ", n); - - for (m = n; m < n + 16; m++) - { - if (m > n && (m%4) == 0) - g_printerr (" "); - if (m < len) - g_printerr ("%02x ", data[m]); - else - g_printerr (" "); - } - - g_printerr (" "); - - for (m = n; m < len && m < n + 16; m++) - g_printerr ("%c", g_ascii_isprint (data[m]) ? data[m] : '.'); - - g_printerr ("\n"); - } -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gboolean -append_gv_to_dbus_iter (DBusMessageIter *iter, - GVariant *value, - GError **error) -{ - const GVariantType *type; - - type = g_variant_get_type (value); - if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) - { - dbus_bool_t v = g_variant_get_boolean (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_BYTE)) - { - guint8 v = g_variant_get_byte (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT16)) - { - gint16 v = g_variant_get_int16 (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT16)) - { - guint16 v = g_variant_get_uint16 (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) - { - gint32 v = g_variant_get_int32 (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) - { - guint32 v = g_variant_get_uint32 (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT64)) - { - gint64 v = g_variant_get_int64 (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT64)) - { - guint64 v = g_variant_get_uint64 (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE)) - { - gdouble v = g_variant_get_double (value); - dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) - { - const gchar *v = g_variant_get_string (value, NULL); - dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_OBJECT_PATH)) - { - const gchar *v = g_variant_get_string (value, NULL); - dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &v); - } - else if (g_variant_type_equal (type, G_VARIANT_TYPE_SIGNATURE)) - { - const gchar *v = g_variant_get_string (value, NULL); - dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &v); - } - else if (g_variant_type_is_variant (type)) - { - DBusMessageIter sub; - GVariant *child; - - child = g_variant_get_child_value (value, 0); - dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, - g_variant_get_type_string (child), - &sub); - if (!append_gv_to_dbus_iter (&sub, child, error)) - { - g_variant_unref (child); - goto fail; - } - dbus_message_iter_close_container (iter, &sub); - g_variant_unref (child); - } - else if (g_variant_type_is_array (type)) - { - DBusMessageIter dbus_iter; - const gchar *type_string; - GVariantIter gv_iter; - GVariant *item; - - type_string = g_variant_get_type_string (value); - type_string++; /* skip the 'a' */ - - dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, - type_string, &dbus_iter); - g_variant_iter_init (&gv_iter, value); - - while ((item = g_variant_iter_next_value (&gv_iter))) - { - if (!append_gv_to_dbus_iter (&dbus_iter, item, error)) - { - goto fail; - } - } - - dbus_message_iter_close_container (iter, &dbus_iter); - } - else if (g_variant_type_is_tuple (type)) - { - DBusMessageIter dbus_iter; - GVariantIter gv_iter; - GVariant *item; - - dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, - NULL, &dbus_iter); - g_variant_iter_init (&gv_iter, value); - - while ((item = g_variant_iter_next_value (&gv_iter))) - { - if (!append_gv_to_dbus_iter (&dbus_iter, item, error)) - goto fail; - } - - dbus_message_iter_close_container (iter, &dbus_iter); - } - else if (g_variant_type_is_dict_entry (type)) - { - DBusMessageIter dbus_iter; - GVariant *key, *val; - - dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, - NULL, &dbus_iter); - key = g_variant_get_child_value (value, 0); - if (!append_gv_to_dbus_iter (&dbus_iter, key, error)) - { - g_variant_unref (key); - goto fail; - } - g_variant_unref (key); - - val = g_variant_get_child_value (value, 1); - if (!append_gv_to_dbus_iter (&dbus_iter, val, error)) - { - g_variant_unref (val); - goto fail; - } - g_variant_unref (val); - - dbus_message_iter_close_container (iter, &dbus_iter); - } - else - { - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - "Error serializing GVariant with type-string `%s' to a D-Bus message", - g_variant_get_type_string (value)); - goto fail; - } - - return TRUE; - - fail: - return FALSE; -} - -static gboolean -append_gv_to_dbus_message (DBusMessage *message, - GVariant *value, - GError **error) -{ - gboolean ret; - guint n; - - ret = FALSE; - - if (value != NULL) - { - DBusMessageIter iter; - GVariantIter gv_iter; - GVariant *item; - - dbus_message_iter_init_append (message, &iter); - - g_variant_iter_init (&gv_iter, value); - n = 0; - while ((item = g_variant_iter_next_value (&gv_iter))) - { - if (!append_gv_to_dbus_iter (&iter, item, error)) - { - g_prefix_error (error, - "Error encoding in-arg %d: ", - n); - goto out; - } - n++; - } - } - - ret = TRUE; - - out: - return ret; -} - -static void -print_gv_dbus_message (GVariant *value) -{ - DBusMessage *message; - char *blob; - int blob_len; - GError *error; - - message = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL); - dbus_message_set_serial (message, 0x41); - dbus_message_set_path (message, "/foo/bar"); - dbus_message_set_member (message, "Member"); - - error = NULL; - if (!append_gv_to_dbus_message (message, value, &error)) - { - g_printerr ("Error printing GVariant as DBusMessage: %s", error->message); - g_error_free (error); - goto out; - } - - dbus_message_marshal (message, &blob, &blob_len); - g_printerr ("\n"); - hexdump ((guchar *) blob, blob_len); - out: - dbus_message_unref (message); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -dbus_1_message_append (GString *s, - guint indent, - DBusMessageIter *iter) -{ - gint arg_type; - DBusMessageIter sub; - - g_string_append_printf (s, "%*s", indent, ""); - - arg_type = dbus_message_iter_get_arg_type (iter); - switch (arg_type) - { - case DBUS_TYPE_BOOLEAN: - { - dbus_bool_t value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "bool: %s\n", value ? "true" : "false"); - break; - } - - case DBUS_TYPE_BYTE: - { - guchar value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "byte: 0x%02x\n", (guint) value); - break; - } - - case DBUS_TYPE_INT16: - { - gint16 value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "int16: %" G_GINT16_FORMAT "\n", value); - break; - } - - case DBUS_TYPE_UINT16: - { - guint16 value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "uint16: %" G_GUINT16_FORMAT "\n", value); - break; - } - - case DBUS_TYPE_INT32: - { - gint32 value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "int32: %" G_GINT32_FORMAT "\n", value); - break; - } - - case DBUS_TYPE_UINT32: - { - guint32 value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "uint32: %" G_GUINT32_FORMAT "\n", value); - break; - } - - case DBUS_TYPE_INT64: - { - gint64 value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "int64: %" G_GINT64_FORMAT "\n", value); - break; - } - - case DBUS_TYPE_UINT64: - { - guint64 value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "uint64: %" G_GUINT64_FORMAT "\n", value); - break; - } - - case DBUS_TYPE_DOUBLE: - { - gdouble value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "double: %f\n", value); - break; - } - - case DBUS_TYPE_STRING: - { - const gchar *value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "string: `%s'\n", value); - break; - } - - case DBUS_TYPE_OBJECT_PATH: - { - const gchar *value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "object_path: `%s'\n", value); - break; - } - - case DBUS_TYPE_SIGNATURE: - { - const gchar *value; - dbus_message_iter_get_basic (iter, &value); - g_string_append_printf (s, "signature: `%s'\n", value); - break; - } - - case DBUS_TYPE_VARIANT: - g_string_append_printf (s, "variant:\n"); - dbus_message_iter_recurse (iter, &sub); - while (dbus_message_iter_get_arg_type (&sub)) - { - dbus_1_message_append (s, indent + 2, &sub); - dbus_message_iter_next (&sub); - } - break; - - case DBUS_TYPE_ARRAY: - g_string_append_printf (s, "array:\n"); - dbus_message_iter_recurse (iter, &sub); - while (dbus_message_iter_get_arg_type (&sub)) - { - dbus_1_message_append (s, indent + 2, &sub); - dbus_message_iter_next (&sub); - } - break; - - case DBUS_TYPE_STRUCT: - g_string_append_printf (s, "struct:\n"); - dbus_message_iter_recurse (iter, &sub); - while (dbus_message_iter_get_arg_type (&sub)) - { - dbus_1_message_append (s, indent + 2, &sub); - dbus_message_iter_next (&sub); - } - break; - - case DBUS_TYPE_DICT_ENTRY: - g_string_append_printf (s, "dict_entry:\n"); - dbus_message_iter_recurse (iter, &sub); - while (dbus_message_iter_get_arg_type (&sub)) - { - dbus_1_message_append (s, indent + 2, &sub); - dbus_message_iter_next (&sub); - } - break; - - default: - g_printerr ("Error serializing D-Bus message to GVariant. Unsupported arg type `%c' (%d)", - arg_type, - arg_type); - g_assert_not_reached (); - break; - } -} - -static gchar * -dbus_1_message_print (DBusMessage *message) -{ - GString *s; - guint n; - DBusMessageIter iter; - - s = g_string_new (NULL); - n = 0; - dbus_message_iter_init (message, &iter); - while (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_INVALID) - { - g_string_append_printf (s, "value %d: ", n); - dbus_1_message_append (s, 2, &iter); - dbus_message_iter_next (&iter); - n++; - } - - return g_string_free (s, FALSE); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static gchar * -get_body_signature (GVariant *value) -{ - const gchar *s; - gsize len; - gchar *ret; - - s = g_variant_get_type_string (value); - len = strlen (s); - g_assert (len>=2); - - ret = g_strndup (s + 1, len - 2); - - return ret; -} - -static void -check_serialization (GVariant *value, - const gchar *expected_dbus_1_output) -{ - guchar *blob; - gsize blob_size; - DBusMessage *dbus_1_message; - GDBusMessage *message; - GDBusMessage *recovered_message; - GError *error; - DBusError dbus_error; - gchar *s; - gchar *s1; - - message = g_dbus_message_new (); - g_dbus_message_set_body (message, value); - g_dbus_message_set_type (message, G_DBUS_MESSAGE_TYPE_METHOD_CALL); - g_dbus_message_set_serial (message, 0x41); - s = get_body_signature (value); - g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_PATH, g_variant_new_object_path ("/foo/bar")); - g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_MEMBER, g_variant_new_string ("Member")); - g_dbus_message_set_header (message, G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE, g_variant_new_signature (s)); - g_free (s); - - /* First check that the serialization to the D-Bus wire format is correct */ - - error = NULL; - blob = g_dbus_message_to_blob (message, - &blob_size, - &error); - g_assert_no_error (error); - g_assert (blob != NULL); - - dbus_error_init (&dbus_error); - dbus_1_message = dbus_message_demarshal ((char *) blob, blob_size, &dbus_error); - if (dbus_error_is_set (&dbus_error)) - { - g_printerr ("Error calling dbus_message_demarshal() on this blob: %s: %s\n", - dbus_error.name, - dbus_error.message); - hexdump (blob, blob_size); - dbus_error_free (&dbus_error); - - s = g_variant_print (value, TRUE); - g_printerr ("\nThe blob was generated from the following GVariant value:\n%s\n\n", s); - g_free (s); - - g_printerr ("If the blob was encoded using DBusMessageIter, the payload would have been:\n"); - print_gv_dbus_message (value); - - g_assert_not_reached (); - } - - s = dbus_1_message_print (dbus_1_message); - dbus_message_unref (dbus_1_message); - - g_assert_cmpstr (s, ==, expected_dbus_1_output); - g_free (s); - - /* Then serialize back and check that the body is identical */ - - error = NULL; - recovered_message = g_dbus_message_new_from_blob (blob, blob_size, &error); - g_assert_no_error (error); - g_assert (recovered_message != NULL); - g_assert (g_dbus_message_get_body (recovered_message) != NULL); - - if (!g_variant_equal (g_dbus_message_get_body (recovered_message), value)) - { - s = g_variant_print (g_dbus_message_get_body (recovered_message), TRUE); - s1 = g_variant_print (value, TRUE); - g_printerr ("Recovered value:\n%s\ndoes not match given value\n%s\n", - s, - s1); - g_free (s); - g_free (s1); - g_assert_not_reached (); - } - g_object_unref (message); - g_object_unref (recovered_message); -} - -static void -message_serialize_basic (void) -{ - check_serialization (g_variant_new ("(sogybnqiuxtd)", - "this is a string", - "/this/is/a/path", - "sad", - 42, - TRUE, - -42, - 60000, - -44, - 100000, - -G_GINT64_CONSTANT(2)<<34, - G_GUINT64_CONSTANT(0xffffffffffffffff), - 42.5), - "value 0: string: `this is a string'\n" - "value 1: object_path: `/this/is/a/path'\n" - "value 2: signature: `sad'\n" - "value 3: byte: 0x2a\n" - "value 4: bool: true\n" - "value 5: int16: -42\n" - "value 6: uint16: 60000\n" - "value 7: int32: -44\n" - "value 8: uint32: 100000\n" - "value 9: int64: -34359738368\n" - "value 10: uint64: 18446744073709551615\n" - "value 11: double: 42.500000\n"); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -message_serialize_complex (void) -{ - GError *error; - GVariant *value; - - error = NULL; - value = g_variant_parse (G_VARIANT_TYPE ("(aia{ss})"), - "([1, 2, 3], {'one': 'white', 'two': 'black'})", - NULL, NULL, &error); - g_assert_no_error (error); - g_assert (value != NULL); - check_serialization (value, - "value 0: array:\n" - " int32: 1\n" - " int32: 2\n" - " int32: 3\n" - "value 1: array:\n" - " dict_entry:\n" - " string: `one'\n" - " string: `white'\n" - " dict_entry:\n" - " string: `two'\n" - " string: `black'\n"); -} - - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - g_type_init (); - g_test_init (&argc, &argv, NULL); - - g_test_add_func ("/gdbus/message-serialize-basic", message_serialize_basic); - g_test_add_func ("/gdbus/message-serialize-complex", message_serialize_complex); - return g_test_run(); -} - diff --git a/gdbus/tests/sessionbus.c b/gdbus/tests/sessionbus.c deleted file mode 100644 index 6a99b94..0000000 --- a/gdbus/tests/sessionbus.c +++ /dev/null @@ -1,342 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "sessionbus.h" - -/* ---------------------------------------------------------------------------------------------------- */ -/* Utilities for bringing up and tearing down session message bus instances */ - -static void -watch_parent (gint fd) -{ - GPollFD fds[1]; - gint num_events; - gchar buf[512]; - guint bytes_read; - GArray *buses_to_kill_array; - - fds[0].fd = fd; - fds[0].events = G_IO_HUP | G_IO_IN; - fds[0].revents = 0; - - buses_to_kill_array = g_array_new (FALSE, TRUE, sizeof (guint)); - - do - { - guint pid; - guint n; - - num_events = g_poll (fds, 1, -1); - if (num_events == 0) - continue; - - if (fds[0].revents == G_IO_HUP) - { - for (n = 0; n < buses_to_kill_array->len; n++) - { - pid = g_array_index (buses_to_kill_array, guint, n); - g_print ("cleaning up bus with pid %d\n", pid); - kill (pid, SIGTERM); - } - g_array_free (buses_to_kill_array, TRUE); - exit (0); - } - - //g_debug ("data from parent"); - - memset (buf, '\0', sizeof buf); - again: - bytes_read = read (fds[0].fd, buf, sizeof buf); - if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR)) - goto again; - - if (sscanf (buf, "add %d\n", &pid) == 1) - { - g_array_append_val (buses_to_kill_array, pid); - } - else if (sscanf (buf, "remove %d\n", &pid) == 1) - { - for (n = 0; n < buses_to_kill_array->len; n++) - { - if (g_array_index (buses_to_kill_array, guint, n) == pid) - { - g_array_remove_index (buses_to_kill_array, n); - pid = 0; - break; - } - } - if (pid != 0) - { - g_warning ("unknown pid %d to remove", pid); - } - } - else - { - g_warning ("unknown command from parent '%s'", buf); - } - } - while (TRUE); - -} - -static GHashTable *session_bus_address_to_pid = NULL; -static gint pipe_fds[2]; - -const gchar * -session_bus_up_with_address (const gchar *given_address) -{ - gchar *address; - int stdout_fd; - GError *error; - gchar *argv[] = {"dbus-daemon", "--print-address", "--config-file=foo", NULL}; - GPid pid; - gchar buf[512]; - ssize_t bytes_read; - gchar *config_file_name; - gint config_file_fd; - GString *config_file_contents; - - address = NULL; - error = NULL; - config_file_name = NULL; - config_file_fd = -1; - argv[2] = NULL; - - config_file_fd = g_file_open_tmp ("g-dbus-tests-XXXXXX", - &config_file_name, - &error); - if (config_file_fd < 0) - { - g_warning ("Error creating temporary config file: %s", error->message); - g_error_free (error); - goto out; - } - - config_file_contents = g_string_new (NULL); - g_string_append (config_file_contents, "\n"); - g_string_append (config_file_contents, " session\n"); - g_string_append_printf (config_file_contents, " %s\n", given_address); - g_string_append (config_file_contents, - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n" - " \n"); - g_string_append (config_file_contents, "\n"); - - if (write (config_file_fd, config_file_contents->str, config_file_contents->len) != (gssize) config_file_contents->len) - { - g_warning ("Error writing %d bytes to config file: %m", (gint) config_file_contents->len); - g_string_free (config_file_contents, TRUE); - goto out; - } - g_string_free (config_file_contents, TRUE); - - argv[2] = g_strdup_printf ("--config-file=%s", config_file_name); - - if (session_bus_address_to_pid == NULL) - { - /* keep a mapping from session bus address to the pid */ - session_bus_address_to_pid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - /* fork a child to clean up session buses when we are killed */ - if (pipe (pipe_fds) != 0) - { - g_warning ("pipe() failed: %m"); - g_assert_not_reached (); - } - switch (fork ()) - { - case -1: - g_warning ("fork() failed: %m"); - g_assert_not_reached (); - break; - - case 0: - /* child */ - close (pipe_fds[1]); - watch_parent (pipe_fds[0]); - break; - - default: - /* parent */ - close (pipe_fds[0]); - break; - } - - //atexit (cleanup_session_buses); - /* TODO: need to handle the cases where we crash */ - } - else - { - /* check if we already have a bus running for this address */ - if (g_hash_table_lookup (session_bus_address_to_pid, given_address) != NULL) - { - g_warning ("Already have a bus instance for the given address %s", given_address); - goto out; - } - } - - if (!g_spawn_async_with_pipes (NULL, - argv, - NULL, - G_SPAWN_SEARCH_PATH, - NULL, - NULL, - &pid, - NULL, - &stdout_fd, - NULL, - &error)) - { - g_warning ("Error spawning dbus-daemon: %s", error->message); - g_error_free (error); - goto out; - } - - memset (buf, '\0', sizeof buf); - again: - bytes_read = read (stdout_fd, buf, sizeof buf); - if (bytes_read < 0 && (errno == EAGAIN || errno == EINTR)) - goto again; - close (stdout_fd); - - if (bytes_read == 0 || bytes_read == sizeof buf) - { - g_warning ("Error reading address from dbus daemon, %d bytes read", (gint) bytes_read); - kill (SIGTERM, pid); - goto out; - } - - address = g_strdup (buf); - g_strstrip (address); - - /* write the pid to the child so it can kill it when we die */ - g_snprintf (buf, sizeof buf, "add %d\n", (guint) pid); - write (pipe_fds[1], buf, strlen (buf)); - - /* start dbus-monitor */ - if (g_getenv ("G_DBUS_MONITOR") != NULL) - { - g_spawn_command_line_async ("dbus-monitor --session", NULL); - usleep (500 * 1000); - } - - g_hash_table_insert (session_bus_address_to_pid, address, GUINT_TO_POINTER (pid)); - - out: - if (config_file_fd > 0) - { - if (close (config_file_fd) != 0) - { - g_warning ("Error closing fd for config file %s: %m", config_file_name); - } - g_assert (config_file_name != NULL); - if (unlink (config_file_name) != 0) - { - g_warning ("Error unlinking config file %s: %m", config_file_name); - } - } - g_free (argv[2]); - g_free (config_file_name); - return address; -} - -void -session_bus_down_with_address (const gchar *address) -{ - gpointer value; - GPid pid; - gchar buf[512]; - - g_assert (address != NULL); - g_assert (session_bus_address_to_pid != NULL); - - value = g_hash_table_lookup (session_bus_address_to_pid, address); - g_assert (value != NULL); - - pid = GPOINTER_TO_UINT (g_hash_table_lookup (session_bus_address_to_pid, address)); - - kill (pid, SIGTERM); - - /* write the pid to the child so it won't kill it when we die */ - g_snprintf (buf, sizeof buf, "remove %d\n", (guint) pid); - write (pipe_fds[1], buf, strlen (buf)); - - g_hash_table_remove (session_bus_address_to_pid, address); -} - -static gchar *temporary_address = NULL; -static gchar *temporary_address_used_by_bus = NULL; - -const gchar * -session_bus_get_temporary_address (void) -{ - if (temporary_address == NULL) - { - /* TODO: maybe use a more random name etc etc */ - temporary_address = g_strdup_printf ("unix:path=/tmp/g-dbus-tests-pid-%d", getpid ()); - } - - return temporary_address; -} - -const gchar * -session_bus_up (void) -{ - if (temporary_address_used_by_bus != NULL) - { - g_warning ("There is already a session bus up"); - goto out; - } - - temporary_address_used_by_bus = g_strdup (session_bus_up_with_address (session_bus_get_temporary_address ())); - - out: - return temporary_address_used_by_bus; -} - -void -session_bus_down (void) -{ - if (temporary_address_used_by_bus == NULL) - { - g_warning ("There is not a session bus up"); - } - else - { - session_bus_down_with_address (temporary_address_used_by_bus); - g_free (temporary_address_used_by_bus); - temporary_address_used_by_bus = NULL; - } -} diff --git a/gdbus/tests/sessionbus.h b/gdbus/tests/sessionbus.h deleted file mode 100644 index 38229a6..0000000 --- a/gdbus/tests/sessionbus.h +++ /dev/null @@ -1,38 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#ifndef __SESSION_BUS_H__ -#define __SESSION_BUS_H__ - -#include - -G_BEGIN_DECLS - -const gchar *session_bus_up_with_address (const gchar *given_address); -void session_bus_down_with_address (const gchar *address); -const gchar *session_bus_get_temporary_address (void); -const gchar *session_bus_up (void); -void session_bus_down (void); - -G_END_DECLS - -#endif /* __SESSION_BUS_H__ */ diff --git a/gdbus/tests/tests.c b/gdbus/tests/tests.c deleted file mode 100644 index 46f3b3a..0000000 --- a/gdbus/tests/tests.c +++ /dev/null @@ -1,218 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include - -#include "tests.h" - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - GMainLoop *loop; - gboolean timed_out; -} PropertyNotifyData; - -static void -on_property_notify (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - PropertyNotifyData *data = user_data; - g_main_loop_quit (data->loop); -} - -static gboolean -on_property_notify_timeout (gpointer user_data) -{ - PropertyNotifyData *data = user_data; - data->timed_out = TRUE; - g_main_loop_quit (data->loop); - return TRUE; -} - -gboolean -_g_assert_property_notify_run (gpointer object, - const gchar *property_name) -{ - gchar *s; - gulong handler_id; - guint timeout_id; - PropertyNotifyData data; - - data.loop = g_main_loop_new (NULL, FALSE); - data.timed_out = FALSE; - s = g_strdup_printf ("notify::%s", property_name); - handler_id = g_signal_connect (object, - s, - G_CALLBACK (on_property_notify), - &data); - g_free (s); - timeout_id = g_timeout_add (5 * 1000, - on_property_notify_timeout, - &data); - g_main_loop_run (data.loop); - g_signal_handler_disconnect (object, handler_id); - g_source_remove (timeout_id); - g_main_loop_unref (data.loop); - - return data.timed_out; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - GMainLoop *loop; - gboolean timed_out; -} SignalReceivedData; - -static void -on_signal_received (gpointer user_data) -{ - SignalReceivedData *data = user_data; - g_main_loop_quit (data->loop); -} - -static gboolean -on_signal_received_timeout (gpointer user_data) -{ - SignalReceivedData *data = user_data; - data->timed_out = TRUE; - g_main_loop_quit (data->loop); - return TRUE; -} - -gboolean -_g_assert_signal_received_run (gpointer object, - const gchar *signal_name) -{ - gulong handler_id; - guint timeout_id; - SignalReceivedData data; - - data.loop = g_main_loop_new (NULL, FALSE); - data.timed_out = FALSE; - handler_id = g_signal_connect_swapped (object, - signal_name, - G_CALLBACK (on_signal_received), - &data); - timeout_id = g_timeout_add (5 * 1000, - on_signal_received_timeout, - &data); - g_main_loop_run (data.loop); - g_signal_handler_disconnect (object, handler_id); - g_source_remove (timeout_id); - g_main_loop_unref (data.loop); - - return data.timed_out; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -GDBusConnection * -_g_bus_get_priv (GBusType bus_type, - GCancellable *cancellable, - GError **error) -{ - gchar *address; - GDBusConnection *ret; - - ret = NULL; - - address = g_dbus_address_get_for_bus_sync (bus_type, cancellable, error); - if (address == NULL) - goto out; - - ret = g_dbus_connection_new_for_address_sync (address, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | - G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, - cancellable, - error); - g_free (address); - - out: - return ret; -} - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - GMainLoop *loop; - gboolean timed_out; -} WaitSingleRefData; - -static gboolean -on_wait_single_ref_timeout (gpointer user_data) -{ - WaitSingleRefData *data = user_data; - data->timed_out = TRUE; - g_main_loop_quit (data->loop); - return TRUE; -} - -static void -on_wait_for_single_ref_toggled (gpointer user_data, - GObject *object, - gboolean is_last_ref) -{ - WaitSingleRefData *data = user_data; - g_main_loop_quit (data->loop); -} - -gboolean -_g_object_wait_for_single_ref_do (gpointer object) -{ - WaitSingleRefData data; - guint timeout_id; - - data.timed_out = FALSE; - - if (G_OBJECT (object)->ref_count == 1) - goto out; - - data.loop = g_main_loop_new (NULL, FALSE); - timeout_id = g_timeout_add (5 * 1000, - on_wait_single_ref_timeout, - &data); - - g_object_add_toggle_ref (G_OBJECT (object), - on_wait_for_single_ref_toggled, - &data); - g_object_unref (object); - - g_main_loop_run (data.loop); - - g_object_ref (object); - g_object_remove_toggle_ref (object, - on_wait_for_single_ref_toggled, - &data); - - g_source_remove (timeout_id); - g_main_loop_unref (data.loop); - out: - return data.timed_out; -} - -/* ---------------------------------------------------------------------------------------------------- */ diff --git a/gdbus/tests/tests.h b/gdbus/tests/tests.h deleted file mode 100644 index 366efdc..0000000 --- a/gdbus/tests/tests.h +++ /dev/null @@ -1,146 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#ifndef __TESTS_H__ -#define __TESTS_H__ - -#include -#include "sessionbus.h" - -G_BEGIN_DECLS - -/* TODO: clean up and move to gtestutils.c - * - * This is needed because libdbus-1 does not give predictable error messages - e.g. you - * get a different error message on connecting to a bus if the socket file is there vs - * if the socket file is missing. - */ - -#define _g_assert_error_domain(err, dom) do { if (!err || (err)->domain != dom) \ - g_assertion_message_error (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - #err, err, dom, -1); } while (0) - -#define _g_assert_property_notify(object, property_name) \ - do \ - { \ - if (!G_IS_OBJECT (object)) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Not a GObject instance"); \ - } \ - if (g_object_class_find_property (G_OBJECT_GET_CLASS (object), \ - property_name) == NULL) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Property " property_name " does not " \ - "exist on object"); \ - } \ - if (_g_assert_property_notify_run (object, property_name)) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Timed out waiting for notification " \ - "on property " property_name); \ - } \ - } \ - while (FALSE) - -#define _g_assert_signal_received(object, signal_name) \ - do \ - { \ - if (!G_IS_OBJECT (object)) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Not a GObject instance"); \ - } \ - if (g_signal_lookup (signal_name, \ - G_TYPE_FROM_INSTANCE (object)) == 0) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Signal `" signal_name "' does not " \ - "exist on object"); \ - } \ - if (_g_assert_signal_received_run (object, signal_name)) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Timed out waiting for signal `" \ - signal_name "'"); \ - } \ - } \ - while (FALSE) - -gboolean _g_assert_property_notify_run (gpointer object, - const gchar *property_name); - - -gboolean _g_assert_signal_received_run (gpointer object, - const gchar *signal_name); - -GDBusConnection *_g_bus_get_priv (GBusType bus_type, - GCancellable *cancellable, - GError **error); - - -#define _g_object_wait_for_single_ref(object) \ - do \ - { \ - if (!G_IS_OBJECT (object)) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Not a GObject instance"); \ - } \ - if (_g_object_wait_for_single_ref_do (object)) \ - { \ - g_assertion_message (G_LOG_DOMAIN, \ - __FILE__, \ - __LINE__, \ - G_STRFUNC, \ - "Timed out waiting for single ref"); \ - } \ - } \ - while (FALSE) - -gboolean _g_object_wait_for_single_ref_do (gpointer object); - -G_END_DECLS - -#endif /* __TESTS_H__ */ diff --git a/gdbus/tests/threading.c b/gdbus/tests/threading.c deleted file mode 100644 index 6dd105c..0000000 --- a/gdbus/tests/threading.c +++ /dev/null @@ -1,532 +0,0 @@ -/* GLib testing framework examples and tests - * - * Copyright (C) 2008-2009 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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. - * - * Author: David Zeuthen - */ - -#include -#include -#include - -#include "tests.h" - -/* all tests rely on a global connection */ -static GDBusConnection *c = NULL; - -/* all tests rely on a shared mainloop */ -static GMainLoop *loop = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ -/* Ensure that signal and method replies are delivered in the right thread */ -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct { - GThread *thread; - GMainLoop *thread_loop; - guint signal_count; -} DeliveryData; - -static void -msg_cb_expect_success (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) -{ - DeliveryData *data = user_data; - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_variant_unref (result); - - g_assert (g_thread_self () == data->thread); - - g_main_loop_quit (data->thread_loop); -} - -static void -msg_cb_expect_error_cancelled (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) -{ - DeliveryData *data = user_data; - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_connection_invoke_method_finish (connection, - res, - &error); - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED); - g_assert (!g_dbus_error_is_remote_error (error)); - g_error_free (error); - g_assert (result == NULL); - - g_assert (g_thread_self () == data->thread); - - g_main_loop_quit (data->thread_loop); -} - -static void -signal_handler (GDBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - DeliveryData *data = user_data; - - g_assert (g_thread_self () == data->thread); - - data->signal_count++; - - g_main_loop_quit (data->thread_loop); -} - -static gpointer -test_delivery_in_thread_func (gpointer _data) -{ - GMainLoop *thread_loop; - GMainContext *thread_context; - DeliveryData data; - GCancellable *ca; - guint subscription_id; - GDBusConnection *priv_c; - GError *error; - - error = NULL; - - thread_context = g_main_context_new (); - thread_loop = g_main_loop_new (thread_context, FALSE); - g_main_context_push_thread_default (thread_context); - - data.thread = g_thread_self (); - data.thread_loop = thread_loop; - data.signal_count = 0; - - /* ---------------------------------------------------------------------------------------------------- */ - - /* - * Check that we get a reply to the GetId() method call. - */ - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) msg_cb_expect_success, - &data); - g_main_loop_run (thread_loop); - - /* - * Check that we never actually send a message if the GCancellable - * is already cancelled - i.e. we should get #G_IO_ERROR_CANCELLED - * when the actual connection is not up. - */ - ca = g_cancellable_new (); - g_cancellable_cancel (ca); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled, - &data); - g_main_loop_run (thread_loop); - g_object_unref (ca); - - /* - * Check that cancellation works when the message is already in flight. - */ - ca = g_cancellable_new (); - g_dbus_connection_invoke_method (c, - "org.freedesktop.DBus", /* bus_name */ - "/org/freedesktop/DBus", /* object path */ - "org.freedesktop.DBus", /* interface name */ - "GetId", /* method name */ - NULL, - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - ca, - (GAsyncReadyCallback) msg_cb_expect_error_cancelled, - &data); - g_cancellable_cancel (ca); - g_main_loop_run (thread_loop); - g_object_unref (ca); - - /* - * Check that signals are delivered to the correct thread. - * - * First we subscribe to the signal, then we create a a private - * connection. This should cause a NameOwnerChanged message from - * the message bus. - */ - subscription_id = g_dbus_connection_signal_subscribe (c, - "org.freedesktop.DBus", /* sender */ - "org.freedesktop.DBus", /* interface */ - "NameOwnerChanged", /* member */ - "/org/freedesktop/DBus", /* path */ - NULL, - signal_handler, - &data, - NULL); - g_assert (subscription_id != 0); - g_assert (data.signal_count == 0); - - priv_c = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (priv_c != NULL); - - g_main_loop_run (thread_loop); - g_assert (data.signal_count == 1); - - g_object_unref (priv_c); - - g_dbus_connection_signal_unsubscribe (c, subscription_id); - - /* ---------------------------------------------------------------------------------------------------- */ - - g_main_context_pop_thread_default (thread_context); - g_main_loop_unref (thread_loop); - g_main_context_unref (thread_context); - - g_main_loop_quit (loop); - - return NULL; -} - -static void -test_delivery_in_thread (void) -{ - GError *error; - GThread *thread; - - error = NULL; - thread = g_thread_create (test_delivery_in_thread_func, - NULL, - TRUE, - &error); - g_assert_no_error (error); - g_assert (thread != NULL); - - /* run the event loop - it is needed to dispatch D-Bus messages */ - g_main_loop_run (loop); - - g_thread_join (thread); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct { - GDBusProxy *proxy; - gint msec; - guint num; - gboolean async; - - GMainLoop *thread_loop; - GThread *thread; - - gboolean done; -} SyncThreadData; - -static void -sleep_cb (GDBusProxy *proxy, - GAsyncResult *res, - gpointer user_data) -{ - SyncThreadData *data = user_data; - GError *error; - GVariant *result; - - error = NULL; - result = g_dbus_proxy_invoke_method_finish (proxy, - res, - &error); - g_assert_no_error (error); - g_assert (result != NULL); - g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); - g_variant_unref (result); - - g_assert (data->thread == g_thread_self ()); - - g_main_loop_quit (data->thread_loop); - - //g_debug ("async cb (%p)", g_thread_self ()); -} - -static gpointer -test_sleep_in_thread_func (gpointer _data) -{ - SyncThreadData *data = _data; - GMainContext *thread_context; - guint n; - - thread_context = g_main_context_new (); - data->thread_loop = g_main_loop_new (thread_context, FALSE); - g_main_context_push_thread_default (thread_context); - - data->thread = g_thread_self (); - - for (n = 0; n < data->num; n++) - { - if (data->async) - { - //g_debug ("invoking async (%p)", g_thread_self ()); - g_dbus_proxy_invoke_method (data->proxy, - "Sleep", - g_variant_new ("(i)", data->msec), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - (GAsyncReadyCallback) sleep_cb, - data); - g_main_loop_run (data->thread_loop); - g_print ("A"); - //g_debug ("done invoking async (%p)", g_thread_self ()); - } - else - { - GError *error; - GVariant *result; - - error = NULL; - //g_debug ("invoking sync (%p)", g_thread_self ()); - result = g_dbus_proxy_invoke_method_sync (data->proxy, - "Sleep", - g_variant_new ("(i)", data->msec), - G_DBUS_INVOKE_METHOD_FLAGS_NONE, - -1, - NULL, - &error); - g_print ("S"); - //g_debug ("done invoking sync (%p)", g_thread_self ()); - g_assert_no_error (error); - g_assert (result != NULL); - g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); - g_variant_unref (result); - } - } - - g_main_context_pop_thread_default (thread_context); - g_main_loop_unref (data->thread_loop); - g_main_context_unref (thread_context); - - data->done = TRUE; - g_main_loop_quit (loop); - - return NULL; -} - -static void -on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) -{ - guint n; - - /* - * Check that multiple threads can do calls without interferring with - * each other. We do this by creating three threads that call the - * Sleep() method on the server (which handles it asynchronously, e.g. - * it won't block other requests) with different sleep durations and - * a number of times. We do this so each set of calls add up to 4000 - * milliseconds. - * - * We run this test twice - first with async calls in each thread, then - * again with sync calls - */ - - for (n = 0; n < 2; n++) - { - gboolean do_async; - GThread *thread1; - GThread *thread2; - GThread *thread3; - SyncThreadData data1; - SyncThreadData data2; - SyncThreadData data3; - GError *error; - GTimeVal start_time; - GTimeVal end_time; - guint elapsed_msec; - - error = NULL; - do_async = (n == 0); - - g_get_current_time (&start_time); - - data1.proxy = proxy; - data1.msec = 40; - data1.num = 100; - data1.async = do_async; - data1.done = FALSE; - thread1 = g_thread_create (test_sleep_in_thread_func, - &data1, - TRUE, - &error); - g_assert_no_error (error); - g_assert (thread1 != NULL); - - data2.proxy = proxy; - data2.msec = 20; - data2.num = 200; - data2.async = do_async; - data2.done = FALSE; - thread2 = g_thread_create (test_sleep_in_thread_func, - &data2, - TRUE, - &error); - g_assert_no_error (error); - g_assert (thread2 != NULL); - - data3.proxy = proxy; - data3.msec = 100; - data3.num = 40; - data3.async = do_async; - data3.done = FALSE; - thread3 = g_thread_create (test_sleep_in_thread_func, - &data3, - TRUE, - &error); - g_assert_no_error (error); - g_assert (thread3 != NULL); - - /* we handle messages in the main loop - threads will quit it when they are done */ - while (!(data1.done && data2.done && data3.done)) - g_main_loop_run (loop); - - g_thread_join (thread1); - g_thread_join (thread2); - g_thread_join (thread3); - - g_get_current_time (&end_time); - - elapsed_msec = ((end_time.tv_sec * G_USEC_PER_SEC + end_time.tv_usec) - - (start_time.tv_sec * G_USEC_PER_SEC + start_time.tv_usec)) / 1000; - - //g_debug ("Elapsed time for %s = %d msec", n == 0 ? "async" : "sync", elapsed_msec); - - /* elapsed_msec should be 4000 msec + change for overhead */ - g_assert_cmpint (elapsed_msec, >=, 4000); - g_assert_cmpint (elapsed_msec, <, 5000); - - g_print (" "); - } - - g_main_loop_quit (loop); -} - -static void -on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ -} - -static void -test_method_calls_in_thread (void) -{ - guint watcher_id; - - watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, - "com.example.TestService", - G_BUS_NAME_WATCHER_FLAGS_NONE, - "/com/example/TestObject", - "com.example.Frob", - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_NONE, - on_proxy_appeared, - on_proxy_vanished, - NULL, - NULL); - - g_main_loop_run (loop); - - g_bus_unwatch_proxy (watcher_id); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -int -main (int argc, - char *argv[]) -{ - GError *error; - gint ret; - - g_type_init (); - g_thread_init (NULL); - g_test_init (&argc, &argv, NULL); - - /* all the tests rely on a shared main loop */ - loop = g_main_loop_new (NULL, FALSE); - - /* all the tests use a session bus with a well-known address that we can bring up and down - * using session_bus_up() and session_bus_down(). - */ - g_unsetenv ("DISPLAY"); - g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); - - session_bus_up (); - - /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return - * until one can connect to the bus but that's not how things work right now - */ - usleep (500 * 1000); - - /* this is safe; testserver will exit once the bus goes away */ - g_assert (g_spawn_command_line_async ("./testserver.py", NULL)); - - /* wait for the service to come up */ - usleep (500 * 1000); - - /* Create the connection in the main thread */ - error = NULL; - c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); - g_assert_no_error (error); - g_assert (c != NULL); - - g_test_add_func ("/gdbus/delivery-in-thread", test_delivery_in_thread); - g_test_add_func ("/gdbus/method-calls-in-thread", test_method_calls_in_thread); - - ret = g_test_run(); - - g_object_unref (c); - - /* tear down bus */ - session_bus_down (); - - return ret; -} -- cgit v1.2.3