/*
* This file is part of Cockpit.
*
* Copyright (C) 2013 Red Hat, Inc.
*
* Cockpit 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.
*
* Cockpit 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 Cockpit; If not, see .
*/
#include "config.h"
#include "mock-service.h"
#include "mock-dbus-tests.h"
#include "cockpitunixfd.h"
#include
#include
#include
#include
typedef struct {
GDBusConnection *connection;
GDBusObjectManagerServer *object_manager;
GHashTable *other_names;
} MockData;
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
on_handle_hello_world (TestFrobber *object,
GDBusMethodInvocation *invocation,
const gchar *greeting,
gpointer user_data)
{
gchar *response;
response = g_strdup_printf ("Word! You said `%s'. I'm Skeleton, btw!", greeting);
test_frobber_complete_hello_world (object, invocation, response);
g_free (response);
return TRUE;
}
static gboolean
on_handle_never_return (TestFrobber *object,
GDBusMethodInvocation *invocation,
const gchar *greeting,
gpointer user_data)
{
return TRUE;
}
static gboolean
on_handle_test_primitive_types (TestFrobber *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);
test_frobber_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 (TestFrobber *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);
test_frobber_complete_test_non_primitive_types (object, invocation, str->str);
g_string_free (str, TRUE);
return TRUE;
}
static gboolean
on_handle_test_variant (TestFrobber *object,
GDBusMethodInvocation *invocation,
GVariant *v,
gpointer user_data)
{
test_frobber_complete_test_variant (object, invocation);
return TRUE;
}
static gboolean
on_handle_request_signal_emission (TestFrobber *object,
GDBusMethodInvocation *invocation,
gint which_one,
gpointer user_data)
{
if (which_one == 0)
{
const gchar *a_strv[] = {"foo", "frobber", NULL};
const gchar *a_objpath_array[] = {"/foo", "/foo/bar", NULL};
GVariant *a_variant = g_variant_new_parsed ("{'first': (42, 42), 'second': (43, 43)}");
test_frobber_emit_test_signal (object, 43, a_strv, a_objpath_array, a_variant); /* consumes a_variant */
test_frobber_complete_request_signal_emission (object, invocation);
}
return TRUE;
}
static gboolean
on_handle_request_property_mods (TestFrobber *object,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
test_frobber_set_y (object, test_frobber_get_y (object) + 1);
test_frobber_set_i (object, test_frobber_get_i (object) + 1);
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (object));
test_frobber_complete_request_multi_property_mods (object, invocation);
return TRUE;
}
static gboolean
on_handle_request_multi_property_mods (TestFrobber *object,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
test_frobber_set_y (object, test_frobber_get_y (object) + 1);
test_frobber_set_i (object, test_frobber_get_i (object) + 1);
test_frobber_set_y (object, test_frobber_get_y (object) + 1);
test_frobber_set_i (object, test_frobber_get_i (object) + 1);
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (object));
test_frobber_set_y (object, test_frobber_get_y (object) + 1);
test_frobber_set_i (object, test_frobber_get_i (object) + 1);
test_frobber_complete_request_multi_property_mods (object, invocation);
return TRUE;
}
static gboolean
on_handle_property_cancellation (TestFrobber *object,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
guint n;
n = test_frobber_get_n (object);
/* This queues up a PropertiesChange event */
test_frobber_set_n (object, n + 1);
/* this modifies the queued up event */
test_frobber_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 */
test_frobber_complete_property_cancellation (object, invocation);
return TRUE;
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
on_handle_create_object (TestFrobber *object,
GDBusMethodInvocation *invocation,
const gchar *at_path,
gpointer user_data)
{
MockData *data = user_data;
GDBusObject *previous;
previous = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (data->object_manager), at_path);
if (previous != NULL)
{
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR, G_IO_ERROR_FAILED,
"Sorry, object already exists at %s",
at_path);
g_object_unref (previous);
}
else
{
TestObjectSkeleton *new_object;
TestFrobber *frobber;
new_object = test_object_skeleton_new (at_path);
frobber = test_frobber_skeleton_new ();
test_object_skeleton_set_frobber (new_object, frobber);
g_dbus_object_manager_server_export (data->object_manager, G_DBUS_OBJECT_SKELETON (new_object));
g_object_unref (frobber);
g_object_unref (new_object);
g_signal_connect (frobber,
"handle-request-property-mods",
G_CALLBACK (on_handle_request_property_mods),
NULL);
test_frobber_complete_create_object (object, invocation);
}
return TRUE;
}
static gboolean
on_handle_delete_object (TestFrobber *object,
GDBusMethodInvocation *invocation,
const gchar *path,
gpointer user_data)
{
MockData *data = user_data;
GDBusObject *previous;
previous = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (data->object_manager), path);
if (previous != NULL)
{
g_warn_if_fail (g_dbus_object_manager_server_unexport (data->object_manager, path));
test_frobber_complete_delete_object (object, invocation);
g_object_unref (previous);
}
else
{
g_dbus_method_invocation_return_error (invocation,
G_IO_ERROR, G_IO_ERROR_FAILED,
"Sorry, there is no object at %s",
path);
}
return TRUE;
}
static gboolean
on_handle_delete_all_objects (TestFrobber *object,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
MockData *data = user_data;
const gchar *path;
GList *objects;
GList *l;
objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (data->object_manager));
for (l = objects; l != NULL; l = g_list_next (l))
{
path = g_dbus_object_get_object_path (l->data);
if (!g_str_has_suffix (path, "/frobber"))
g_warn_if_fail (g_dbus_object_manager_server_unexport (data->object_manager, path));
}
test_frobber_complete_delete_all_objects (object, invocation);
g_list_free_full (objects, g_object_unref);
return TRUE;
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
on_handle_test_asv (TestFrobber *object,
GDBusMethodInvocation *invocation,
GVariant *asv,
gpointer user_data)
{
gchar *s;
s = g_variant_print (asv, TRUE);
test_frobber_complete_test_asv (object, invocation, s);
g_free (s);
return TRUE;
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean
on_handle_add_alpha (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
TestObjectSkeleton *enclosing;
enclosing = TEST_OBJECT_SKELETON (g_dbus_interface_get_object (G_DBUS_INTERFACE (frobber)));
if (test_object_peek_alpha (TEST_OBJECT (enclosing)) == NULL)
{
TestAlpha *iface = test_alpha_skeleton_new ();
test_object_skeleton_set_alpha (enclosing, iface);
g_object_unref (iface);
}
test_frobber_complete_add_alpha (frobber, invocation);
return TRUE;
}
static gboolean
on_handle_remove_alpha (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
TestObjectSkeleton *enclosing;
enclosing = TEST_OBJECT_SKELETON (g_dbus_interface_get_object (G_DBUS_INTERFACE (frobber)));
if (test_object_peek_alpha (TEST_OBJECT (enclosing)) != NULL)
test_object_skeleton_set_alpha (enclosing, NULL);
test_frobber_complete_add_alpha (frobber, invocation);
return TRUE;
}
/* ---------------------------------------------------------------------------------------------------- */
typedef struct {
GDBusMethodInvocation *invocation;
} ClaimNameData;
static void
claim_name_data_free (gpointer data)
{
ClaimNameData *claim_data = data;
if (claim_data->invocation)
g_object_unref (claim_data->invocation);
g_free (claim_data);
}
static void
on_other_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
ClaimNameData *claim_data = user_data;
if (claim_data->invocation)
{
g_dbus_method_invocation_return_value (claim_data->invocation, NULL);
g_clear_object (&claim_data->invocation);
}
}
static void
on_other_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
ClaimNameData *claim_data = user_data;
if (claim_data->invocation)
{
g_dbus_method_invocation_return_error (claim_data->invocation,
G_IO_ERROR, G_IO_ERROR_FAILED,
"Couldn't claim name: %s", name);
g_message ("couldn't claim name: %s", name);
g_clear_object (&claim_data->invocation);
}
}
static gboolean
on_claim_other_name (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
const gchar *name,
gpointer user_data)
{
ClaimNameData *claim_data;
MockData *mock_data = user_data;
guint id;
g_return_val_if_fail (g_hash_table_lookup (mock_data->other_names, name) == NULL, FALSE);
claim_data = g_new0 (ClaimNameData, 1);
claim_data->invocation = g_object_ref (invocation);
id = g_bus_own_name_on_connection (mock_data->connection, name,
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_NONE,
on_other_name_acquired,
on_other_name_lost,
claim_data, claim_name_data_free);
g_hash_table_replace (mock_data->other_names, g_strdup (name), GUINT_TO_POINTER (id));
return TRUE;
}
static gboolean
on_release_other_name (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
const gchar *name,
gpointer user_data)
{
MockData *mock_data = user_data;
guint id;
g_return_val_if_fail (g_hash_table_lookup (mock_data->other_names, name) != NULL, FALSE);
id = GPOINTER_TO_UINT (g_hash_table_lookup (mock_data->other_names, name));
g_hash_table_remove (mock_data->other_names, name);
g_bus_unown_name (id);
g_dbus_method_invocation_return_value (invocation, NULL);
return TRUE;
}
static gboolean
on_tell_me_your_name (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
GDBusMessage *message = g_dbus_method_invocation_get_message (invocation);
GVariant *name = g_variant_new_string (g_dbus_message_get_destination (message));
g_dbus_method_invocation_return_value (invocation, g_variant_new_tuple (&name, 1));
return TRUE;
}
static gboolean
test_fd_pipe_readable (gint fd,
GIOCondition condition,
gpointer user_data)
{
const char *expected = "Hello, fd";
size_t expected_len = strlen (expected);
char buffer[100];
size_t n;
g_assert (condition == G_IO_IN);
n = read (fd, buffer, 100);
g_assert (n == expected_len);
g_assert (strncmp (buffer, expected, expected_len) == 0);
close (fd);
return G_SOURCE_REMOVE;
}
static gboolean
on_make_test_fd (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
const char *type,
gpointer user_data)
{
gint fds[2] = { -1, -1 };
GUnixFDList *fd_list;
const char *data = "Hello, fd";
size_t data_size = strlen (data);
size_t n;
GError *error = NULL;
g_unix_open_pipe (fds, FD_CLOEXEC, &error);
g_assert_no_error (error);
fd_list = g_unix_fd_list_new ();
if (g_str_equal (type, "readable"))
{
g_unix_fd_list_append (fd_list, fds[0], &error);
g_assert_no_error (error);
close (fds[0]);
n = write(fds[1], data, data_size);
g_assert (n == data_size);
close (fds[1]);
}
else if (g_str_equal (type, "writable"))
{
g_unix_fd_list_append (fd_list, fds[1], &error);
g_assert_no_error (error);
close (fds[1]);
cockpit_unix_fd_add (fds[0], G_IO_IN, test_fd_pipe_readable, NULL);
}
else
g_assert_not_reached ();
g_dbus_method_invocation_return_value_with_unix_fd_list (invocation, g_variant_new ("(h)", 0), fd_list);
return TRUE;
}
/* ------------------------------------------------------------------------
* Non object manager stuff
*/
static GVariant *
clique_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
/* The only property is Friend */
gchar *friend = user_data;
return g_variant_new_object_path (friend);
}
static gboolean
on_create_clique (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
const gchar *name,
gpointer user_data)
{
GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation);
GError *error = NULL;
gchar *path = NULL;
gchar *friend;
gint i;
static const GDBusInterfaceVTable vtable = {
.method_call = NULL,
.get_property = clique_get_property,
.set_property = NULL,
};
for (i = 0; i < 3; i++)
{
g_free (path);
path = g_strdup_printf ("/cliques/%s/%d", name, i);
friend = g_strdup_printf ("/cliques/%s/%d", name, (i + 1) % 3);
g_dbus_connection_register_object (connection, path,
test_clique_interface_info (),
&vtable, friend, g_free, &error);
if (error)
{
g_critical ("Couldn't register new clique: %s", error->message);
g_clear_error (&error);
}
}
test_frobber_complete_create_clique (frobber, invocation, path);
g_free (path);
return TRUE;
}
static GVariant *
hidden_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
/* The only property is Name */
gchar *name = user_data;
return g_variant_new_string (name);
}
static gboolean
on_emit_hidden (TestFrobber *frobber,
GDBusMethodInvocation *invocation,
const gchar *name,
gpointer user_data)
{
GDBusConnection *connection = g_dbus_method_invocation_get_connection (invocation);
GError *error = NULL;
gchar *path = NULL;
static const GDBusInterfaceVTable vtable = {
.method_call = NULL,
.get_property = hidden_get_property,
.set_property = NULL,
};
path = g_strdup_printf ("/hidden/%s", name);
g_dbus_connection_register_object (connection, path,
test_hidden_interface_info (),
&vtable, g_strdup (name), g_free, &error);
if (error)
{
g_critical ("Couldn't register new hidden: %s", error->message);
g_clear_error (&error);
}
g_dbus_connection_emit_signal (connection, NULL, path,
"com.redhat.Cockpit.DBusTests.Hidden", "Yooohooo",
g_variant_new ("()"), &error);
if (error)
{
g_critical ("Couldn't emit signal on hidden: %s", error->message);
g_clear_error (&error);
}
/* Now emit a signal from it */
test_frobber_complete_emit_hidden (frobber, invocation);
g_free (path);
return TRUE;
}
/* ----------------------------------------------------------------------------------------------------
* An Introspect() that actually fails
*/
static void
introspect_fail_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
const gchar *dbus_error = user_data;
g_dbus_method_invocation_return_dbus_error (invocation, dbus_error, dbus_error);
}
static void
mock_service_create_introspect_fail (GDBusConnection *connection)
{
static const gchar introspectable_xml[] =
""
" "
" "
" "
" "
" "
"";
const GDBusInterfaceVTable introspect_vtable = {
.method_call = introspect_fail_method_call,
};
GDBusNodeInfo *node_info;
GDBusInterfaceInfo *interface_info;
GError *error = NULL;
node_info = g_dbus_node_info_new_for_xml (introspectable_xml, &error);
g_assert_no_error (error);
interface_info = g_dbus_node_info_lookup_interface (node_info, "org.freedesktop.DBus.Introspectable");
g_assert (interface_info != NULL);
/* Return a failure when introspecting this object path */
g_dbus_connection_register_object (connection, "/introspect/unknown", interface_info, &introspect_vtable,
"org.freedesktop.DBus.Error.UnknownObject", NULL, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
g_error_free (error);
else
g_assert_no_error (error);
g_dbus_node_info_unref (node_info);
}
/* ---------------------------------------------------------------------------------------------------- */
static void
mock_data_free (gpointer data)
{
MockData *mock_data = data;
g_object_unref (mock_data->connection);
g_hash_table_destroy (mock_data->other_names);
g_free (mock_data);
}
GObject *
mock_service_create_and_export (GDBusConnection *connection,
const gchar *object_manager_path)
{
GError *error;
TestFrobber *exported_frobber;
TestObjectSkeleton *exported_object;
GDBusObjectManagerServer *object_manager;
MockData *mock_data;
gchar *path;
mock_data = g_new0 (MockData, 1);
mock_data->connection = g_object_ref (connection);
mock_data->other_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
/* Test that we can export an object using the generated
* TestFrobberSkeleton 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_frobber = test_frobber_skeleton_new ();
test_frobber_set_ay (exported_frobber, "ABCabc");
test_frobber_set_y (exported_frobber, 42);
test_frobber_set_d (exported_frobber, 43.0);
test_frobber_set_finally_normal_name (exported_frobber, "There aint no place like home");
test_frobber_set_writeonly_property (exported_frobber, "Mr. Burns");
test_frobber_set_readonly_property (exported_frobber, "blah");
object_manager = g_dbus_object_manager_server_new (object_manager_path);
mock_data->object_manager = object_manager;
g_object_set_data_full (G_OBJECT (object_manager), "mock-data", mock_data, mock_data_free);
path = g_strdup_printf ("%s/frobber", object_manager_path);
exported_object = test_object_skeleton_new (path);
g_free (path);
test_object_skeleton_set_frobber (exported_object, exported_frobber);
g_dbus_object_manager_server_export (object_manager, G_DBUS_OBJECT_SKELETON (exported_object));
g_object_unref (exported_object);
g_dbus_object_manager_server_set_connection (object_manager, connection);
g_assert_no_error (error);
g_signal_connect (exported_frobber, "handle-hello-world",
G_CALLBACK (on_handle_hello_world), NULL);
g_signal_connect (exported_frobber, "handle-never-return",
G_CALLBACK (on_handle_never_return), NULL);
g_signal_connect (exported_frobber,
"handle-test-primitive-types",
G_CALLBACK (on_handle_test_primitive_types), NULL);
g_signal_connect (exported_frobber,
"handle-test-non-primitive-types",
G_CALLBACK (on_handle_test_non_primitive_types), NULL);
g_signal_connect (exported_frobber,
"handle-test-variant",
G_CALLBACK (on_handle_test_variant), NULL);
g_signal_connect (exported_frobber,
"handle-request-signal-emission",
G_CALLBACK (on_handle_request_signal_emission), NULL);
g_signal_connect (exported_frobber,
"handle-request-property-mods",
G_CALLBACK (on_handle_request_property_mods), NULL);
g_signal_connect (exported_frobber,
"handle-request-multi-property-mods",
G_CALLBACK (on_handle_request_multi_property_mods), NULL);
g_signal_connect (exported_frobber,
"handle-property-cancellation",
G_CALLBACK (on_handle_property_cancellation), NULL);
g_signal_connect (exported_frobber,
"handle-delete-all-objects",
G_CALLBACK (on_handle_delete_all_objects), mock_data);
g_signal_connect (exported_frobber,
"handle-create-object",
G_CALLBACK (on_handle_create_object), mock_data);
g_signal_connect (exported_frobber,
"handle-delete-object",
G_CALLBACK (on_handle_delete_object), mock_data);
g_signal_connect (exported_frobber,
"handle-test-asv",
G_CALLBACK (on_handle_test_asv), NULL);
g_signal_connect (exported_frobber,
"handle-add-alpha",
G_CALLBACK (on_handle_add_alpha), NULL);
g_signal_connect (exported_frobber,
"handle-remove-alpha",
G_CALLBACK (on_handle_remove_alpha), NULL);
g_signal_connect (exported_frobber, "handle-create-clique",
G_CALLBACK (on_create_clique), NULL);
g_signal_connect (exported_frobber, "handle-emit-hidden",
G_CALLBACK (on_emit_hidden), NULL);
g_signal_connect (exported_frobber, "handle-claim-other-name",
G_CALLBACK (on_claim_other_name), mock_data);
g_signal_connect (exported_frobber, "handle-release-other-name",
G_CALLBACK (on_release_other_name), mock_data);
g_signal_connect (exported_frobber, "handle-tell-me-your-name",
G_CALLBACK (on_tell_me_your_name), mock_data);
g_signal_connect (exported_frobber, "handle-make-test-fd",
G_CALLBACK (on_make_test_fd), mock_data);
g_object_unref (exported_frobber);
mock_service_create_introspect_fail (connection);
return G_OBJECT (object_manager);
}
static GThread *mock_thread = NULL;
static GDBusConnection *mock_conn = NULL;
static GCond mock_cond;
static GMutex mock_mutex;
static void
on_name_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
gboolean *owned = user_data;
*owned = TRUE;
}
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
gboolean *owned = user_data;
*owned = FALSE;
}
typedef struct {
GMainContext *context;
gpointer *clear;
} GoneContext;
static void
on_object_gone (gpointer data,
GObject *where_the_object_was)
{
GoneContext *ctx = data;
g_atomic_pointer_set (ctx->clear, NULL);
g_main_context_wakeup (ctx->context);
}
static void
wait_until_object_gone (GMainContext *context,
gpointer object)
{
GoneContext ctx = { context, &object };
g_object_weak_ref (object, on_object_gone, &ctx);
g_object_unref (object);
while (g_atomic_pointer_get (&object))
g_main_context_iteration (context, TRUE);
}
static gpointer
mock_service_thread (gpointer unused)
{
GDBusConnection *conn;
GObject *exported;
GMainContext *main_ctx;
gboolean owned = FALSE;
GError *error = NULL;
gchar *address;
main_ctx = g_main_context_new ();
g_main_context_push_thread_default (main_ctx);
address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
conn = g_dbus_connection_new_for_address_sync (address,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
NULL, NULL, &error);
g_assert_no_error (error);
g_free (address);
exported = mock_service_create_and_export (conn, "/otree");
g_assert (exported != NULL);
g_bus_own_name_on_connection (conn, "com.redhat.Cockpit.DBusTests.Test",
G_BUS_NAME_OWNER_FLAGS_NONE,
on_name_acquired, on_name_lost, &owned, NULL);
g_mutex_lock (&mock_mutex);
while (!owned)
g_main_context_iteration (main_ctx, TRUE);
mock_conn = conn;
g_cond_signal (&mock_cond);
g_mutex_unlock (&mock_mutex);
while (!g_dbus_connection_is_closed (conn))
g_main_context_iteration (main_ctx, TRUE);
g_mutex_lock (&mock_mutex);
mock_conn = NULL;
g_mutex_unlock (&mock_mutex);
g_object_unref (exported);
wait_until_object_gone (main_ctx, conn);
while (g_main_context_iteration (main_ctx, FALSE));
g_main_context_pop_thread_default (main_ctx);
g_main_context_unref (main_ctx);
return NULL;
}
void
mock_service_start (void)
{
g_assert (mock_thread == NULL);
mock_thread = g_thread_new ("mock-service", mock_service_thread, NULL);
g_mutex_lock (&mock_mutex);
while (!mock_conn)
g_cond_wait (&mock_cond, &mock_mutex);
g_mutex_unlock (&mock_mutex);
}
void
mock_service_stop (void)
{
GError *error = NULL;
g_assert (mock_thread != NULL);
g_dbus_connection_close_sync (mock_conn, NULL, &error);
g_assert_no_error (error);
g_thread_join (mock_thread);
mock_thread = NULL;
}