| /* GLib testing framework examples and tests | |
| * | |
| * Copyright (C) 2012 Red Hat Inc. | |
| * | |
| * SPDX-License-Identifier: LGPL-2.1-or-later | |
| * | |
| * 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 <http://www.gnu.org/licenses/>. | |
| * | |
| * Author: Stef Walter <stefw@gnome.org> | |
| */ | |
| #include "config.h" | |
| #include <gio/gio.h> | |
| #include <sys/socket.h> | |
| #include <errno.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| typedef struct { | |
| GDBusInterfaceSkeleton parent; | |
| gint number; | |
| } MockInterface; | |
| typedef struct { | |
| GDBusInterfaceSkeletonClass parent_class; | |
| } MockInterfaceClass; | |
| static GType mock_interface_get_type (void); | |
| G_DEFINE_TYPE (MockInterface, mock_interface, G_TYPE_DBUS_INTERFACE_SKELETON) | |
| static void | |
| mock_interface_init (MockInterface *self) | |
| { | |
| } | |
| static GDBusInterfaceInfo * | |
| mock_interface_get_info (GDBusInterfaceSkeleton *skeleton) | |
| { | |
| static GDBusPropertyInfo path_info = { | |
| -1, | |
| "Path", | |
| "o", | |
| G_DBUS_PROPERTY_INFO_FLAGS_READABLE, | |
| NULL, | |
| }; | |
| static GDBusPropertyInfo number_info = { | |
| -1, | |
| "Number", | |
| "i", | |
| G_DBUS_PROPERTY_INFO_FLAGS_READABLE, | |
| NULL, | |
| }; | |
| static GDBusPropertyInfo *property_info[] = { | |
| &path_info, | |
| &number_info, | |
| NULL | |
| }; | |
| static GDBusInterfaceInfo interface_info = { | |
| -1, | |
| (gchar *) "org.mock.Interface", | |
| NULL, | |
| NULL, | |
| (GDBusPropertyInfo **) &property_info, | |
| NULL | |
| }; | |
| return &interface_info; | |
| } | |
| static GVariant * | |
| mock_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) | |
| { | |
| MockInterface *self = user_data; | |
| if (g_str_equal (property_name, "Path")) | |
| return g_variant_new_object_path (object_path); | |
| else if (g_str_equal (property_name, "Number")) | |
| return g_variant_new_int32 (self->number); | |
| else | |
| return NULL; | |
| } | |
| static GDBusInterfaceVTable * | |
| mock_interface_get_vtable (GDBusInterfaceSkeleton *interface) | |
| { | |
| static GDBusInterfaceVTable vtable = { | |
| NULL, | |
| mock_interface_get_property, | |
| NULL, | |
| { 0 } | |
| }; | |
| return &vtable; | |
| } | |
| static GVariant * | |
| mock_interface_get_properties (GDBusInterfaceSkeleton *interface) | |
| { | |
| GVariantBuilder builder; | |
| GDBusInterfaceInfo *info; | |
| GDBusInterfaceVTable *vtable; | |
| guint n; | |
| /* Groan, this is completely generic code and should be in gdbus */ | |
| info = g_dbus_interface_skeleton_get_info (interface); | |
| vtable = g_dbus_interface_skeleton_get_vtable (interface); | |
| g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); | |
| for (n = 0; info->properties[n] != NULL; n++) | |
| { | |
| if (info->properties[n]->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) | |
| { | |
| GVariant *value; | |
| g_return_val_if_fail (vtable->get_property != NULL, NULL); | |
| value = (vtable->get_property) (g_dbus_interface_skeleton_get_connection (interface), NULL, | |
| g_dbus_interface_skeleton_get_object_path (interface), | |
| info->name, info->properties[n]->name, | |
| NULL, interface); | |
| if (value != NULL) | |
| { | |
| g_variant_take_ref (value); | |
| g_variant_builder_add (&builder, "{sv}", info->properties[n]->name, value); | |
| g_variant_unref (value); | |
| } | |
| } | |
| } | |
| return g_variant_builder_end (&builder); | |
| } | |
| static void | |
| mock_interface_flush (GDBusInterfaceSkeleton *skeleton) | |
| { | |
| } | |
| static void | |
| mock_interface_class_init (MockInterfaceClass *klass) | |
| { | |
| GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass); | |
| skeleton_class->get_info = mock_interface_get_info; | |
| skeleton_class->get_properties = mock_interface_get_properties; | |
| skeleton_class->flush = mock_interface_flush; | |
| skeleton_class->get_vtable = mock_interface_get_vtable; | |
| } | |
| typedef struct { | |
| GDBusConnection *server; | |
| GDBusConnection *client; | |
| GMainLoop *loop; | |
| GAsyncResult *result; | |
| } Test; | |
| static void | |
| on_server_connection (GObject *source, | |
| GAsyncResult *result, | |
| gpointer user_data) | |
| { | |
| Test *test = user_data; | |
| GError *error = NULL; | |
| g_assert_null (test->server); | |
| test->server = g_dbus_connection_new_finish (result, &error); | |
| g_assert_no_error (error); | |
| g_assert_nonnull (test->server); | |
| if (test->server && test->client) | |
| g_main_loop_quit (test->loop); | |
| } | |
| static void | |
| on_client_connection (GObject *source, | |
| GAsyncResult *result, | |
| gpointer user_data) | |
| { | |
| Test *test = user_data; | |
| GError *error = NULL; | |
| g_assert_null (test->client); | |
| test->client = g_dbus_connection_new_finish (result, &error); | |
| g_assert_no_error (error); | |
| g_assert_nonnull (test->client); | |
| if (test->server && test->client) | |
| g_main_loop_quit (test->loop); | |
| } | |
| static void | |
| setup (Test *test, | |
| gconstpointer unused) | |
| { | |
| GError *error = NULL; | |
| GSocket *socket; | |
| GSocketConnection *stream; | |
| gchar *guid; | |
| int pair[2]; | |
| test->loop = g_main_loop_new (NULL, FALSE); | |
| if (socketpair (AF_UNIX, SOCK_STREAM, 0, pair) < 0) | |
| { | |
| int errsv = errno; | |
| g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (errsv), | |
| "%s", g_strerror (errsv)); | |
| g_assert_no_error (error); | |
| } | |
| /* Build up the server stuff */ | |
| socket = g_socket_new_from_fd (pair[1], &error); | |
| g_assert_no_error (error); | |
| stream = g_socket_connection_factory_create_connection (socket); | |
| g_assert_nonnull (stream); | |
| g_object_unref (socket); | |
| guid = g_dbus_generate_guid (); | |
| g_dbus_connection_new (G_IO_STREAM (stream), guid, | |
| G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER | | |
| G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, | |
| NULL, NULL, on_server_connection, test); | |
| g_object_unref (stream); | |
| g_free (guid); | |
| /* Build up the client stuff */ | |
| socket = g_socket_new_from_fd (pair[0], &error); | |
| g_assert_no_error (error); | |
| stream = g_socket_connection_factory_create_connection (socket); | |
| g_assert_nonnull (stream); | |
| g_object_unref (socket); | |
| g_dbus_connection_new (G_IO_STREAM (stream), NULL, | |
| G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | | |
| G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, | |
| NULL, NULL, on_client_connection, test); | |
| g_main_loop_run (test->loop); | |
| g_assert_nonnull (test->server); | |
| g_assert_nonnull (test->client); | |
| g_object_unref (stream); | |
| } | |
| static void | |
| teardown (Test *test, | |
| gconstpointer unused) | |
| { | |
| g_clear_object (&test->client); | |
| g_clear_object (&test->server); | |
| g_main_loop_unref (test->loop); | |
| } | |
| static void | |
| on_result (GObject *source, | |
| GAsyncResult *result, | |
| gpointer user_data) | |
| { | |
| Test *test = user_data; | |
| g_assert_null (test->result); | |
| test->result = g_object_ref (result); | |
| g_main_loop_quit (test->loop); | |
| } | |
| static void | |
| test_object_manager (Test *test, | |
| gconstpointer test_data) | |
| { | |
| GDBusObjectManager *client; | |
| GDBusObjectManagerServer *server; | |
| MockInterface *mock; | |
| GDBusObjectSkeleton *skeleton; | |
| const gchar *dbus_name; | |
| GError *error = NULL; | |
| GDBusInterface *proxy; | |
| GVariant *prop; | |
| const gchar *object_path = test_data; | |
| gchar *number1_path = NULL, *number2_path = NULL; | |
| if (g_strcmp0 (object_path, "/") == 0) | |
| { | |
| number1_path = g_strdup ("/number_1"); | |
| number2_path = g_strdup ("/number_2"); | |
| } | |
| else | |
| { | |
| number1_path = g_strdup_printf ("%s/number_1", object_path); | |
| number2_path = g_strdup_printf ("%s/number_2", object_path); | |
| } | |
| server = g_dbus_object_manager_server_new (object_path); | |
| mock = g_object_new (mock_interface_get_type (), NULL); | |
| mock->number = 1; | |
| skeleton = g_dbus_object_skeleton_new (number1_path); | |
| g_dbus_object_skeleton_add_interface (skeleton, G_DBUS_INTERFACE_SKELETON (mock)); | |
| g_dbus_object_manager_server_export (server, skeleton); | |
| mock = g_object_new (mock_interface_get_type (), NULL); | |
| mock->number = 2; | |
| skeleton = g_dbus_object_skeleton_new (number2_path); | |
| g_dbus_object_skeleton_add_interface (skeleton, G_DBUS_INTERFACE_SKELETON (mock)); | |
| g_dbus_object_manager_server_export (server, skeleton); | |
| g_dbus_object_manager_server_set_connection (server, test->server); | |
| dbus_name = NULL; | |
| g_dbus_object_manager_client_new (test->client, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, | |
| dbus_name, object_path, NULL, NULL, NULL, NULL, on_result, test); | |
| g_main_loop_run (test->loop); | |
| client = g_dbus_object_manager_client_new_finish (test->result, &error); | |
| g_assert_no_error (error); | |
| g_clear_object (&test->result); | |
| proxy = g_dbus_object_manager_get_interface (client, number1_path, "org.mock.Interface"); | |
| g_assert_nonnull (proxy); | |
| prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Path"); | |
| g_assert_nonnull (prop); | |
| g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_OBJECT_PATH); | |
| g_assert_cmpstr (g_variant_get_string (prop, NULL), ==, number1_path); | |
| g_variant_unref (prop); | |
| prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Number"); | |
| g_assert_nonnull (prop); | |
| g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_INT32); | |
| g_assert_cmpint (g_variant_get_int32 (prop), ==, 1); | |
| g_variant_unref (prop); | |
| g_object_unref (proxy); | |
| proxy = g_dbus_object_manager_get_interface (client, number2_path, "org.mock.Interface"); | |
| g_assert_nonnull (proxy); | |
| prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Path"); | |
| g_assert_nonnull (prop); | |
| g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_OBJECT_PATH); | |
| g_assert_cmpstr (g_variant_get_string (prop, NULL), ==, number2_path); | |
| g_variant_unref (prop); | |
| prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Number"); | |
| g_assert_nonnull (prop); | |
| g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_INT32); | |
| g_assert_cmpint (g_variant_get_int32 (prop), ==, 2); | |
| g_variant_unref (prop); | |
| g_object_unref (proxy); | |
| g_object_unref (server); | |
| g_object_unref (client); | |
| g_free (number2_path); | |
| g_free (number1_path); | |
| } | |
| int | |
| main (int argc, | |
| char *argv[]) | |
| { | |
| g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); | |
| g_test_add ("/gdbus/peer-object-manager/normal", Test, "/objects", | |
| setup, test_object_manager, teardown); | |
| g_test_add ("/gdbus/peer-object-manager/root", Test, "/", | |
| setup, test_object_manager, teardown); | |
| return g_test_run(); | |
| } |