Merge branch 'fake-document-portal-in-process' into 'main'

tests: Move fake-document-portal subprocess inside dbus-appinfo test

See merge request GNOME/glib!4311
This commit is contained in:
Philip Withnall 2024-10-01 15:38:30 +00:00
commit 2b5d3b5831
8 changed files with 247 additions and 64 deletions

View File

@ -23,6 +23,7 @@
#include <gio/gdesktopappinfo.h> #include <gio/gdesktopappinfo.h>
#include "gdbus-sessionbus.h" #include "gdbus-sessionbus.h"
#include "fake-document-portal.h"
static GDesktopAppInfo *appinfo; static GDesktopAppInfo *appinfo;
static int current_state; static int current_state;
@ -348,9 +349,14 @@ test_flatpak_doc_export (void)
GDesktopAppInfo *flatpak_appinfo; GDesktopAppInfo *flatpak_appinfo;
GApplication *app; GApplication *app;
int status; int status;
GFakeDocumentPortalThread *thread = NULL;
g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal."); g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal.");
/* Run a fake-document-portal */
thread = g_fake_document_portal_thread_new (session_bus_get_address ());
g_fake_document_portal_thread_run (thread);
desktop_file = g_test_build_filename (G_TEST_DIST, desktop_file = g_test_build_filename (G_TEST_DIST,
"org.gtk.test.dbusappinfo.flatpak.desktop", "org.gtk.test.dbusappinfo.flatpak.desktop",
NULL); NULL);
@ -369,6 +375,8 @@ test_flatpak_doc_export (void)
g_object_unref (app); g_object_unref (app);
g_object_unref (flatpak_appinfo); g_object_unref (flatpak_appinfo);
g_fake_document_portal_thread_stop (thread);
g_clear_object (&thread);
} }
static void static void
@ -426,9 +434,14 @@ test_flatpak_missing_doc_export (void)
GDesktopAppInfo *flatpak_appinfo; GDesktopAppInfo *flatpak_appinfo;
GApplication *app; GApplication *app;
int status; int status;
GFakeDocumentPortalThread *thread = NULL;
g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal."); g_test_summary ("Test that files launched via Flatpak apps are made available via the document portal.");
/* Run a fake-document-portal */
thread = g_fake_document_portal_thread_new (session_bus_get_address ());
g_fake_document_portal_thread_run (thread);
desktop_file = g_test_build_filename (G_TEST_DIST, desktop_file = g_test_build_filename (G_TEST_DIST,
"org.gtk.test.dbusappinfo.flatpak.desktop", "org.gtk.test.dbusappinfo.flatpak.desktop",
NULL); NULL);
@ -447,12 +460,14 @@ test_flatpak_missing_doc_export (void)
g_object_unref (app); g_object_unref (app);
g_object_unref (flatpak_appinfo); g_object_unref (flatpak_appinfo);
g_free (desktop_file); g_free (desktop_file);
g_fake_document_portal_thread_stop (thread);
g_clear_object (&thread);
} }
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
g_test_init (&argc, &argv, NULL); g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
g_test_add_func ("/appinfo/dbusappinfo", test_dbus_appinfo); g_test_add_func ("/appinfo/dbusappinfo", test_dbus_appinfo);
g_test_add_func ("/appinfo/flatpak-doc-export", test_flatpak_doc_export); g_test_add_func ("/appinfo/flatpak-doc-export", test_flatpak_doc_export);

View File

@ -26,8 +26,56 @@
#include <gio/gio.h> #include <gio/gio.h>
#include <gio/gunixfdlist.h> #include <gio/gunixfdlist.h>
#include "fake-document-portal.h"
#include "fake-document-portal-generated.h" #include "fake-document-portal-generated.h"
struct _GFakeDocumentPortalThread
{
GObject parent_instance;
char *address; /* (not nullable) */
GCancellable *cancellable; /* (not nullable) (owned) */
GThread *thread; /* (not nullable) (owned) */
GCond cond; /* (mutex mutex) */
GMutex mutex;
gboolean ready; /* (mutex mutex) */
};
G_DEFINE_FINAL_TYPE (GFakeDocumentPortalThread, g_fake_document_portal_thread, G_TYPE_OBJECT)
static void g_fake_document_portal_thread_finalize (GObject *object);
static void
g_fake_document_portal_thread_class_init (GFakeDocumentPortalThreadClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = g_fake_document_portal_thread_finalize;
}
static void
g_fake_document_portal_thread_init (GFakeDocumentPortalThread *self)
{
self->cancellable = g_cancellable_new ();
g_cond_init (&self->cond);
g_mutex_init (&self->mutex);
}
static void
g_fake_document_portal_thread_finalize (GObject *object)
{
GFakeDocumentPortalThread *self = G_FAKE_DOCUMENT_PORTAL_THREAD (object);
g_assert (self->thread == NULL); /* should already have been joined */
g_mutex_clear (&self->mutex);
g_cond_clear (&self->cond);
g_clear_object (&self->cancellable);
g_clear_pointer (&self->address, g_free);
G_OBJECT_CLASS (g_fake_document_portal_thread_parent_class)->finalize (object);
}
static gboolean static gboolean
on_handle_get_mount_point (FakeDocuments *object, on_handle_get_mount_point (FakeDocuments *object,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
@ -76,13 +124,66 @@ on_handle_add_full (FakeDocuments *object,
} }
static void static void
on_bus_acquired (GDBusConnection *connection, on_name_acquired (GDBusConnection *connection,
const gchar *name, const gchar *name,
gpointer user_data) gpointer user_data)
{ {
FakeDocuments *interface; GFakeDocumentPortalThread *self = G_FAKE_DOCUMENT_PORTAL_THREAD (user_data);
GError *error = NULL;
g_test_message ("Acquired the name %s", name);
g_mutex_lock (&self->mutex);
self->ready = TRUE;
g_cond_signal (&self->cond);
g_mutex_unlock (&self->mutex);
}
static void
on_name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_test_message ("Lost the name %s", name);
}
static gboolean
cancelled_cb (GCancellable *cancellable,
gpointer user_data)
{
g_test_message ("fake-document-portal cancelled");
return G_SOURCE_CONTINUE;
}
static gpointer
fake_document_portal_thread_cb (gpointer user_data)
{
GFakeDocumentPortalThread *self = G_FAKE_DOCUMENT_PORTAL_THREAD (user_data);
GMainContext *context = NULL;
GDBusConnection *connection = NULL;
GSource *source = NULL;
guint id;
FakeDocuments *interface = NULL;
GError *local_error = NULL;
context = g_main_context_new ();
g_main_context_push_thread_default (context);
connection = g_dbus_connection_new_for_address_sync (self->address,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
NULL,
self->cancellable,
&local_error);
g_assert_no_error (local_error);
/* Listen for cancellation. The source will wake up the context iteration
* which can then re-check its exit condition below. */
source = g_cancellable_source_new (self->cancellable);
g_source_set_callback (source, G_SOURCE_FUNC (cancelled_cb), NULL, NULL);
g_source_attach (source, context);
g_source_unref (source);
/* Set up the interface */
g_test_message ("Acquired a message bus connection"); g_test_message ("Acquired a message bus connection");
interface = fake_documents_skeleton_new (); interface = fake_documents_skeleton_new ();
@ -98,51 +199,81 @@ on_bus_acquired (GDBusConnection *connection,
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface), g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (interface),
connection, connection,
"/org/freedesktop/portal/documents", "/org/freedesktop/portal/documents",
&error); &local_error);
g_assert_no_error (error); g_assert_no_error (local_error);
}
static void /* Own the portal name */
on_name_acquired (GDBusConnection *connection, id = g_bus_own_name_on_connection (connection,
const gchar *name, "org.freedesktop.portal.Documents",
gpointer user_data) G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
{ G_BUS_NAME_OWNER_FLAGS_REPLACE,
g_test_message ("Acquired the name %s", name); on_name_acquired,
} on_name_lost,
self,
NULL);
static void while (!g_cancellable_is_cancelled (self->cancellable))
on_name_lost (GDBusConnection *connection, g_main_context_iteration (context, TRUE);
const gchar *name,
gpointer user_data)
{
g_test_message ("Lost the name %s", name);
}
gint
main (gint argc, gchar *argv[])
{
GMainLoop *loop;
guint id;
g_log_writer_default_set_use_stderr (TRUE);
loop = g_main_loop_new (NULL, FALSE);
id = g_bus_own_name (G_BUS_TYPE_SESSION,
"org.freedesktop.portal.Documents",
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
G_BUS_NAME_OWNER_FLAGS_REPLACE,
on_bus_acquired,
on_name_acquired,
on_name_lost,
loop,
NULL);
g_main_loop_run (loop);
g_bus_unown_name (id); g_bus_unown_name (id);
g_main_loop_unref (loop); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (interface));
g_clear_object (&interface);
g_clear_object (&connection);
g_main_context_pop_thread_default (context);
g_clear_pointer (&context, g_main_context_unref);
return 0; return NULL;
}
/*
* Create a new #GFakeDocumentPortalThread. The thread isnt started until
* g_fake_document_portal_thread_run() is called on it.
*
* Returns: (transfer full): the new fake document portal wrapper
*/
GFakeDocumentPortalThread *
g_fake_document_portal_thread_new (const char *address)
{
GFakeDocumentPortalThread *self = g_object_new (G_TYPE_FAKE_DOCUMENT_PORTAL_THREAD, NULL);
self->address = g_strdup (address);
return g_steal_pointer (&self);
}
/*
* Start a worker thread which will run a fake
* `org.freedesktop.portal.Documents` portal on the bus at @address. This is
* intended to be used with #GTestDBus to mock up a portal from within a unit
* test process, rather than relying on D-Bus activation of a mock portal
* subprocess.
*
* It blocks until the thread has owned its D-Bus name and is ready to handle
* requests.
*/
void
g_fake_document_portal_thread_run (GFakeDocumentPortalThread *self)
{
g_return_if_fail (G_IS_FAKE_DOCUMENT_PORTAL_THREAD (self));
g_return_if_fail (self->thread == NULL);
self->thread = g_thread_new ("fake-document-portal", fake_document_portal_thread_cb, self);
/* Block until the thread is ready. */
g_mutex_lock (&self->mutex);
while (!self->ready)
g_cond_wait (&self->cond, &self->mutex);
g_mutex_unlock (&self->mutex);
}
/* Stop and join a worker thread started with fake_document_portal_thread_run().
* Blocks until the thread has stopped and joined.
*
* Once this has been called, its safe to drop the final reference on @self. */
void
g_fake_document_portal_thread_stop (GFakeDocumentPortalThread *self)
{
g_return_if_fail (G_IS_FAKE_DOCUMENT_PORTAL_THREAD (self));
g_return_if_fail (self->thread != NULL);
g_cancellable_cancel (self->cancellable);
g_thread_join (g_steal_pointer (&self->thread));
} }

View File

@ -0,0 +1,37 @@
/*
* Copyright 2024 GNOME Foundation
*
* 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/>.
*/
#ifndef __FAKE_DOCUMENT_PORTAL_H__
#define __FAKE_DOCUMENT_PORTAL_H__
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
#define G_TYPE_FAKE_DOCUMENT_PORTAL_THREAD (g_fake_document_portal_thread_get_type ())
G_DECLARE_FINAL_TYPE (GFakeDocumentPortalThread, g_fake_document_portal_thread, G, FAKE_DOCUMENT_PORTAL_THREAD, GObject)
GFakeDocumentPortalThread *g_fake_document_portal_thread_new (const char *address);
void g_fake_document_portal_thread_run (GFakeDocumentPortalThread *self);
void g_fake_document_portal_thread_stop (GFakeDocumentPortalThread *self);
G_END_DECLS
#endif /* __FAKE_DOCUMENT_PORTAL_H__ */

View File

@ -71,3 +71,10 @@ session_bus_run (void)
return ret; return ret;
} }
const char *
session_bus_get_address (void)
{
g_assert (singleton != NULL);
return g_test_dbus_get_bus_address (singleton);
}

View File

@ -31,6 +31,7 @@ void session_bus_up (void);
void session_bus_stop (void); void session_bus_stop (void);
void session_bus_down (void); void session_bus_down (void);
gint session_bus_run (void); gint session_bus_run (void);
const char *session_bus_get_address (void);
G_END_DECLS G_END_DECLS

View File

@ -544,15 +544,6 @@ if host_machine.system() != 'windows'
}, },
} }
if not glib_have_cocoa
gio_tests += {
'dbus-appinfo' : {
'extra_sources' : extra_sources,
'extra_programs' : ['fake-document-portal'],
},
}
endif
fake_document_portal_generated = custom_target('fake-document-portal-generated', fake_document_portal_generated = custom_target('fake-document-portal-generated',
input : ['../org.freedesktop.portal.Documents.xml'], input : ['../org.freedesktop.portal.Documents.xml'],
output : ['fake-document-portal-generated.h', output : ['fake-document-portal-generated.h',
@ -566,10 +557,15 @@ if host_machine.system() != 'windows'
'--c-namespace', 'Fake', '--c-namespace', 'Fake',
'@INPUT@']) '@INPUT@'])
if not glib_have_cocoa
gio_tests += {
'dbus-appinfo' : {
'extra_sources' : [extra_sources, 'fake-document-portal.c', fake_document_portal_generated],
},
}
endif
test_extra_programs += { test_extra_programs += {
'fake-document-portal' : {
'extra_sources': fake_document_portal_generated,
},
'fake-service-name' : {} 'fake-service-name' : {}
} }
endif # have_dbus_daemon endif # have_dbus_daemon

View File

@ -1,5 +1,4 @@
dbus_service_files = [ dbus_service_files = [
'org.freedesktop.portal.Documents.service',
'org.gtk.GDBus.FakeService.service' 'org.gtk.GDBus.FakeService.service'
] ]

View File

@ -1,3 +0,0 @@
[D-BUS Service]
Name=org.freedesktop.portal.Documents
Exec=@installed_tests_dir@/fake-document-portal