/* GLib testing framework examples and tests * * Copyright (C) 2008-2018 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.1 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, see . * * Author: David Zeuthen */ #include #include #include #include #include "gdbus-tests.h" #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 #include "gdbus-test-codegen-generated-min-version-2-64.h" #else #include "gdbus-test-codegen-generated.h" #endif #include "gdbus-test-codegen-generated-interface-info.h" /* ---------------------------------------------------------------------------------------------------- */ static guint count_annotations (GDBusAnnotationInfo **annotations) { guint ret; ret = 0; while (annotations != NULL && annotations[ret] != NULL) ret++; return ret; } /* checks that * * - non-internal annotations are written out correctly; and * - injection via --annotation --key --value works */ static void test_annotations (void) { GDBusInterfaceInfo *iface; GDBusMethodInfo *method; GDBusSignalInfo *signal; GDBusPropertyInfo *property; iface = foo_igen_bar_interface_info (); g_assert (iface != NULL); /* see meson.build for where these annotations are injected */ g_assert_cmpint (count_annotations (iface->annotations), ==, 1); g_assert_cmpstr (g_dbus_annotation_info_lookup (iface->annotations, "Key1"), ==, "Value1"); method = g_dbus_interface_info_lookup_method (iface, "HelloWorld"); g_assert (method != NULL); g_assert_cmpint (count_annotations (method->annotations), ==, 2); g_assert_cmpstr (g_dbus_annotation_info_lookup (method->annotations, "ExistingAnnotation"), ==, "blah"); g_assert_cmpstr (g_dbus_annotation_info_lookup (method->annotations, "Key3"), ==, "Value3"); signal = g_dbus_interface_info_lookup_signal (iface, "TestSignal"); g_assert (signal != NULL); g_assert_cmpint (count_annotations (signal->annotations), ==, 1); g_assert_cmpstr (g_dbus_annotation_info_lookup (signal->annotations, "Key4"), ==, "Value4"); g_assert_cmpstr (g_dbus_annotation_info_lookup (signal->args[1]->annotations, "Key8"), ==, "Value8"); property = g_dbus_interface_info_lookup_property (iface, "ay"); g_assert (property != NULL); g_assert_cmpint (count_annotations (property->annotations), ==, 1); g_assert_cmpstr (g_dbus_annotation_info_lookup (property->annotations, "Key5"), ==, "Value5"); method = g_dbus_interface_info_lookup_method (iface, "TestPrimitiveTypes"); g_assert (method != NULL); g_assert_cmpstr (g_dbus_annotation_info_lookup (method->in_args[4]->annotations, "Key6"), ==, "Value6"); g_assert_cmpstr (g_dbus_annotation_info_lookup (method->out_args[5]->annotations, "Key7"), ==, "Value7"); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean on_handle_hello_world (FooiGenBar *object, GDBusMethodInvocation *invocation, const gchar *greeting, gpointer user_data) { gchar *response; response = g_strdup_printf ("Word! You said '%s'. I'm Skeleton, btw!", greeting); foo_igen_bar_complete_hello_world (object, invocation, response); g_free (response); return TRUE; } static gboolean on_handle_test_primitive_types (FooiGenBar *object, GDBusMethodInvocation *invocation, guchar val_byte, gboolean val_boolean, gint16 val_int16, guint16 val_uint16, gint val_int32, guint val_uint32, gint64 val_int64, guint64 val_uint64, gdouble val_double, const gchar *val_string, const gchar *val_objpath, const gchar *val_signature, const gchar *val_bytestring, gpointer user_data) { gchar *s1; gchar *s2; gchar *s3; s1 = g_strdup_printf ("Word! You said '%s'. Rock'n'roll!", val_string); s2 = g_strdup_printf ("/modified%s", val_objpath); s3 = g_strdup_printf ("assgit%s", val_signature); foo_igen_bar_complete_test_primitive_types (object, invocation, 10 + val_byte, !val_boolean, 100 + val_int16, 1000 + val_uint16, 10000 + val_int32, 100000 + val_uint32, 1000000 + val_int64, 10000000 + val_uint64, val_double / G_PI, s1, s2, s3, "bytestring!\xff"); g_free (s1); g_free (s2); g_free (s3); return TRUE; } static gboolean on_handle_test_non_primitive_types (FooiGenBar *object, GDBusMethodInvocation *invocation, GVariant *dict_s_to_s, GVariant *dict_s_to_pairs, GVariant *a_struct, const gchar* const *array_of_strings, const gchar* const *array_of_objpaths, GVariant *array_of_signatures, const gchar* const *array_of_bytestrings, gpointer user_data) { gchar *s; GString *str; str = g_string_new (NULL); s = g_variant_print (dict_s_to_s, TRUE); g_string_append (str, s); g_free (s); s = g_variant_print (dict_s_to_pairs, TRUE); g_string_append (str, s); g_free (s); s = g_variant_print (a_struct, TRUE); g_string_append (str, s); g_free (s); s = g_strjoinv (", ", (gchar **) array_of_strings); g_string_append_printf (str, "array_of_strings: [%s] ", s); g_free (s); s = g_strjoinv (", ", (gchar **) array_of_objpaths); g_string_append_printf (str, "array_of_objpaths: [%s] ", s); g_free (s); s = g_variant_print (array_of_signatures, TRUE); g_string_append_printf (str, "array_of_signatures: %s ", s); g_free (s); s = g_strjoinv (", ", (gchar **) array_of_bytestrings); g_string_append_printf (str, "array_of_bytestrings: [%s] ", s); g_free (s); foo_igen_bar_complete_test_non_primitive_types (object, invocation, array_of_strings, array_of_objpaths, array_of_signatures, array_of_bytestrings, str->str); g_string_free (str, TRUE); return TRUE; } static gboolean on_handle_request_signal_emission (FooiGenBar *object, GDBusMethodInvocation *invocation, gint which_one, gpointer user_data) { if (which_one == 0) { const gchar *a_strv[] = {"foo", "bar", NULL}; const gchar *a_bytestring_array[] = {"foo\xff", "bar\xff", NULL}; GVariant *a_variant = g_variant_new_parsed ("{'first': (42, 42), 'second': (43, 43)}"); foo_igen_bar_emit_test_signal (object, 43, a_strv, a_bytestring_array, a_variant); /* consumes a_variant */ foo_igen_bar_complete_request_signal_emission (object, invocation); } return TRUE; } static gboolean on_handle_request_multi_property_mods (FooiGenBar *object, GDBusMethodInvocation *invocation, gpointer user_data) { foo_igen_bar_set_y (object, foo_igen_bar_get_y (object) + 1); foo_igen_bar_set_i (object, foo_igen_bar_get_i (object) + 1); foo_igen_bar_set_y (object, foo_igen_bar_get_y (object) + 1); foo_igen_bar_set_i (object, foo_igen_bar_get_i (object) + 1); g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (object)); foo_igen_bar_set_y (object, foo_igen_bar_get_y (object) + 1); foo_igen_bar_set_i (object, foo_igen_bar_get_i (object) + 1); foo_igen_bar_complete_request_multi_property_mods (object, invocation); return TRUE; } static gboolean on_handle_property_cancellation (FooiGenBar *object, GDBusMethodInvocation *invocation, gpointer user_data) { guint n; n = foo_igen_bar_get_n (object); /* This queues up a PropertiesChange event */ foo_igen_bar_set_n (object, n + 1); /* this modifies the queued up event */ foo_igen_bar_set_n (object, n); /* this flushes all PropertiesChanges event (sends the D-Bus message right * away, if any - there should not be any) */ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (object)); /* this makes us return the reply D-Bus method */ foo_igen_bar_complete_property_cancellation (object, invocation); return TRUE; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean on_handle_force_method (FooiGenBat *object, GDBusMethodInvocation *invocation, GVariant *force_in_i, GVariant *force_in_s, GVariant *force_in_ay, GVariant *force_in_struct, gpointer user_data) { GVariant *ret_i; GVariant *ret_s; GVariant *ret_ay; GVariant *ret_struct; gint32 val; gchar *s; ret_i = g_variant_new_int32 (g_variant_get_int32 (force_in_i) + 10); s = g_strdup_printf ("%s_foo", g_variant_get_string (force_in_s, NULL)); ret_s = g_variant_new_string (s); g_free (s); s = g_strdup_printf ("%s_foo\xff", g_variant_get_bytestring (force_in_ay)); ret_ay = g_variant_new_bytestring (s); g_free (s); g_variant_get (force_in_struct, "(i)", &val); ret_struct = g_variant_new ("(i)", val + 10); g_variant_ref_sink (ret_i); g_variant_ref_sink (ret_s); g_variant_ref_sink (ret_ay); g_variant_ref_sink (ret_struct); foo_igen_bat_emit_force_signal (object, ret_i, ret_s, ret_ay, ret_struct); foo_igen_bat_complete_force_method (object, invocation, ret_i, ret_s, ret_ay, ret_struct); g_variant_unref (ret_i); g_variant_unref (ret_s); g_variant_unref (ret_ay); g_variant_unref (ret_struct); return TRUE; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean my_g_authorize_method_handler (GDBusInterfaceSkeleton *interface, GDBusMethodInvocation *invocation, gpointer user_data) { const gchar *method_name; gboolean authorized; authorized = FALSE; method_name = g_dbus_method_invocation_get_method_name (invocation); if (g_strcmp0 (method_name, "CheckNotAuthorized") == 0) { authorized = FALSE; } else if (g_strcmp0 (method_name, "CheckAuthorized") == 0) { authorized = TRUE; } else if (g_strcmp0 (method_name, "CheckNotAuthorizedFromObject") == 0) { authorized = TRUE; } else { g_assert_not_reached (); } if (!authorized) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "not authorized..."); } return authorized; } static gboolean my_object_authorize_method_handler (GDBusObjectSkeleton *object, GDBusInterfaceSkeleton *interface, GDBusMethodInvocation *invocation, gpointer user_data) { const gchar *method_name; gboolean authorized; authorized = FALSE; method_name = g_dbus_method_invocation_get_method_name (invocation); if (g_strcmp0 (method_name, "CheckNotAuthorized") == 0) { authorized = TRUE; } else if (g_strcmp0 (method_name, "CheckAuthorized") == 0) { authorized = TRUE; } else if (g_strcmp0 (method_name, "CheckNotAuthorizedFromObject") == 0) { authorized = FALSE; } else { g_assert_not_reached (); } if (!authorized) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, G_IO_ERROR_PENDING, "not authorized (from object)..."); } return authorized; } static gboolean on_handle_check_not_authorized (FooiGenAuthorize *object, GDBusMethodInvocation *invocation, gpointer user_data) { foo_igen_authorize_complete_check_not_authorized (object, invocation); return TRUE; } static gboolean on_handle_check_authorized (FooiGenAuthorize *object, GDBusMethodInvocation *invocation, gpointer user_data) { foo_igen_authorize_complete_check_authorized (object, invocation); return TRUE; } static gboolean on_handle_check_not_authorized_from_object (FooiGenAuthorize *object, GDBusMethodInvocation *invocation, gpointer user_data) { foo_igen_authorize_complete_check_not_authorized_from_object (object, invocation); return TRUE; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean on_handle_get_self (FooiGenMethodThreads *object, GDBusMethodInvocation *invocation, gpointer user_data) { gchar *s; s = g_strdup_printf ("%p", (void *)g_thread_self ()); foo_igen_method_threads_complete_get_self (object, invocation, s); g_free (s); return TRUE; } /* ---------------------------------------------------------------------------------------------------- */ static GThread *method_handler_thread = NULL; static FooiGenBar *exported_bar_object = NULL; static FooiGenBat *exported_bat_object = NULL; static FooiGenAuthorize *exported_authorize_object = NULL; static GDBusObjectSkeleton *authorize_enclosing_object = NULL; static FooiGenMethodThreads *exported_thread_object_1 = NULL; static FooiGenMethodThreads *exported_thread_object_2 = NULL; static void unexport_objects (void) { g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_bar_object)); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_bat_object)); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_authorize_object)); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_thread_object_1)); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (exported_thread_object_2)); } static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { GError *error; /* Test that we can export an object using the generated * FooiGenBarSkeleton subclass. Notes: * * 1. We handle methods by simply connecting to the appropriate * GObject signal. * * 2. Property storage is taken care of by the class; we can * use g_object_get()/g_object_set() (and the generated * C bindings at will) */ error = NULL; exported_bar_object = foo_igen_bar_skeleton_new (); foo_igen_bar_set_ay (exported_bar_object, "ABCabc"); foo_igen_bar_set_y (exported_bar_object, 42); foo_igen_bar_set_d (exported_bar_object, 43.0); foo_igen_bar_set_finally_normal_name (exported_bar_object, "There aint no place like home"); foo_igen_bar_set_writeonly_property (exported_bar_object, "Mr. Burns"); /* The following works because it's on the Skeleton object - it will * fail (at run-time) on a Proxy (see on_proxy_appeared() below) */ foo_igen_bar_set_readonly_property (exported_bar_object, "blah"); g_assert_cmpstr (foo_igen_bar_get_writeonly_property (exported_bar_object), ==, "Mr. Burns"); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (exported_bar_object), connection, "/bar", &error); g_assert_no_error (error); g_signal_connect (exported_bar_object, "handle-hello-world", G_CALLBACK (on_handle_hello_world), NULL); g_signal_connect (exported_bar_object, "handle-test-primitive-types", G_CALLBACK (on_handle_test_primitive_types), NULL); g_signal_connect (exported_bar_object, "handle-test-non-primitive-types", G_CALLBACK (on_handle_test_non_primitive_types), NULL); g_signal_connect (exported_bar_object, "handle-request-signal-emission", G_CALLBACK (on_handle_request_signal_emission), NULL); g_signal_connect (exported_bar_object, "handle-request-multi-property-mods", G_CALLBACK (on_handle_request_multi_property_mods), NULL); g_signal_connect (exported_bar_object, "handle-property-cancellation", G_CALLBACK (on_handle_property_cancellation), NULL); exported_bat_object = foo_igen_bat_skeleton_new (); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (exported_bat_object), connection, "/bat", &error); g_assert_no_error (error); g_signal_connect (exported_bat_object, "handle-force-method", G_CALLBACK (on_handle_force_method), NULL); g_object_set (exported_bat_object, "force-i", g_variant_new_int32 (43), "force-s", g_variant_new_string ("prop string"), "force-ay", g_variant_new_bytestring ("prop bytestring\xff"), "force-struct", g_variant_new ("(i)", 4300), NULL); authorize_enclosing_object = g_dbus_object_skeleton_new ("/authorize"); g_signal_connect (authorize_enclosing_object, "authorize-method", G_CALLBACK (my_object_authorize_method_handler), NULL); exported_authorize_object = foo_igen_authorize_skeleton_new (); g_dbus_object_skeleton_add_interface (authorize_enclosing_object, G_DBUS_INTERFACE_SKELETON (exported_authorize_object)); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (exported_authorize_object), connection, "/authorize", &error); g_assert_no_error (error); g_signal_connect (exported_authorize_object, "g-authorize-method", G_CALLBACK (my_g_authorize_method_handler), NULL); g_signal_connect (exported_authorize_object, "handle-check-not-authorized", G_CALLBACK (on_handle_check_not_authorized), NULL); g_signal_connect (exported_authorize_object, "handle-check-authorized", G_CALLBACK (on_handle_check_authorized), NULL); g_signal_connect (exported_authorize_object, "handle-check-not-authorized-from-object", G_CALLBACK (on_handle_check_not_authorized_from_object), NULL); /* only object 1 has the G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD flag set */ exported_thread_object_1 = foo_igen_method_threads_skeleton_new (); g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (exported_thread_object_1), G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); g_assert (!g_dbus_interface_skeleton_has_connection (G_DBUS_INTERFACE_SKELETON (exported_thread_object_1), connection)); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (exported_thread_object_1), connection, "/method_threads_1", &error); g_assert_no_error (error); g_signal_connect (exported_thread_object_1, "handle-get-self", G_CALLBACK (on_handle_get_self), NULL); g_assert_cmpint (g_dbus_interface_skeleton_get_flags (G_DBUS_INTERFACE_SKELETON (exported_thread_object_1)), ==, G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD); exported_thread_object_2 = foo_igen_method_threads_skeleton_new (); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (exported_thread_object_2), connection, "/method_threads_2", &error); g_assert_no_error (error); g_signal_connect (exported_thread_object_2, "handle-get-self", G_CALLBACK (on_handle_get_self), NULL); g_assert_cmpint (g_dbus_interface_skeleton_get_flags (G_DBUS_INTERFACE_SKELETON (exported_thread_object_2)), ==, G_DBUS_INTERFACE_SKELETON_FLAGS_NONE); method_handler_thread = g_thread_self (); } static gpointer check_proxies_in_thread (gpointer user_data); static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { GMainLoop *loop = user_data; GThread *thread = g_thread_new ("check-proxies", check_proxies_in_thread, loop); g_thread_unref (thread); } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { g_assert_not_reached (); } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { GMainLoop *thread_loop; gint initial_y; gint initial_i; guint num_g_properties_changed; gboolean received_test_signal; guint num_notify_u; guint num_notify_n; } ClientData; static void on_notify_u (GObject *object, GParamSpec *pspec, gpointer user_data) { ClientData *data = user_data; g_assert_cmpstr (pspec->name, ==, "u"); data->num_notify_u += 1; } static void on_notify_n (GObject *object, GParamSpec *pspec, gpointer user_data) { ClientData *data = user_data; g_assert_cmpstr (pspec->name, ==, "n"); data->num_notify_n += 1; } static void on_g_properties_changed (GDBusProxy *_proxy, GVariant *changed_properties, const gchar* const *invalidated_properties, gpointer user_data) { ClientData *data = user_data; FooiGenBar *proxy = FOO_IGEN_BAR (_proxy); g_assert_cmpint (g_variant_n_children (changed_properties), ==, 2); if (data->num_g_properties_changed == 0) { g_assert_cmpint (data->initial_y, ==, foo_igen_bar_get_y (proxy) - 2); g_assert_cmpint (data->initial_i, ==, foo_igen_bar_get_i (proxy) - 2); } else if (data->num_g_properties_changed == 1) { g_assert_cmpint (data->initial_y, ==, foo_igen_bar_get_y (proxy) - 3); g_assert_cmpint (data->initial_i, ==, foo_igen_bar_get_i (proxy) - 3); } else g_assert_not_reached (); data->num_g_properties_changed++; if (data->num_g_properties_changed == 2) g_main_loop_quit (data->thread_loop); } static void on_test_signal (FooiGenBar *proxy, gint val_int32, const gchar* const *array_of_strings, const gchar* const *array_of_bytestrings, GVariant *dict_s_to_pairs, gpointer user_data) { ClientData *data = user_data; g_assert_cmpint (val_int32, ==, 43); g_assert_cmpstr (array_of_strings[0], ==, "foo"); g_assert_cmpstr (array_of_strings[1], ==, "bar"); g_assert (array_of_strings[2] == NULL); g_assert_cmpstr (array_of_bytestrings[0], ==, "foo\xff"); g_assert_cmpstr (array_of_bytestrings[1], ==, "bar\xff"); g_assert (array_of_bytestrings[2] == NULL); data->received_test_signal = TRUE; g_main_loop_quit (data->thread_loop); } static void on_property_cancellation_cb (FooiGenBar *proxy, GAsyncResult *res, gpointer user_data) { ClientData *data = user_data; gboolean ret; GError *error = NULL; error = NULL; ret = foo_igen_bar_call_property_cancellation_finish (proxy, res, &error); g_assert_no_error (error); g_assert (ret); g_main_loop_quit (data->thread_loop); } static void check_bar_proxy (FooiGenBar *proxy, GMainLoop *thread_loop) { const gchar *array_of_strings[3] = {"one", "two", NULL}; const gchar *array_of_strings_2[3] = {"one2", "two2", NULL}; const gchar *array_of_objpaths[3] = {"/one", "/one/two", NULL}; GVariant *array_of_signatures = NULL; const gchar *array_of_bytestrings[3] = {"one\xff", "two\xff", NULL}; gchar **ret_array_of_strings = NULL; gchar **ret_array_of_objpaths = NULL; GVariant *ret_array_of_signatures = NULL; gchar **ret_array_of_bytestrings = NULL; guchar ret_val_byte; gboolean ret_val_boolean; gint16 ret_val_int16; guint16 ret_val_uint16; gint ret_val_int32; guint ret_val_uint32; gint64 ret_val_int64; guint64 ret_val_uint64; gdouble ret_val_double; gchar *ret_val_string; gchar *ret_val_objpath; gchar *ret_val_signature; gchar *ret_val_bytestring; gboolean ret; GError *error; ClientData *data; guchar val_y; gboolean val_b; gint val_n; guint val_q; gint val_i; guint val_u; gint64 val_x; guint64 val_t; gdouble val_d; gchar *val_s; gchar *val_o; gchar *val_g; gchar *val_ay; gchar **val_as; gchar **val_ao; GVariant *val_ag; gint32 val_unset_i; gdouble val_unset_d; gchar *val_unset_s; gchar *val_unset_o; gchar *val_unset_g; gchar *val_unset_ay; gchar **val_unset_as; gchar **val_unset_ao; GVariant *val_unset_ag; GVariant *val_unset_struct; gchar *val_finally_normal_name; GVariant *v; gchar *s; const gchar *const *read_as; const gchar *const *read_as2; const gchar *const *read_as3; data = g_new0 (ClientData, 1); data->thread_loop = thread_loop; v = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "y"); g_assert (v != NULL); g_variant_unref (v); /* set empty values to non-empty */ val_unset_i = 42; val_unset_d = 42.0; val_unset_s = "42"; val_unset_o = "42"; val_unset_g = "42"; val_unset_ay = NULL; val_unset_as = NULL; val_unset_ao = NULL; val_unset_ag = NULL; val_unset_struct = NULL; /* check properties */ g_object_get (proxy, "y", &val_y, "b", &val_b, "n", &val_n, "q", &val_q, "i", &val_i, "u", &val_u, "x", &val_x, "t", &val_t, "d", &val_d, "s", &val_s, "o", &val_o, "g", &val_g, "ay", &val_ay, "as", &val_as, "ao", &val_ao, "ag", &val_ag, "unset_i", &val_unset_i, "unset_d", &val_unset_d, "unset_s", &val_unset_s, "unset_o", &val_unset_o, "unset_g", &val_unset_g, "unset_ay", &val_unset_ay, "unset_as", &val_unset_as, "unset_ao", &val_unset_ao, "unset_ag", &val_unset_ag, "unset_struct", &val_unset_struct, "finally-normal-name", &val_finally_normal_name, NULL); g_assert_cmpint (val_y, ==, 42); g_assert_cmpstr (val_finally_normal_name, ==, "There aint no place like home"); g_free (val_s); g_free (val_o); g_free (val_g); g_assert_cmpstr (val_ay, ==, "ABCabc"); g_free (val_ay); g_strfreev (val_as); g_strfreev (val_ao); g_variant_unref (val_ag); g_free (val_finally_normal_name); /* check empty values */ g_assert_cmpint (val_unset_i, ==, 0); g_assert_cmpfloat (val_unset_d, ==, 0.0); g_assert_cmpstr (val_unset_s, ==, ""); g_assert_cmpstr (val_unset_o, ==, "/"); g_assert_cmpstr (val_unset_g, ==, ""); g_free (val_unset_s); g_free (val_unset_o); g_free (val_unset_g); g_assert_cmpstr (val_unset_ay, ==, ""); g_assert (val_unset_as[0] == NULL); g_assert (val_unset_ao[0] == NULL); g_assert (g_variant_is_of_type (val_unset_ag, G_VARIANT_TYPE ("ag"))); g_assert (g_variant_is_of_type (val_unset_struct, G_VARIANT_TYPE ("(idsogayasaoag)"))); s = g_variant_print (val_unset_struct, TRUE); g_assert_cmpstr (s, ==, "(0, 0.0, '', objectpath '/', signature '', @ay [], @as [], @ao [], @ag [])"); g_free (s); g_free (val_unset_ay); g_strfreev (val_unset_as); g_strfreev (val_unset_ao); g_variant_unref (val_unset_ag); g_variant_unref (val_unset_struct); /* Try setting a property. This causes the generated glue to invoke * the org.fd.DBus.Properties.Set() method asynchronously. So we * have to wait for properties-changed... */ foo_igen_bar_set_finally_normal_name (proxy, "foo!"); _g_assert_property_notify (proxy, "finally-normal-name"); g_assert_cmpstr (foo_igen_bar_get_finally_normal_name (proxy), ==, "foo!"); /* Try setting properties that requires memory management. This * is to exercise the paths that frees the references. */ g_object_set (proxy, "s", "a string", "o", "/a/path", "g", "asig", "ay", g_variant_new_parsed ("[byte 0x65, 0x67]"), "as", array_of_strings, "ao", array_of_objpaths, "ag", g_variant_new_parsed ("[@g 'ass', 'git']"), NULL); error = NULL; ret = foo_igen_bar_call_test_primitive_types_sync (proxy, 10, TRUE, 11, 12, 13, 14, 15, 16, 17, "a string", "/a/path", "asig", "bytestring\xff", #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 G_DBUS_CALL_FLAGS_NONE, -1, #endif &ret_val_byte, &ret_val_boolean, &ret_val_int16, &ret_val_uint16, &ret_val_int32, &ret_val_uint32, &ret_val_int64, &ret_val_uint64, &ret_val_double, &ret_val_string, &ret_val_objpath, &ret_val_signature, &ret_val_bytestring, NULL, /* GCancellable */ &error); g_assert_no_error (error); g_assert (ret); g_clear_pointer (&ret_val_string, g_free); g_clear_pointer (&ret_val_objpath, g_free); g_clear_pointer (&ret_val_signature, g_free); g_clear_pointer (&ret_val_bytestring, g_free); error = NULL; array_of_signatures = g_variant_ref_sink (g_variant_new_parsed ("[@g 'ass', 'git']")); ret = foo_igen_bar_call_test_non_primitive_types_sync (proxy, g_variant_new_parsed ("{'one': 'red'," " 'two': 'blue'}"), g_variant_new_parsed ("{'first': (42, 42), " "'second': (43, 43)}"), g_variant_new_parsed ("(42, 'foo', 'bar')"), array_of_strings, array_of_objpaths, array_of_signatures, array_of_bytestrings, #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 G_DBUS_CALL_FLAGS_NONE, -1, #endif &ret_array_of_strings, &ret_array_of_objpaths, &ret_array_of_signatures, &ret_array_of_bytestrings, &s, NULL, /* GCancellable */ &error); g_assert_no_error (error); g_assert (ret); g_assert_nonnull (ret_array_of_strings); g_assert_cmpuint (g_strv_length ((gchar **) ret_array_of_strings), ==, g_strv_length ((gchar **) array_of_strings)); g_assert_nonnull (ret_array_of_objpaths); g_assert_cmpuint (g_strv_length ((gchar **) ret_array_of_objpaths), ==, g_strv_length ((gchar **) array_of_objpaths)); g_assert_nonnull (ret_array_of_signatures); g_assert_cmpvariant (ret_array_of_signatures, array_of_signatures); g_assert_nonnull (ret_array_of_bytestrings); g_assert_cmpuint (g_strv_length ((gchar **) ret_array_of_bytestrings), ==, g_strv_length ((gchar **) array_of_bytestrings)); g_clear_pointer (&ret_array_of_strings, g_strfreev); g_clear_pointer (&ret_array_of_objpaths, g_strfreev); g_clear_pointer (&ret_array_of_signatures, g_variant_unref); g_clear_pointer (&ret_array_of_bytestrings, g_strfreev); g_clear_pointer (&s, g_free); /* Check that org.freedesktop.DBus.Error.UnknownMethod is returned on * unimplemented methods. */ error = NULL; #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 ret = foo_igen_bar_call_unimplemented_method_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL /* GCancellable */, &error); #else ret = foo_igen_bar_call_unimplemented_method_sync (proxy, NULL /* GCancellable */, &error); #endif g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); g_error_free (error); error = NULL; g_assert (!ret); g_signal_connect (proxy, "test-signal", G_CALLBACK (on_test_signal), data); error = NULL; #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 ret = foo_igen_bar_call_request_signal_emission_sync (proxy, 0, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); #else ret = foo_igen_bar_call_request_signal_emission_sync (proxy, 0, NULL, &error); #endif g_assert_no_error (error); g_assert (ret); g_assert (!data->received_test_signal); g_main_loop_run (thread_loop); g_assert (data->received_test_signal); /* Try setting a property. This causes the generated glue to invoke * the org.fd.DBus.Properties.Set() method asynchronously. So we * have to wait for properties-changed... */ foo_igen_bar_set_finally_normal_name (proxy, "hey back!"); _g_assert_property_notify (proxy, "finally-normal-name"); g_assert_cmpstr (foo_igen_bar_get_finally_normal_name (proxy), ==, "hey back!"); /* Check that multiple calls to a strv getter works... and that * updates on them works as well (See comment for "property vfuncs" * in gio/gdbus-codegen/codegen.py for details) */ read_as = foo_igen_bar_get_as (proxy); read_as2 = foo_igen_bar_get_as (proxy); g_assert_cmpint (g_strv_length ((gchar **) read_as), ==, 2); g_assert_cmpstr (read_as[0], ==, "one"); g_assert_cmpstr (read_as[1], ==, "two"); g_assert (read_as == read_as2); /* this is more testing an implementation detail */ g_object_set (proxy, "as", array_of_strings_2, NULL); _g_assert_property_notify (proxy, "as"); read_as3 = foo_igen_bar_get_as (proxy); g_assert_cmpint (g_strv_length ((gchar **) read_as3), ==, 2); g_assert_cmpstr (read_as3[0], ==, "one2"); g_assert_cmpstr (read_as3[1], ==, "two2"); /* Check that grouping changes in idle works. * * See on_handle_request_multi_property_mods(). The server should * emit exactly two PropertiesChanged signals each containing two * properties. * * On the first reception, y and i should both be increased by * two. On the second reception, only by one. The signal handler * checks this. * * This also checks that _drain_notify() works. */ data->initial_y = foo_igen_bar_get_y (proxy); data->initial_i = foo_igen_bar_get_i (proxy); g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (on_g_properties_changed), data); error = NULL; #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 ret = foo_igen_bar_call_request_multi_property_mods_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); #else ret = foo_igen_bar_call_request_multi_property_mods_sync (proxy, NULL, &error); #endif g_assert_no_error (error); g_assert (ret); g_main_loop_run (thread_loop); g_assert_cmpint (data->num_g_properties_changed, ==, 2); g_signal_handlers_disconnect_by_func (proxy, G_CALLBACK (on_g_properties_changed), data); /* Check that we don't emit PropertiesChanged() if the property * didn't change... we actually get two notifies.. one for the * local set (without a value change) and one when receiving * the PropertiesChanged() signal generated from the remote end. */ g_assert_cmpint (data->num_notify_u, ==, 0); g_signal_connect (proxy, "notify::u", G_CALLBACK (on_notify_u), data); foo_igen_bar_set_u (proxy, 1042); g_assert_cmpint (data->num_notify_u, ==, 1); g_assert_cmpint (foo_igen_bar_get_u (proxy), ==, 0); _g_assert_property_notify (proxy, "u"); g_assert_cmpint (foo_igen_bar_get_u (proxy), ==, 1042); g_assert_cmpint (data->num_notify_u, ==, 2); /* Now change u again to the same value.. this will cause a * local notify:: notify and the usual Properties.Set() call * * (Btw, why also the Set() call if the value in the cache is * the same? Because someone else might have changed it * in the mean time and we're just waiting to receive the * PropertiesChanged() signal...) * * More tricky - how do we check for the *absence* of the * notification that u changed? Simple: we change another * property and wait for that PropertiesChanged() message * to arrive. */ foo_igen_bar_set_u (proxy, 1042); g_assert_cmpint (data->num_notify_u, ==, 3); g_assert_cmpint (data->num_notify_n, ==, 0); g_signal_connect (proxy, "notify::n", G_CALLBACK (on_notify_n), data); foo_igen_bar_set_n (proxy, 10042); g_assert_cmpint (data->num_notify_n, ==, 1); g_assert_cmpint (foo_igen_bar_get_n (proxy), ==, 0); _g_assert_property_notify (proxy, "n"); g_assert_cmpint (foo_igen_bar_get_n (proxy), ==, 10042); g_assert_cmpint (data->num_notify_n, ==, 2); /* Checks that u didn't change at all */ g_assert_cmpint (data->num_notify_u, ==, 3); /* Now we check that if the service does * * guint n = foo_igen_bar_get_n (foo); * foo_igen_bar_set_n (foo, n + 1); * foo_igen_bar_set_n (foo, n); * * then no PropertiesChanged() signal is emitted! */ error = NULL; foo_igen_bar_call_property_cancellation (proxy, #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 G_DBUS_CALL_FLAGS_NONE, -1, #endif NULL, /* GCancellable */ (GAsyncReadyCallback) on_property_cancellation_cb, data); g_main_loop_run (thread_loop); /* Checks that n didn't change at all */ g_assert_cmpint (data->num_notify_n, ==, 2); /* cleanup */ g_free (data); g_variant_unref (array_of_signatures); } /* ---------------------------------------------------------------------------------------------------- */ static void on_force_signal (FooiGenBat *proxy, GVariant *force_i, GVariant *force_s, GVariant *force_ay, GVariant *force_struct, gpointer user_data) { gboolean *signal_received = user_data; gint val; g_assert (!(*signal_received)); g_assert_cmpint (g_variant_get_int32 (force_i), ==, 42 + 10); g_assert_cmpstr (g_variant_get_string (force_s, NULL), ==, "a string_foo"); g_assert_cmpstr (g_variant_get_bytestring (force_ay), ==, "a bytestring\xff_foo\xff"); g_variant_get (force_struct, "(i)", &val); g_assert_cmpint (val, ==, 4200 + 10); *signal_received = TRUE; } static void check_bat_proxy (FooiGenBat *proxy, GMainLoop *thread_loop) { GError *error; GVariant *ret_i; GVariant *ret_s; GVariant *ret_ay; GVariant *ret_struct; gint val; gboolean force_signal_received; /* --------------------------------------------------- */ /* Check type-mapping where we force use of a GVariant */ /* --------------------------------------------------- */ /* check properties */ g_object_get (proxy, "force-i", &ret_i, "force-s", &ret_s, "force-ay", &ret_ay, "force-struct", &ret_struct, NULL); g_assert_cmpint (g_variant_get_int32 (ret_i), ==, 43); g_assert_cmpstr (g_variant_get_string (ret_s, NULL), ==, "prop string"); g_assert_cmpstr (g_variant_get_bytestring (ret_ay), ==, "prop bytestring\xff"); g_variant_get (ret_struct, "(i)", &val); g_assert_cmpint (val, ==, 4300); g_variant_unref (ret_i); g_variant_unref (ret_s); g_variant_unref (ret_ay); g_variant_unref (ret_struct); /* check method and signal */ force_signal_received = FALSE; g_signal_connect (proxy, "force-signal", G_CALLBACK (on_force_signal), &force_signal_received); error = NULL; foo_igen_bat_call_force_method_sync (proxy, g_variant_new_int32 (42), g_variant_new_string ("a string"), g_variant_new_bytestring ("a bytestring\xff"), g_variant_new ("(i)", 4200), #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 G_DBUS_CALL_FLAGS_NONE, -1, #endif &ret_i, &ret_s, &ret_ay, &ret_struct, NULL, /* GCancellable* */ &error); g_assert_no_error (error); g_assert_cmpint (g_variant_get_int32 (ret_i), ==, 42 + 10); g_assert_cmpstr (g_variant_get_string (ret_s, NULL), ==, "a string_foo"); g_assert_cmpstr (g_variant_get_bytestring (ret_ay), ==, "a bytestring\xff_foo\xff"); g_variant_get (ret_struct, "(i)", &val); g_assert_cmpint (val, ==, 4200 + 10); g_variant_unref (ret_i); g_variant_unref (ret_s); g_variant_unref (ret_ay); g_variant_unref (ret_struct); _g_assert_signal_received (proxy, "force-signal"); g_assert (force_signal_received); } /* ---------------------------------------------------------------------------------------------------- */ static void check_authorize_proxy (FooiGenAuthorize *proxy, GMainLoop *thread_loop) { GError *error; gboolean ret; /* Check that g-authorize-method works as intended */ error = NULL; #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 ret = foo_igen_authorize_call_check_not_authorized_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); #else ret = foo_igen_authorize_call_check_not_authorized_sync (proxy, NULL, &error); #endif g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED); g_error_free (error); g_assert (!ret); error = NULL; #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 ret = foo_igen_authorize_call_check_authorized_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); #else ret = foo_igen_authorize_call_check_authorized_sync (proxy, NULL, &error); #endif g_assert_no_error (error); g_assert (ret); error = NULL; #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 ret = foo_igen_authorize_call_check_not_authorized_from_object_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); #else ret = foo_igen_authorize_call_check_not_authorized_from_object_sync (proxy, NULL, &error); #endif g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING); g_error_free (error); g_assert (!ret); } /* ---------------------------------------------------------------------------------------------------- */ static GThread * get_self_via_proxy (FooiGenMethodThreads *proxy_1) { GError *error; gchar *self_str; gboolean ret; gpointer self; error = NULL; #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 ret = foo_igen_method_threads_call_get_self_sync (proxy_1, G_DBUS_CALL_FLAGS_NONE, -1, &self_str, NULL, &error); #else ret = foo_igen_method_threads_call_get_self_sync (proxy_1, &self_str, NULL, &error); #endif g_assert_no_error (error); g_assert (ret); g_assert_cmpint (sscanf (self_str, "%p", &self), ==, 1); g_free (self_str); return self; } static void check_thread_proxies (FooiGenMethodThreads *proxy_1, FooiGenMethodThreads *proxy_2, GMainLoop *thread_loop) { /* proxy_1 is indeed using threads so should never get the handler thread */ g_assert (get_self_via_proxy (proxy_1) != method_handler_thread); /* proxy_2 is not using threads so should get the handler thread */ g_assert (get_self_via_proxy (proxy_2) == method_handler_thread); } /* ---------------------------------------------------------------------------------------------------- */ static gpointer check_proxies_in_thread (gpointer user_data) { GMainLoop *loop = user_data; GMainContext *thread_context; GMainLoop *thread_loop; GError *error; FooiGenBar *bar_proxy; FooiGenBat *bat_proxy; FooiGenAuthorize *authorize_proxy; FooiGenMethodThreads *thread_proxy_1; FooiGenMethodThreads *thread_proxy_2; thread_context = g_main_context_new (); thread_loop = g_main_loop_new (thread_context, FALSE); g_main_context_push_thread_default (thread_context); /* Check the object */ error = NULL; bar_proxy = foo_igen_bar_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, "org.gtk.GDBus.BindingsTool.Test", "/bar", NULL, /* GCancellable* */ &error); check_bar_proxy (bar_proxy, thread_loop); g_assert_no_error (error); g_object_unref (bar_proxy); error = NULL; bat_proxy = foo_igen_bat_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, "org.gtk.GDBus.BindingsTool.Test", "/bat", NULL, /* GCancellable* */ &error); check_bat_proxy (bat_proxy, thread_loop); g_assert_no_error (error); g_object_unref (bat_proxy); error = NULL; authorize_proxy = foo_igen_authorize_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, "org.gtk.GDBus.BindingsTool.Test", "/authorize", NULL, /* GCancellable* */ &error); check_authorize_proxy (authorize_proxy, thread_loop); g_assert_no_error (error); g_object_unref (authorize_proxy); error = NULL; thread_proxy_1 = foo_igen_method_threads_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, "org.gtk.GDBus.BindingsTool.Test", "/method_threads_1", NULL, /* GCancellable* */ &error); g_assert_no_error (error); thread_proxy_2 = foo_igen_method_threads_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, "org.gtk.GDBus.BindingsTool.Test", "/method_threads_2", NULL, /* GCancellable* */ &error); g_assert_no_error (error); check_thread_proxies (thread_proxy_1, thread_proxy_2, thread_loop); g_object_unref (thread_proxy_1); g_object_unref (thread_proxy_2); g_main_loop_unref (thread_loop); g_main_context_unref (thread_context); /* this breaks out of the loop in main() (below) */ g_main_loop_quit (loop); return NULL; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { gchar *xml; GMainLoop *loop; } IntrospectData; static void introspect_cb (GDBusConnection *connection, GAsyncResult *res, gpointer user_data) { IntrospectData *data = user_data; GVariant *result; GError *error; error = NULL; result = g_dbus_connection_call_finish (connection, res, &error); g_assert_no_error (error); g_assert (result != NULL); g_variant_get (result, "(s)", &data->xml); g_variant_unref (result); g_main_loop_quit (data->loop); } static GDBusNodeInfo * introspect (GDBusConnection *connection, const gchar *name, const gchar *object_path, GMainLoop *loop) { GError *error; GDBusNodeInfo *node_info; IntrospectData *data; data = g_new0 (IntrospectData, 1); data->xml = NULL; data->loop = loop; /* do this async to avoid deadlocks */ g_dbus_connection_call (connection, name, object_path, "org.freedesktop.DBus.Introspectable", "Introspect", NULL, /* params */ G_VARIANT_TYPE ("(s)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback) introspect_cb, data); g_main_loop_run (loop); g_assert (data->xml != NULL); error = NULL; node_info = g_dbus_node_info_new_for_xml (data->xml, &error); g_assert_no_error (error); g_assert (node_info != NULL); g_free (data->xml); g_free (data); return node_info; } static guint count_interfaces (GDBusNodeInfo *info) { guint n; for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++) ; return n; } static guint count_nodes (GDBusNodeInfo *info) { guint n; for (n = 0; info->nodes != NULL && info->nodes[n] != NULL; n++) ; return n; } static guint has_interface (GDBusNodeInfo *info, const gchar *name) { guint n; for (n = 0; info->interfaces != NULL && info->interfaces[n] != NULL; n++) { if (g_strcmp0 (info->interfaces[n]->name, name) == 0) return TRUE; } return FALSE; } /* ---------------------------------------------------------------------------------------------------- */ typedef struct { GMainLoop *loop; GVariant *result; } OMGetManagedObjectsData; static void om_get_all_cb (GDBusConnection *connection, GAsyncResult *res, gpointer user_data) { OMGetManagedObjectsData *data = user_data; GError *error; error = NULL; data->result = g_dbus_connection_call_finish (connection, res, &error); g_assert_no_error (error); g_assert (data->result != NULL); g_main_loop_quit (data->loop); } static void om_check_get_all (GDBusConnection *c, GMainLoop *loop, const gchar *str) { OMGetManagedObjectsData data; gchar *s; data.loop = loop; data.result = NULL; /* do this async to avoid deadlocks */ g_dbus_connection_call (c, g_dbus_connection_get_unique_name (c), "/managed", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", NULL, /* params */ G_VARIANT_TYPE ("(a{oa{sa{sv}}})"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, (GAsyncReadyCallback) om_get_all_cb, &data); g_main_loop_run (loop); g_assert (data.result != NULL); s = g_variant_print (data.result, TRUE); g_assert_cmpstr (s, ==, str); g_free (s); g_variant_unref (data.result); } typedef struct { GMainLoop *loop; guint state; guint num_object_proxy_added_signals; guint num_object_proxy_removed_signals; guint num_interface_added_signals; guint num_interface_removed_signals; } OMData; static gint my_pstrcmp (const gchar **a, const gchar **b) { return g_strcmp0 (*a, *b); } static void om_check_interfaces_added (const gchar *signal_name, GVariant *parameters, const gchar *object_path, const gchar *first_interface_name, ...) { const gchar *path; GVariant *array; guint n; GPtrArray *interfaces; GPtrArray *interfaces_in_message; va_list var_args; const gchar *str; interfaces = g_ptr_array_new (); g_ptr_array_add (interfaces, (gpointer) first_interface_name); va_start (var_args, first_interface_name); do { str = va_arg (var_args, const gchar *); if (str == NULL) break; g_ptr_array_add (interfaces, (gpointer) str); } while (TRUE); va_end (var_args); g_variant_get (parameters, "(&o*)", &path, &array); g_assert_cmpstr (signal_name, ==, "InterfacesAdded"); g_assert_cmpstr (path, ==, object_path); g_assert_cmpint (g_variant_n_children (array), ==, interfaces->len); interfaces_in_message = g_ptr_array_new (); for (n = 0; n < interfaces->len; n++) { const gchar *iface_name; g_variant_get_child (array, n, "{&sa{sv}}", &iface_name, NULL); g_ptr_array_add (interfaces_in_message, (gpointer) iface_name); } g_assert_cmpint (interfaces_in_message->len, ==, interfaces->len); g_ptr_array_sort (interfaces, (GCompareFunc) my_pstrcmp); g_ptr_array_sort (interfaces_in_message, (GCompareFunc) my_pstrcmp); for (n = 0; n < interfaces->len; n++) g_assert_cmpstr (interfaces->pdata[n], ==, interfaces_in_message->pdata[n]); g_ptr_array_unref (interfaces_in_message); g_ptr_array_unref (interfaces); g_variant_unref (array); } static void om_check_interfaces_removed (const gchar *signal_name, GVariant *parameters, const gchar *object_path, const gchar *first_interface_name, ...) { const gchar *path; GVariant *array; guint n; GPtrArray *interfaces; GPtrArray *interfaces_in_message; va_list var_args; const gchar *str; interfaces = g_ptr_array_new (); g_ptr_array_add (interfaces, (gpointer) first_interface_name); va_start (var_args, first_interface_name); do { str = va_arg (var_args, const gchar *); if (str == NULL) break; g_ptr_array_add (interfaces, (gpointer) str); } while (TRUE); va_end (var_args); g_variant_get (parameters, "(&o*)", &path, &array); g_assert_cmpstr (signal_name, ==, "InterfacesRemoved"); g_assert_cmpstr (path, ==, object_path); g_assert_cmpint (g_variant_n_children (array), ==, interfaces->len); interfaces_in_message = g_ptr_array_new (); for (n = 0; n < interfaces->len; n++) { const gchar *iface_name; g_variant_get_child (array, n, "&s", &iface_name, NULL); g_ptr_array_add (interfaces_in_message, (gpointer) iface_name); } g_assert_cmpint (interfaces_in_message->len, ==, interfaces->len); g_ptr_array_sort (interfaces, (GCompareFunc) my_pstrcmp); g_ptr_array_sort (interfaces_in_message, (GCompareFunc) my_pstrcmp); for (n = 0; n < interfaces->len; n++) g_assert_cmpstr (interfaces->pdata[n], ==, interfaces_in_message->pdata[n]); g_ptr_array_unref (interfaces_in_message); g_ptr_array_unref (interfaces); g_variant_unref (array); } static void om_on_signal (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { OMData *om_data = user_data; //g_debug ("foo: %s", g_variant_print (parameters, TRUE)); switch (om_data->state) { default: case 0: g_printerr ("failing and om_data->state=%d on signal %s, params=%s\n", om_data->state, signal_name, g_variant_print (parameters, TRUE)); g_assert_not_reached (); break; case 1: om_check_interfaces_added (signal_name, parameters, "/managed/first", "org.project.Bar", NULL); om_data->state = 2; g_main_loop_quit (om_data->loop); break; case 3: om_check_interfaces_removed (signal_name, parameters, "/managed/first", "org.project.Bar", NULL); om_data->state = 5; /* keep running the loop */ break; case 5: om_check_interfaces_added (signal_name, parameters, "/managed/first", "org.project.Bar", NULL); om_data->state = 6; g_main_loop_quit (om_data->loop); break; case 7: om_check_interfaces_removed (signal_name, parameters, "/managed/first", "org.project.Bar", NULL); om_data->state = 9; /* keep running the loop */ break; case 9: om_check_interfaces_added (signal_name, parameters, "/managed/first", "org.project.Bar", NULL); om_data->state = 10; g_main_loop_quit (om_data->loop); break; case 11: om_check_interfaces_added (signal_name, parameters, "/managed/first", "org.project.Bat", NULL); om_data->state = 12; g_main_loop_quit (om_data->loop); break; case 13: om_check_interfaces_removed (signal_name, parameters, "/managed/first", "org.project.Bar", NULL); om_data->state = 14; g_main_loop_quit (om_data->loop); break; case 15: om_check_interfaces_removed (signal_name, parameters, "/managed/first", "org.project.Bat", NULL); om_data->state = 16; g_main_loop_quit (om_data->loop); break; case 17: om_check_interfaces_added (signal_name, parameters, "/managed/first", "com.acme.Coyote", NULL); om_data->state = 18; g_main_loop_quit (om_data->loop); break; case 101: om_check_interfaces_added (signal_name, parameters, "/managed/second", "org.project.Bat", "org.project.Bar", NULL); om_data->state = 102; g_main_loop_quit (om_data->loop); break; case 103: om_check_interfaces_removed (signal_name, parameters, "/managed/second", "org.project.Bat", "org.project.Bar", NULL); om_data->state = 104; g_main_loop_quit (om_data->loop); break; case 200: om_check_interfaces_added (signal_name, parameters, "/managed/first_1", "com.acme.Coyote", NULL); om_data->state = 201; g_main_loop_quit (om_data->loop); break; } } static GAsyncResult *om_res = NULL; static void om_pm_start_cb (FooiGenObjectManagerClient *manager, GAsyncResult *res, gpointer user_data) { GMainLoop *loop = user_data; om_res = g_object_ref (res); g_main_loop_quit (loop); } static void on_interface_added (GDBusObject *object, GDBusInterface *interface, gpointer user_data) { OMData *om_data = user_data; om_data->num_interface_added_signals += 1; } static void on_interface_removed (GDBusObject *object, GDBusInterface *interface, gpointer user_data) { OMData *om_data = user_data; om_data->num_interface_removed_signals += 1; } static void on_object_proxy_added (GDBusObjectManagerClient *manager, GDBusObjectProxy *object_proxy, gpointer user_data) { OMData *om_data = user_data; om_data->num_object_proxy_added_signals += 1; g_signal_connect (object_proxy, "interface-added", G_CALLBACK (on_interface_added), om_data); g_signal_connect (object_proxy, "interface-removed", G_CALLBACK (on_interface_removed), om_data); } static void on_object_proxy_removed (GDBusObjectManagerClient *manager, GDBusObjectProxy *object_proxy, gpointer user_data) { OMData *om_data = user_data; om_data->num_object_proxy_removed_signals += 1; g_assert_cmpint (g_signal_handlers_disconnect_by_func (object_proxy, G_CALLBACK (on_interface_added), om_data), ==, 1); g_assert_cmpint (g_signal_handlers_disconnect_by_func (object_proxy, G_CALLBACK (on_interface_removed), om_data), ==, 1); } static void property_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { gboolean *changed = user_data; *changed = TRUE; } static void om_check_property_and_signal_emission (GMainLoop *loop, FooiGenBar *skeleton, FooiGenBar *proxy) { gboolean d_changed = FALSE; gboolean quiet_changed = FALSE; gboolean quiet_too_changed = FALSE; guint handler; /* First PropertiesChanged */ g_assert_cmpint (foo_igen_bar_get_i (skeleton), ==, 0); g_assert_cmpint (foo_igen_bar_get_i (proxy), ==, 0); foo_igen_bar_set_i (skeleton, 1); _g_assert_property_notify (proxy, "i"); g_assert_cmpint (foo_igen_bar_get_i (skeleton), ==, 1); g_assert_cmpint (foo_igen_bar_get_i (proxy), ==, 1); /* Double-check the gdouble case */ g_assert_cmpfloat (foo_igen_bar_get_d (skeleton), ==, 0.0); g_assert_cmpfloat (foo_igen_bar_get_d (proxy), ==, 0.0); foo_igen_bar_set_d (skeleton, 1.0); _g_assert_property_notify (proxy, "d"); /* Verify that re-setting it to the same value doesn't cause a * notify on the proxy, by taking advantage of the fact that * notifications are serialized. */ handler = g_signal_connect (proxy, "notify::d", G_CALLBACK (property_changed), &d_changed); foo_igen_bar_set_d (skeleton, 1.0); foo_igen_bar_set_i (skeleton, 2); _g_assert_property_notify (proxy, "i"); g_assert (d_changed == FALSE); g_signal_handler_disconnect (proxy, handler); /* Verify that re-setting a property with the "EmitsChangedSignal" * set to false doesn't emit a signal. */ handler = g_signal_connect (proxy, "notify::quiet", G_CALLBACK (property_changed), &quiet_changed); foo_igen_bar_set_quiet (skeleton, "hush!"); foo_igen_bar_set_i (skeleton, 3); _g_assert_property_notify (proxy, "i"); g_assert (quiet_changed == FALSE); g_assert_cmpstr (foo_igen_bar_get_quiet (skeleton), ==, "hush!"); g_signal_handler_disconnect (proxy, handler); /* Also verify that re-setting a property with the "EmitsChangedSignal" * set to 'const' doesn't emit a signal. */ handler = g_signal_connect (proxy, "notify::quiet-too", G_CALLBACK (property_changed), &quiet_changed); foo_igen_bar_set_quiet_too (skeleton, "hush too!"); foo_igen_bar_set_i (skeleton, 4); _g_assert_property_notify (proxy, "i"); g_assert (quiet_too_changed == FALSE); g_assert_cmpstr (foo_igen_bar_get_quiet_too (skeleton), ==, "hush too!"); g_signal_handler_disconnect (proxy, handler); /* Then just a regular signal */ foo_igen_bar_emit_another_signal (skeleton, "word"); _g_assert_signal_received (proxy, "another-signal"); } static void check_object_manager (void) { FooiGenObjectSkeleton *o = NULL; FooiGenObjectSkeleton *o2 = NULL; FooiGenObjectSkeleton *o3 = NULL; GDBusInterfaceSkeleton *i; GDBusConnection *c; GDBusObjectManagerServer *manager = NULL; GDBusNodeInfo *info; GError *error; GMainLoop *loop; OMData *om_data = NULL; guint om_signal_id = -1; GDBusObjectManager *pm = NULL; GList *object_proxies; GList *proxies; GDBusObject *op; GDBusProxy *p; FooiGenBar *bar_skeleton; GDBusInterface *iface; gchar *path, *name, *name_owner; GDBusConnection *c2; GDBusObjectManagerClientFlags flags; loop = g_main_loop_new (NULL, FALSE); om_data = g_new0 (OMData, 1); om_data->loop = loop; om_data->state = 0; error = NULL; c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); g_assert_no_error (error); g_assert (c != NULL); om_signal_id = g_dbus_connection_signal_subscribe (c, NULL, /* sender */ "org.freedesktop.DBus.ObjectManager", NULL, /* member */ NULL, /* object_path */ NULL, /* arg0 */ G_DBUS_SIGNAL_FLAGS_NONE, om_on_signal, om_data, NULL); /* user_data_free_func */ /* Our GDBusObjectManagerClient tests are simple - we basically just count the * number of times the various signals have been emitted (we don't check * that the right objects/interfaces are passed though - that's checked * in the lower-level tests in om_on_signal()...) * * Note that these tests rely on the D-Bus signal handlers used by * GDBusObjectManagerClient firing before om_on_signal(). */ error = NULL; pm = foo_igen_object_manager_client_new_sync (c, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, g_dbus_connection_get_unique_name (c), "/managed", NULL, /* GCancellable */ &error); g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD); g_error_free (error); g_assert (pm == NULL); manager = g_dbus_object_manager_server_new ("/managed"); g_assert (g_dbus_object_manager_server_get_connection (manager) == NULL); g_dbus_object_manager_server_set_connection (manager, c); g_assert_cmpstr (g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager)), ==, "/managed"); g_object_get (manager, "object-path", &path, "connection", &c2, NULL); g_assert_cmpstr (path, ==, "/managed"); g_assert (c2 == c); g_free (path); g_clear_object (&c2); /* Check that the manager object is visible */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed", loop); g_assert_cmpint (count_interfaces (info), ==, 4); /* ObjectManager + Properties,Introspectable,Peer */ g_assert (has_interface (info, "org.freedesktop.DBus.ObjectManager")); g_assert_cmpint (count_nodes (info), ==, 0); g_dbus_node_info_unref (info); /* Check GetManagedObjects() - should be empty since we have no objects */ om_check_get_all (c, loop, "(@a{oa{sa{sv}}} {},)"); /* Now try to create the proxy manager again - this time it should work */ error = NULL; foo_igen_object_manager_client_new (c, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, g_dbus_connection_get_unique_name (c), "/managed", NULL, /* GCancellable */ (GAsyncReadyCallback) om_pm_start_cb, loop); g_main_loop_run (loop); error = NULL; pm = foo_igen_object_manager_client_new_finish (om_res, &error); g_clear_object (&om_res); g_assert_no_error (error); g_assert (pm != NULL); g_signal_connect (pm, "object-added", G_CALLBACK (on_object_proxy_added), om_data); g_signal_connect (pm, "object-removed", G_CALLBACK (on_object_proxy_removed), om_data); g_assert_cmpstr (g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (pm)), ==, "/managed"); g_object_get (pm, "object-path", &path, "connection", &c2, "name", &name, "name-owner", &name_owner, "flags", &flags, NULL); g_assert_cmpstr (path, ==, "/managed"); g_assert_cmpstr (name, ==, g_dbus_connection_get_unique_name (c)); g_assert_cmpstr (name_owner, ==, g_dbus_connection_get_unique_name (c)); g_assert_cmpint (flags, ==, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE); g_assert (c2 == c); g_free (path); g_clear_object (&c2); g_free (name); g_free (name_owner); /* ... check there are no object proxies yet */ object_proxies = g_dbus_object_manager_get_objects (pm); g_assert (object_proxies == NULL); /* First, export an object with a single interface (also check that * g_dbus_interface_get_object() works and that the object isn't reffed) */ o = foo_igen_object_skeleton_new ("/managed/first"); i = G_DBUS_INTERFACE_SKELETON (foo_igen_bar_skeleton_new ()); g_assert (g_dbus_interface_get_object (G_DBUS_INTERFACE (i)) == NULL); g_assert_cmpint (G_OBJECT (o)->ref_count, ==, 1); foo_igen_object_skeleton_set_bar (o, FOO_IGEN_BAR (i)); g_assert_cmpint (G_OBJECT (o)->ref_count, ==, 1); g_assert (g_dbus_interface_get_object (G_DBUS_INTERFACE (i)) == G_DBUS_OBJECT (o)); g_assert_cmpint (G_OBJECT (o)->ref_count, ==, 1); foo_igen_object_skeleton_set_bar (o, NULL); g_assert (g_dbus_interface_get_object (G_DBUS_INTERFACE (i)) == NULL); g_assert_cmpint (G_OBJECT (o)->ref_count, ==, 1); foo_igen_object_skeleton_set_bar (o, FOO_IGEN_BAR (i)); g_assert (g_dbus_interface_get_object (G_DBUS_INTERFACE (i)) == G_DBUS_OBJECT (o)); g_assert_cmpint (G_OBJECT (o)->ref_count, ==, 1); o2 = FOO_IGEN_OBJECT_SKELETON (g_dbus_interface_dup_object (G_DBUS_INTERFACE (i))); g_assert (G_DBUS_OBJECT (o2) == G_DBUS_OBJECT (o)); g_assert_cmpint (G_OBJECT (o2)->ref_count, ==, 2); g_clear_object (&o2); g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (o)); /* ... check we get the InterfacesAdded signal */ om_data->state = 1; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 2); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 1); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 0); g_assert_cmpint (om_data->num_interface_added_signals, ==, 0); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 0); /* ... check there's one non-standard interfaces */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 4); /* Bar + Properties,Introspectable,Peer */ g_assert (has_interface (info, "org.project.Bar")); g_dbus_node_info_unref (info); /* Also check g_dbus_object_manager_get_interface */ iface = g_dbus_object_manager_get_interface (G_DBUS_OBJECT_MANAGER (manager), "/managed/first", "org.project.Bar"); g_assert (iface != NULL); g_clear_object (&iface); iface = g_dbus_object_manager_get_interface (G_DBUS_OBJECT_MANAGER (manager), "/managed/first", "org.project.Bat"); g_assert (iface == NULL); iface = g_dbus_object_manager_get_interface (G_DBUS_OBJECT_MANAGER (pm), "/managed/first", "org.project.Bar"); g_assert (iface != NULL); g_clear_object (&iface); iface = g_dbus_object_manager_get_interface (G_DBUS_OBJECT_MANAGER (pm), "/managed/first", "org.project.Bat"); g_assert (iface == NULL); /* Now, check adding the same interface replaces the existing one */ foo_igen_object_skeleton_set_bar (o, FOO_IGEN_BAR (i)); /* ... check we get the InterfacesRemoved */ om_data->state = 3; g_main_loop_run (om_data->loop); /* ... and then check we get the InterfacesAdded */ g_assert_cmpint (om_data->state, ==, 6); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 2); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 1); g_assert_cmpint (om_data->num_interface_added_signals, ==, 0); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 0); /* ... check introspection data */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 4); /* Bar + Properties,Introspectable,Peer */ g_assert (has_interface (info, "org.project.Bar")); g_dbus_node_info_unref (info); g_clear_object (&i); /* check adding an interface of same type (but not same object) replaces the existing one */ i = G_DBUS_INTERFACE_SKELETON (foo_igen_bar_skeleton_new ()); foo_igen_object_skeleton_set_bar (o, FOO_IGEN_BAR (i)); /* ... check we get the InterfacesRemoved and then InterfacesAdded */ om_data->state = 7; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 10); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 3); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 2); g_assert_cmpint (om_data->num_interface_added_signals, ==, 0); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 0); /* ... check introspection data */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 4); /* Bar + Properties,Introspectable,Peer */ g_assert (has_interface (info, "org.project.Bar")); g_dbus_node_info_unref (info); g_clear_object (&i); /* check adding an interface of another type doesn't replace the existing one */ i = G_DBUS_INTERFACE_SKELETON (foo_igen_bat_skeleton_new ()); foo_igen_object_skeleton_set_bat (o, FOO_IGEN_BAT (i)); g_clear_object (&i); /* ... check we get the InterfacesAdded */ om_data->state = 11; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 12); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 3); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 2); g_assert_cmpint (om_data->num_interface_added_signals, ==, 1); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 0); /* ... check introspection data */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 5); /* Bar,Bat + Properties,Introspectable,Peer */ g_assert (has_interface (info, "org.project.Bar")); g_assert (has_interface (info, "org.project.Bat")); g_dbus_node_info_unref (info); /* check we can remove an interface */ foo_igen_object_skeleton_set_bar (o, NULL); /* ... check we get the InterfacesRemoved */ om_data->state = 13; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 14); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 3); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 2); g_assert_cmpint (om_data->num_interface_added_signals, ==, 1); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 1); /* ... check introspection data */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 4); /* Bat + Properties,Introspectable,Peer */ g_assert (has_interface (info, "org.project.Bat")); g_dbus_node_info_unref (info); /* also and that the call only has effect if the interface actually exists * * (Note: if a signal was emitted we'd assert in the signal handler * because we're in state 14) */ foo_igen_object_skeleton_set_bar (o, NULL); /* ... check introspection data */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 4); /* Bat + Properties,Introspectable,Peer */ g_assert (has_interface (info, "org.project.Bat")); g_dbus_node_info_unref (info); /* remove the last interface */ foo_igen_object_skeleton_set_bat (o, NULL); /* ... check we get the InterfacesRemoved */ om_data->state = 15; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 16); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 3); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 3); g_assert_cmpint (om_data->num_interface_added_signals, ==, 1); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 1); /* ... check introspection data */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 0); /* nothing */ g_dbus_node_info_unref (info); /* and add an interface again */ i = G_DBUS_INTERFACE_SKELETON (foo_igen_com_acme_coyote_skeleton_new ()); foo_igen_object_skeleton_set_com_acme_coyote (o, FOO_IGEN_COM_ACME_COYOTE (i)); g_clear_object (&i); /* ... check we get the InterfacesAdded */ om_data->state = 17; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 18); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 4); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 3); g_assert_cmpint (om_data->num_interface_added_signals, ==, 1); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 1); /* ... check introspection data */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/first", loop); g_assert_cmpint (count_interfaces (info), ==, 4); /* com.acme.Coyote + Properties,Introspectable,Peer */ g_assert (has_interface (info, "com.acme.Coyote")); g_dbus_node_info_unref (info); /* Check GetManagedObjects() - should be just the Coyote */ om_check_get_all (c, loop, "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}},)"); /* -------------------------------------------------- */ /* create a new object with two interfaces */ o2 = foo_igen_object_skeleton_new ("/managed/second"); i = G_DBUS_INTERFACE_SKELETON (foo_igen_bar_skeleton_new ()); bar_skeleton = FOO_IGEN_BAR (i); /* save for later test */ foo_igen_object_skeleton_set_bar (o2, FOO_IGEN_BAR (i)); g_clear_object (&i); i = G_DBUS_INTERFACE_SKELETON (foo_igen_bat_skeleton_new ()); foo_igen_object_skeleton_set_bat (o2, FOO_IGEN_BAT (i)); g_clear_object (&i); /* ... add it */ g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (o2)); /* ... check we get the InterfacesAdded with _two_ interfaces */ om_data->state = 101; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 102); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 5); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 3); g_assert_cmpint (om_data->num_interface_added_signals, ==, 1); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 1); /* -------------------------------------------------- */ /* Now that we have a couple of objects with interfaces, check * that ObjectManager.GetManagedObjects() works */ om_check_get_all (c, loop, "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}, '/managed/second': {'org.project.Bar': {'y': , 'b': , 'n': , 'q': , 'i': <0>, 'u': , 'x': , 't': , 'd': <0.0>, 's': <''>, 'o': , 'g': , 'ay': , 'as': <@as []>, 'aay': <@aay []>, 'ao': <@ao []>, 'ag': <@ag []>, 'FinallyNormalName': <''>, 'ReadonlyProperty': <''>, 'quiet': <''>, 'quiet_too': <''>, 'unset_i': <0>, 'unset_d': <0.0>, 'unset_s': <''>, 'unset_o': , 'unset_g': , 'unset_ay': , 'unset_as': <@as []>, 'unset_ao': <@ao []>, 'unset_ag': <@ag []>, 'unset_struct': <(0, 0.0, '', objectpath '/', signature '', @ay [], @as [], @ao [], @ag [])>}, 'org.project.Bat': {'force_i': <0>, 'force_s': <''>, 'force_ay': <@ay []>, 'force_struct': <(0,)>}}},)"); /* Set connection to NULL, causing everything to be unexported.. verify this.. and * then set the connection back.. and then check things still work */ g_dbus_object_manager_server_set_connection (manager, NULL); info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed", loop); g_assert_cmpint (count_interfaces (info), ==, 0); /* nothing */ g_dbus_node_info_unref (info); g_dbus_object_manager_server_set_connection (manager, c); om_check_get_all (c, loop, "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}, '/managed/second': {'org.project.Bar': {'y': , 'b': , 'n': , 'q': , 'i': <0>, 'u': , 'x': , 't': , 'd': <0.0>, 's': <''>, 'o': , 'g': , 'ay': , 'as': <@as []>, 'aay': <@aay []>, 'ao': <@ao []>, 'ag': <@ag []>, 'FinallyNormalName': <''>, 'ReadonlyProperty': <''>, 'quiet': <''>, 'quiet_too': <''>, 'unset_i': <0>, 'unset_d': <0.0>, 'unset_s': <''>, 'unset_o': , 'unset_g': , 'unset_ay': , 'unset_as': <@as []>, 'unset_ao': <@ao []>, 'unset_ag': <@ag []>, 'unset_struct': <(0, 0.0, '', objectpath '/', signature '', @ay [], @as [], @ao [], @ag [])>}, 'org.project.Bat': {'force_i': <0>, 'force_s': <''>, 'force_ay': <@ay []>, 'force_struct': <(0,)>}}},)"); /* Also check that the ObjectManagerClient returns these objects - and * that they are of the right GType cf. what was requested via * the generated ::get-proxy-type signal handler */ object_proxies = g_dbus_object_manager_get_objects (pm); g_assert (g_list_length (object_proxies) == 2); g_list_free_full (object_proxies, g_object_unref); op = g_dbus_object_manager_get_object (pm, "/managed/first"); g_assert (op != NULL); g_assert (FOO_IGEN_IS_OBJECT_PROXY (op)); g_assert_cmpstr (g_dbus_object_get_object_path (op), ==, "/managed/first"); proxies = g_dbus_object_get_interfaces (op); g_assert (g_list_length (proxies) == 1); g_list_free_full (proxies, g_object_unref); p = G_DBUS_PROXY (foo_igen_object_get_com_acme_coyote (FOO_IGEN_OBJECT (op))); g_assert (p != NULL); g_assert_cmpint (G_TYPE_FROM_INSTANCE (p), ==, FOO_IGEN_TYPE_COM_ACME_COYOTE_PROXY); g_assert (g_type_is_a (G_TYPE_FROM_INSTANCE (p), FOO_IGEN_TYPE_COM_ACME_COYOTE)); g_clear_object (&p); p = (GDBusProxy *) g_dbus_object_get_interface (op, "org.project.NonExisting"); g_assert (p == NULL); g_clear_object (&op); /* -- */ op = g_dbus_object_manager_get_object (pm, "/managed/second"); g_assert (op != NULL); g_assert (FOO_IGEN_IS_OBJECT_PROXY (op)); g_assert_cmpstr (g_dbus_object_get_object_path (op), ==, "/managed/second"); proxies = g_dbus_object_get_interfaces (op); g_assert (g_list_length (proxies) == 2); g_list_free_full (proxies, g_object_unref); p = G_DBUS_PROXY (foo_igen_object_get_bat (FOO_IGEN_OBJECT (op))); g_assert (p != NULL); g_assert_cmpint (G_TYPE_FROM_INSTANCE (p), ==, FOO_IGEN_TYPE_BAT_PROXY); g_assert (g_type_is_a (G_TYPE_FROM_INSTANCE (p), FOO_IGEN_TYPE_BAT)); g_clear_object (&p); p = G_DBUS_PROXY (foo_igen_object_get_bar (FOO_IGEN_OBJECT (op))); g_assert (p != NULL); g_assert_cmpint (G_TYPE_FROM_INSTANCE (p), ==, FOO_IGEN_TYPE_BAR_PROXY); g_assert (g_type_is_a (G_TYPE_FROM_INSTANCE (p), FOO_IGEN_TYPE_BAR)); /* ... now that we have a Bar instance around, also check that we get signals * and property changes... */ om_check_property_and_signal_emission (loop, bar_skeleton, FOO_IGEN_BAR (p)); g_clear_object (&p); p = (GDBusProxy *) g_dbus_object_get_interface (op, "org.project.NonExisting"); g_assert (p == NULL); g_clear_object (&op); /* -------------------------------------------------- */ /* Now remove the second object added above */ g_dbus_object_manager_server_unexport (manager, "/managed/second"); /* ... check we get InterfacesRemoved with both interfaces */ om_data->state = 103; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 104); g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 5); g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 4); g_assert_cmpint (om_data->num_interface_added_signals, ==, 1); g_assert_cmpint (om_data->num_interface_removed_signals, ==, 1); /* ... check introspection data (there should be nothing) */ info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed/second", loop); g_assert_cmpint (count_nodes (info), ==, 0); g_assert_cmpint (count_interfaces (info), ==, 0); g_dbus_node_info_unref (info); /* Check GetManagedObjects() again */ om_check_get_all (c, loop, "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}},)"); /* -------------------------------------------------- */ /* Check that export_uniquely() works */ o3 = foo_igen_object_skeleton_new ("/managed/first"); i = G_DBUS_INTERFACE_SKELETON (foo_igen_com_acme_coyote_skeleton_new ()); foo_igen_com_acme_coyote_set_mood (FOO_IGEN_COM_ACME_COYOTE (i), "indifferent"); foo_igen_object_skeleton_set_com_acme_coyote (o3, FOO_IGEN_COM_ACME_COYOTE (i)); g_clear_object (&i); g_dbus_object_manager_server_export_uniquely (manager, G_DBUS_OBJECT_SKELETON (o3)); /* ... check we get the InterfacesAdded signal */ om_data->state = 200; g_main_loop_run (om_data->loop); g_assert_cmpint (om_data->state, ==, 201); om_check_get_all (c, loop, "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}, '/managed/first_1': {'com.acme.Coyote': {'Mood': <'indifferent'>}}},)"); //g_main_loop_run (loop); /* TODO: tmp */ /* Clean up objects */ g_assert (g_dbus_object_manager_server_unexport (manager, "/managed/first_1")); //g_assert (g_dbus_object_manager_server_unexport (manager, "/managed/second")); g_assert (g_dbus_object_manager_server_unexport (manager, "/managed/first")); g_assert_cmpint (g_list_length (g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager))), ==, 0); if (loop != NULL) g_main_loop_unref (loop); if (om_signal_id != -1) g_dbus_connection_signal_unsubscribe (c, om_signal_id); g_clear_object (&o3); g_clear_object (&o2); g_clear_object (&o); g_clear_object (&manager); if (pm != NULL) { g_assert_cmpint (g_signal_handlers_disconnect_by_func (pm, G_CALLBACK (on_object_proxy_added), om_data), ==, 1); g_assert_cmpint (g_signal_handlers_disconnect_by_func (pm, G_CALLBACK (on_object_proxy_removed), om_data), ==, 1); g_clear_object (&pm); } g_clear_object (&c); g_free (om_data); } /* ---------------------------------------------------------------------------------------------------- */ static void test_object_manager (void) { GMainLoop *loop; guint id; loop = g_main_loop_new (NULL, FALSE); id = g_bus_own_name (G_BUS_TYPE_SESSION, "org.gtk.GDBus.BindingsTool.Test", G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, loop, NULL); g_main_loop_run (loop); check_object_manager (); /* uncomment to keep the service around (to e.g. introspect it) */ /* g_main_loop_run (loop); */ unexport_objects (); g_bus_unown_name (id); g_main_loop_unref (loop); } /* ---------------------------------------------------------------------------------------------------- */ /* This checks that forcing names via org.gtk.GDBus.Name works (see test-codegen.xml) */ extern gpointer name_forcing_1; extern gpointer name_forcing_2; extern gpointer name_forcing_3; extern gpointer name_forcing_4; extern gpointer name_forcing_5; extern gpointer name_forcing_6; extern gpointer name_forcing_7; gpointer name_forcing_1 = foo_igen_rocket123_get_type; gpointer name_forcing_2 = foo_igen_rocket123_call_ignite_xyz; gpointer name_forcing_3 = foo_igen_rocket123_emit_exploded_xyz; gpointer name_forcing_4 = foo_igen_rocket123_get_speed_xyz; gpointer name_forcing_5 = foo_igen_test_ugly_case_interface_call_get_iscsi_servers; gpointer name_forcing_6 = foo_igen_test_ugly_case_interface_emit_servers_updated_now; gpointer name_forcing_7 = foo_igen_test_ugly_case_interface_get_ugly_name; /* ---------------------------------------------------------------------------------------------------- */ /* See https://bugzilla.gnome.org/show_bug.cgi?id=647577#c5 for details */ #define CHECK_FIELD(name, v1, v2) g_assert_cmpint (G_STRUCT_OFFSET (FooiGenChangingInterface##v1##Iface, name), ==, G_STRUCT_OFFSET (FooiGenChangingInterface##v2##Iface, name)); static void test_interface_stability (void) { CHECK_FIELD(handle_foo_method, V1, V2); CHECK_FIELD(handle_bar_method, V1, V2); CHECK_FIELD(handle_baz_method, V1, V2); CHECK_FIELD(foo_signal, V1, V2); CHECK_FIELD(bar_signal, V1, V2); CHECK_FIELD(baz_signal, V1, V2); CHECK_FIELD(handle_new_method_in2, V2, V10); CHECK_FIELD(new_signal_in2, V2, V10); } #undef CHECK_FIELD /* ---------------------------------------------------------------------------------------------------- */ /* property naming * * - check that a property with name "Type" is mapped into g-name "type" * with C accessors get_type_ (to avoid clashing with the GType accessor) * and set_type_ (for symmetri) * (see https://bugzilla.gnome.org/show_bug.cgi?id=679473 for details) * * - (could add more tests here) */ static void test_property_naming (void) { gpointer c_getter_name = foo_igen_naming_get_type_; gpointer c_setter_name = foo_igen_naming_set_type_; FooiGenNaming *skel; (void) c_getter_name; (void) c_setter_name; skel = foo_igen_naming_skeleton_new (); g_assert (g_object_class_find_property (G_OBJECT_GET_CLASS (skel), "type") != NULL); g_object_unref (skel); } /* ---------------------------------------------------------------------------------------------------- */ /* autocleanups * * - check that g_autoptr() works for all generated types, if supported by the * current compiler */ static void test_autocleanups (void) { #ifdef g_autoptr g_autoptr(FooiGenBar) bar = NULL; g_autoptr(FooiGenBarProxy) bar_proxy = NULL; g_autoptr(FooiGenBarSkeleton) bar_skeleton = NULL; g_autoptr(FooiGenObject) object = NULL; g_autoptr(FooiGenObjectProxy) object_proxy = NULL; g_autoptr(FooiGenObjectSkeleton) object_skeleton = NULL; g_autoptr(FooiGenObjectManagerClient) object_manager_client = NULL; (void) bar; (void) bar_proxy; (void) bar_skeleton; (void) object; (void) object_proxy; (void) object_skeleton; (void) object_manager_client; #elif GLIB_CHECK_VERSION(2, 38, 0) /* This file is compiled twice, once without GLib version guards and once * with * * -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36 * -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36 * * g_test_skip() was added in 2.38. */ g_test_skip ("g_autoptr() not supported on this compiler"); #else /* Let's just say it passed. */ #endif } /* ---------------------------------------------------------------------------------------------------- */ /* deprecations */ static void test_deprecations (void) { { FooiGenOldieInterface *iskel; GParamSpec *pspec; G_GNUC_BEGIN_IGNORE_DEPRECATIONS; iskel = foo_igen_oldie_interface_skeleton_new (); G_GNUC_END_IGNORE_DEPRECATIONS; pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (iskel), "bat"); g_assert_nonnull (pspec); g_assert_cmpint (pspec->flags & G_PARAM_DEPRECATED, ==, G_PARAM_DEPRECATED); g_object_unref (iskel); } { FooiGenObjectSkeleton *oskel; GParamSpec *pspec; oskel = foo_igen_object_skeleton_new ("/objects/first"); pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (oskel), "oldie-interface"); g_assert_nonnull (pspec); g_assert_cmpint (pspec->flags & G_PARAM_DEPRECATED, ==, G_PARAM_DEPRECATED); g_object_unref (oskel); } } /* ---------------------------------------------------------------------------------------------------- */ static void assert_arg_infos_equal (GDBusArgInfo **a, GDBusArgInfo **b) { if (a == NULL) { g_assert_null (b); return; } g_assert_nonnull (b); for (; *a != NULL && *b != NULL; a++, b++) { g_assert_cmpstr ((*a)->name, ==, (*b)->name); g_assert_cmpstr ((*a)->signature, ==, (*b)->signature); } g_assert_null (*a); g_assert_null (*b); } static void assert_annotations_equal (GDBusAnnotationInfo **a, GDBusAnnotationInfo **b) { guint a_len = count_annotations (a); guint b_len = count_annotations (b); g_assert_cmpuint (a_len, ==, b_len); if (a == NULL || b == NULL) return; for (; *a != NULL && *b != NULL; a++, b++) { g_assert_cmpstr ((*a)->key, ==, (*b)->key); g_assert_cmpstr ((*a)->value, ==, (*b)->value); assert_annotations_equal ((*a)->annotations, (*b)->annotations); } g_assert_null (*a); g_assert_null (*b); } /* Test that the GDBusInterfaceInfo structure generated by gdbus-codegen * --interface-info-body matches that generated by the other mode. */ static void test_standalone_interface_info (void) { GDBusInterfaceSkeleton *skel = G_DBUS_INTERFACE_SKELETON (foo_igen_bar_skeleton_new ()); GDBusInterfaceInfo *skel_info = g_dbus_interface_skeleton_get_info (skel); const GDBusInterfaceInfo *slim_info = &org_project_bar_interface; gsize i; g_assert_cmpstr (skel_info->name, ==, slim_info->name); for (i = 0; skel_info->methods[i] != NULL; i++) { GDBusMethodInfo *skel_method = skel_info->methods[i]; GDBusMethodInfo *slim_method = slim_info->methods[i]; g_assert_nonnull (slim_method); g_assert_cmpstr (skel_method->name, ==, slim_method->name); assert_arg_infos_equal (skel_method->in_args, slim_method->in_args); assert_arg_infos_equal (skel_method->out_args, slim_method->out_args); assert_annotations_equal (skel_method->annotations, slim_method->annotations); } g_assert_null (slim_info->methods[i]); for (i = 0; skel_info->signals[i] != NULL; i++) { GDBusSignalInfo *skel_signal = skel_info->signals[i]; GDBusSignalInfo *slim_signal = slim_info->signals[i]; g_assert_nonnull (slim_signal); g_assert_cmpstr (skel_signal->name, ==, slim_signal->name); assert_arg_infos_equal (skel_signal->args, slim_signal->args); assert_annotations_equal (skel_signal->annotations, slim_signal->annotations); } g_assert_null (slim_info->signals[i]); for (i = 0; skel_info->properties[i] != NULL; i++) { GDBusPropertyInfo *skel_prop = skel_info->properties[i]; GDBusPropertyInfo *slim_prop = slim_info->properties[i]; g_assert_nonnull (slim_prop); g_assert_cmpstr (skel_prop->name, ==, slim_prop->name); g_assert_cmpstr (skel_prop->signature, ==, slim_prop->signature); g_assert_cmpuint (skel_prop->flags, ==, slim_prop->flags); assert_annotations_equal (skel_prop->annotations, slim_prop->annotations); } g_assert_null (slim_info->properties[i]); assert_annotations_equal (skel_info->annotations, slim_info->annotations); g_clear_object (&skel); } /* ---------------------------------------------------------------------------------------------------- */ static gboolean handle_hello_fd (FooiGenFDPassing *object, GDBusMethodInvocation *invocation, GUnixFDList *fd_list, const gchar *arg_greeting) { foo_igen_fdpassing_complete_hello_fd (object, invocation, fd_list, arg_greeting); return TRUE; } #if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64 static gboolean handle_no_annotation (FooiGenFDPassing *object, GDBusMethodInvocation *invocation, GUnixFDList *fd_list, GVariant *arg_greeting, const gchar *arg_greeting_locale) { foo_igen_fdpassing_complete_no_annotation (object, invocation, fd_list, arg_greeting, arg_greeting_locale); return TRUE; } static gboolean handle_no_annotation_nested (FooiGenFDPassing *object, GDBusMethodInvocation *invocation, GUnixFDList *fd_list, GVariant *arg_files) { foo_igen_fdpassing_complete_no_annotation_nested (object, invocation, fd_list); return TRUE; } #else static gboolean handle_no_annotation (FooiGenFDPassing *object, GDBusMethodInvocation *invocation, GVariant *arg_greeting, const gchar *arg_greeting_locale) { foo_igen_fdpassing_complete_no_annotation (object, invocation, arg_greeting, arg_greeting_locale); return TRUE; } static gboolean handle_no_annotation_nested (FooiGenFDPassing *object, GDBusMethodInvocation *invocation, GVariant *arg_files) { foo_igen_fdpassing_complete_no_annotation_nested (object, invocation); return TRUE; } #endif /* Test that generated code for methods includes GUnixFDList arguments * unconditionally if the method is explicitly annotated as C.UnixFD, and only * emits GUnixFDList arguments when there's merely an 'h' parameter if * --glib-min-version=2.64 or greater. */ static void test_unix_fd_list (void) { FooiGenFDPassingIface iface; g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1726"); /* This method is explicitly annotated. */ iface.handle_hello_fd = handle_hello_fd; /* This one is not annotated; even though it's got an in and out 'h' * parameter, for backwards compatibility we cannot emit GUnixFDList * arguments unless --glib-min-version >= 2.64 was used. */ iface.handle_no_annotation = handle_no_annotation; /* This method has an 'h' inside a complex type. */ iface.handle_no_annotation_nested = handle_no_annotation_nested; (void) iface; } /* ---------------------------------------------------------------------------------------------------- */ int main (int argc, char *argv[]) { g_test_init (&argc, &argv, NULL); g_test_add_func ("/gdbus/codegen/annotations", test_annotations); g_test_add_func ("/gdbus/codegen/interface_stability", test_interface_stability); g_test_add_func ("/gdbus/codegen/object-manager", test_object_manager); g_test_add_func ("/gdbus/codegen/property-naming", test_property_naming); g_test_add_func ("/gdbus/codegen/autocleanups", test_autocleanups); g_test_add_func ("/gdbus/codegen/deprecations", test_deprecations); g_test_add_func ("/gdbus/codegen/standalone-interface-info", test_standalone_interface_info); g_test_add_func ("/gdbus/codegen/unix-fd-list", test_unix_fd_list); return session_bus_run (); }