[tracker-miners/sam/test-removable-devices: 1/2] functional-tests: Add mock-volume-monitor GIO module




commit 9fc661d8635de915a2d1f532ad2477ae1ff9bf39
Author: Sam Thursfield <sam afuera me uk>
Date:   Tue Aug 17 17:28:51 2021 +0200

    functional-tests: Add mock-volume-monitor GIO module
    
    This is our best option for testing Tracker's removable device handling
    code, see: https://discourse.gnome.org/t/testing-removable-usb-devices/7297
    
    You can test-drive the module by running this command in the build tree:
    
        env GIO_USE_VOLUME_MONITOR=mockvolumemonitor 
GIO_MODULE_DIR=$(pwd)/tests/functional-tests/mockvolumemonitor gio mount --monitor
    
    The module is operated over D-Bus, to create a fake mount you can do
    this:
    
         gdbus call --session --dest org.freedesktop.Tracker3.MockVolumeMonitor \
                    --object-path /org/freedesktop/Tracker3/MockVolumeMonitor \
                    --method org.freedesktop.Tracker3.MockVolumeMonitor.AddMount file:///tmp/mymount

 tests/functional-tests/meson.build                 |   2 +
 .../functional-tests/mockvolumemonitor/meson.build |   6 +
 .../mockvolumemonitor/mock-drive.c                 | 268 +++++++++++++++++
 .../mockvolumemonitor/mock-drive.h                 |  48 +++
 .../mockvolumemonitor/mock-mount.c                 | 267 +++++++++++++++++
 .../mockvolumemonitor/mock-mount.h                 |  51 ++++
 .../mockvolumemonitor/mock-volume-monitor.c        | 327 +++++++++++++++++++++
 .../mockvolumemonitor/mock-volume-monitor.h        |  35 +++
 .../mockvolumemonitor/mock-volume.c                | 275 +++++++++++++++++
 .../mockvolumemonitor/mock-volume.h                |  55 ++++
 10 files changed, 1334 insertions(+)
---
diff --git a/tests/functional-tests/meson.build b/tests/functional-tests/meson.build
index 9ed3be7b5..4838dcdaa 100644
--- a/tests/functional-tests/meson.build
+++ b/tests/functional-tests/meson.build
@@ -1,5 +1,7 @@
 python = find_program('python3')
 
+subdir('mockvolumemonitor')
+
 # Configure functional tests to run completely from source tree.
 testconf = configuration_data()
 
diff --git a/tests/functional-tests/mockvolumemonitor/meson.build 
b/tests/functional-tests/mockvolumemonitor/meson.build
new file mode 100644
index 000000000..fa13238b5
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/meson.build
@@ -0,0 +1,6 @@
+shared_module('mockvolumemonitor',
+  'mock-drive.c', 'mock-drive.h',
+  'mock-mount.c', 'mock-mount.h',
+  'mock-volume.c', 'mock-volume.h',
+  'mock-volume-monitor.c', 'mock-volume-monitor.h',
+  dependencies: [gio, gio_unix])
diff --git a/tests/functional-tests/mockvolumemonitor/mock-drive.c 
b/tests/functional-tests/mockvolumemonitor/mock-drive.c
new file mode 100644
index 000000000..f7ab8c08c
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-drive.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "mock-drive.h"
+
+struct _MockDrive
+{
+       GObject parent;
+
+       MockVolumeMonitor  *monitor; /* owned by volume monitor */
+       GList              *volumes; /* entries in list are owned by volume monitor */
+
+       const gchar *name;
+};
+
+static void mock_drive_drive_iface_init ();
+
+G_DEFINE_TYPE_EXTENDED (MockDrive, mock_drive, G_TYPE_OBJECT, 0,
+                             G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE, mock_drive_drive_iface_init))
+
+static void
+mock_drive_finalize (GObject *object)
+{
+       MockDrive *drive = MOCK_DRIVE (object);
+       GList *l;
+
+       for (l = drive->volumes; l != NULL; l = l->next) {
+               MockVolume *volume = l->data;
+               mock_volume_unset_drive (volume, drive);
+       }
+
+       G_OBJECT_CLASS (mock_drive_parent_class)->finalize (object);
+}
+
+static void
+mock_drive_class_init (MockDriveClass *klass)
+{
+       GObjectClass *gobject_class;
+
+       gobject_class = G_OBJECT_CLASS (klass);
+       gobject_class->finalize = mock_drive_finalize;
+}
+
+static void
+mock_drive_init (MockDrive *drive)
+{
+}
+
+static void
+emit_changed (MockDrive *drive)
+{
+       g_signal_emit_by_name (drive, "changed");
+       g_signal_emit_by_name (drive->monitor, "drive-changed", drive);
+}
+
+
+MockDrive *
+mock_drive_new (MockVolumeMonitor *monitor,
+                const gchar       *name)
+{
+       MockDrive *drive;
+
+       drive = g_object_new (MOCK_TYPE_DRIVE, NULL);
+       drive->monitor = monitor;
+       drive->name = g_strdup (name);
+
+       return drive;
+}
+
+void
+mock_drive_disconnected (MockDrive *drive)
+{
+       GList *l, *volumes;
+
+       volumes = drive->volumes;
+       drive->volumes = NULL;
+       for (l = volumes; l != NULL; l = l->next) {
+               MockVolume *volume = l->data;
+               mock_volume_unset_drive (volume, drive);
+       }
+       g_list_free (volumes);
+}
+
+void
+mock_drive_set_volume (MockDrive  *drive,
+                       MockVolume *volume)
+{
+       if (g_list_find (drive->volumes, volume) == NULL) {
+               drive->volumes = g_list_prepend (drive->volumes, volume);
+               emit_changed (drive);
+       }
+}
+
+void
+mock_drive_unset_volume (MockDrive  *drive,
+                                      MockVolume *volume)
+{
+       GList *l;
+       l = g_list_find (drive->volumes, volume);
+       if (l != NULL)
+         {
+           drive->volumes = g_list_delete_link (drive->volumes, l);
+           emit_changed (drive);
+         }
+}
+
+static GIcon *
+mock_drive_get_icon (GDrive *_drive)
+{
+       return NULL;
+}
+
+static GIcon *
+mock_drive_get_symbolic_icon (GDrive *_drive)
+{
+       return NULL;
+}
+
+static char *
+mock_drive_get_name (GDrive *_drive)
+{
+       MockDrive *drive = MOCK_DRIVE (_drive);
+       return g_strdup (drive->name);
+}
+
+static GList *
+mock_drive_get_volumes (GDrive *_drive)
+{
+       MockDrive *drive = MOCK_DRIVE (_drive);
+       GList *l;
+       l = g_list_copy (drive->volumes);
+       g_list_foreach (l, (GFunc) g_object_ref, NULL);
+       return l;
+}
+
+static gboolean
+mock_drive_has_volumes (GDrive *_drive)
+{
+       MockDrive *drive = MOCK_DRIVE (_drive);
+       gboolean res;
+       res = drive->volumes != NULL;
+       return res;
+}
+
+static gboolean
+mock_drive_is_removable (GDrive *_drive)
+{
+       return TRUE;
+}
+
+static gboolean
+mock_drive_is_media_removable (GDrive *_drive)
+{
+       return TRUE;
+}
+
+static gboolean
+mock_drive_has_media (GDrive *_drive)
+{
+       return TRUE;
+}
+
+static gboolean
+mock_drive_is_media_check_automatic (GDrive *_drive)
+{
+       return TRUE;
+}
+
+static gboolean
+mock_drive_can_eject (GDrive *_drive)
+{
+       return FALSE;
+}
+
+static gboolean
+mock_drive_can_poll_for_media (GDrive *_drive)
+{
+       return FALSE;
+}
+
+static gboolean
+mock_drive_can_start (GDrive *_drive)
+{
+       return FALSE;
+}
+
+static gboolean
+mock_drive_can_start_degraded (GDrive *_drive)
+{
+       return FALSE;
+}
+
+static gboolean
+mock_drive_can_stop (GDrive *_drive)
+{
+       return FALSE;
+}
+
+static GDriveStartStopType
+mock_drive_get_start_stop_type (GDrive *_drive)
+{
+       return G_DRIVE_START_STOP_TYPE_SHUTDOWN;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static char *
+mock_drive_get_identifier (GDrive      *_drive,
+                           const gchar *kind)
+{
+       return NULL;
+}
+
+static gchar **
+mock_drive_enumerate_identifiers (GDrive *_drive)
+{
+       return NULL;
+}
+
+static const gchar *
+mock_drive_get_sort_key (GDrive *_drive)
+{
+       return NULL;
+}
+
+static void
+mock_drive_drive_iface_init (GDriveIface *iface)
+{
+       iface->get_name = mock_drive_get_name;
+       iface->get_icon = mock_drive_get_icon;
+       iface->get_symbolic_icon = mock_drive_get_symbolic_icon;
+       iface->has_volumes = mock_drive_has_volumes;
+       iface->get_volumes = mock_drive_get_volumes;
+       iface->is_removable = mock_drive_is_removable;
+       iface->is_media_removable = mock_drive_is_media_removable;
+       iface->has_media = mock_drive_has_media;
+       iface->is_media_check_automatic = mock_drive_is_media_check_automatic;
+       iface->can_eject = mock_drive_can_eject;
+       iface->can_poll_for_media = mock_drive_can_poll_for_media;
+       iface->get_identifier = mock_drive_get_identifier;
+       iface->enumerate_identifiers = mock_drive_enumerate_identifiers;
+       iface->get_start_stop_type = mock_drive_get_start_stop_type;
+       iface->can_start = mock_drive_can_start;
+       iface->can_start_degraded = mock_drive_can_start_degraded;
+       iface->can_stop = mock_drive_can_stop;
+       iface->get_sort_key = mock_drive_get_sort_key;
+}
diff --git a/tests/functional-tests/mockvolumemonitor/mock-drive.h 
b/tests/functional-tests/mockvolumemonitor/mock-drive.h
new file mode 100644
index 000000000..ac6d186fd
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-drive.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+
+#ifndef __MOCK_DRIVE_H__
+#define __MOCK_DRIVE_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gio/gunixmounts.h>
+
+#include "mock-volume-monitor.h"
+#include "mock-volume.h"
+
+G_BEGIN_DECLS
+
+#define MOCK_TYPE_DRIVE  (mock_drive_get_type ())
+G_DECLARE_FINAL_TYPE (MockDrive, mock_drive, MOCK, DRIVE, GObject)
+
+MockDrive *mock_drive_new             (MockVolumeMonitor *monitor,
+                                       const gchar       *name);
+void       mock_drive_disconnected    (MockDrive         *drive);
+void       mock_drive_set_volume      (MockDrive         *drive,
+                                       MockVolume        *volume);
+void       mock_drive_unset_volume    (MockDrive         *drive,
+                                       MockVolume        *volume);
+
+G_END_DECLS
+
+#endif /* __MOCK_DRIVE_H__ */
diff --git a/tests/functional-tests/mockvolumemonitor/mock-mount.c 
b/tests/functional-tests/mockvolumemonitor/mock-mount.c
new file mode 100644
index 000000000..fa7955f4a
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-mount.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "mock-mount.h"
+#include "mock-volume.h"
+
+struct _MockMount
+{
+       GObject parent;
+
+       MockVolumeMonitor *monitor; /* owned by volume monitor */
+
+       /* may be NULL */
+       MockVolume        *volume;  /* owned by volume monitor */
+
+       gchar *name;
+       GFile *root;
+};
+
+static void mock_mount_mount_iface_init (GMountIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MockMount, mock_mount, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT,
+                                               mock_mount_mount_iface_init))
+
+static void on_volume_changed (GVolume *volume, gpointer user_data);
+
+static void
+mock_mount_finalize (GObject *object)
+{
+       MockMount *mount = MOCK_MOUNT (object);
+
+       if (mount->volume != NULL) {
+               g_signal_handlers_disconnect_by_func (mount->volume, on_volume_changed, mount);
+               mock_volume_unset_mount (mount->volume, mount);
+       }
+
+       g_free (mount->name);
+       g_clear_object (&mount->root);
+
+       G_OBJECT_CLASS (mock_mount_parent_class)->finalize (object);
+}
+
+static void
+mock_mount_class_init (MockMountClass *klass)
+{
+       GObjectClass *gobject_class;
+
+       gobject_class = G_OBJECT_CLASS (klass);
+       gobject_class->finalize = mock_mount_finalize;
+}
+
+static void
+mock_mount_init (MockMount *mount)
+{
+}
+
+static void
+emit_changed (MockMount *mount)
+{
+       g_signal_emit_by_name (mount, "changed");
+       g_signal_emit_by_name (mount->monitor, "mount-changed", mount);
+}
+
+static void
+on_volume_changed (GVolume  *volume,
+                   gpointer  user_data)
+{
+       MockMount *mount = MOCK_MOUNT (user_data);
+       emit_changed (mount);
+}
+
+MockMount *
+mock_mount_new (MockVolumeMonitor *monitor,
+                MockVolume        *volume,
+                const gchar       *name,
+                GFile             *root)
+{
+       MockMount *mount = NULL;
+
+       mount = g_object_new (MOCK_TYPE_MOUNT, NULL);
+       mount->monitor = monitor;
+       mount->name = g_strdup (name);
+       mount->root = g_object_ref (root);
+
+       /* need to set the volume only when the mount is fully constructed */
+       mount->volume = volume;
+       if (mount->volume != NULL) {
+               mock_volume_set_mount (volume, mount);
+               /* this is for piggy backing on the name and icon of the associated volume */
+               g_signal_connect (mount->volume, "changed", G_CALLBACK (on_volume_changed), mount);
+       }
+
+       return mount;
+}
+
+void
+mock_mount_unmounted (MockMount *mount)
+{
+       if (mount->volume != NULL) {
+               mock_volume_unset_mount (mount->volume, mount);
+               g_signal_handlers_disconnect_by_func (mount->volume, on_volume_changed, mount);
+               mount->volume = NULL;
+               emit_changed (mount);
+       }
+}
+
+void
+mock_mount_unset_volume (MockMount   *mount,
+                         MockVolume  *volume)
+{
+       if (mount->volume == volume) {
+               g_signal_handlers_disconnect_by_func (mount->volume, on_volume_changed, mount);
+               mount->volume = NULL;
+               emit_changed (mount);
+       }
+}
+
+void
+mock_mount_set_volume (MockMount   *mount,
+                       MockVolume  *volume)
+{
+       if (mount->volume != volume) {
+               if (mount->volume != NULL)
+                       mock_mount_unset_volume (mount, mount->volume);
+               mount->volume = volume;
+               if (mount->volume != NULL) {
+                       mock_volume_set_mount (volume, mount);
+                       /* this is for piggy backing on the name and icon of the associated volume */
+                       g_signal_connect (mount->volume, "changed", G_CALLBACK (on_volume_changed), mount);
+               }
+               emit_changed (mount);
+       }
+}
+
+static GFile *
+mock_mount_get_root (GMount *_mount)
+{
+       MockMount *mount = MOCK_MOUNT (_mount);
+       return g_object_ref (mount->root);
+}
+
+static GIcon *
+mock_mount_get_icon (GMount *_mount)
+{
+       return NULL;
+}
+
+static GIcon *
+mock_mount_get_symbolic_icon (GMount *_mount)
+{
+       return NULL;
+}
+
+static gchar *
+mock_mount_get_uuid (GMount *_mount)
+{
+       return NULL;
+}
+
+static gchar *
+mock_mount_get_name (GMount *_mount)
+{
+       MockMount *mount = MOCK_MOUNT (_mount);
+       return g_strdup (mount->name);
+}
+
+gboolean
+mock_mount_has_uuid (MockMount *_mount,
+                     const gchar      *uuid)
+{
+       return FALSE;
+}
+
+const gchar *
+mock_mount_get_mount_path (MockMount *mount)
+{
+       return NULL;
+}
+
+GUnixMountEntry *
+mock_mount_get_mount_entry (MockMount *_mount)
+{
+       return NULL;
+}
+
+static GDrive *
+mock_mount_get_drive (GMount *_mount)
+{
+       MockMount *mount = MOCK_MOUNT (_mount);
+       GDrive *drive = NULL;
+
+       if (mount->volume != NULL)
+               drive = g_volume_get_drive (G_VOLUME (mount->volume));
+       return drive;
+}
+
+static GVolume *
+mock_mount_get_volume_ (GMount *_mount)
+{
+       MockMount *mount = MOCK_MOUNT (_mount);
+       GVolume *volume = NULL;
+
+       if (mount->volume != NULL)
+               volume = G_VOLUME (g_object_ref (mount->volume));
+       return volume;
+}
+
+static gboolean
+mock_mount_can_unmount (GMount *_mount)
+{
+       return TRUE;
+}
+
+static gboolean
+mock_mount_can_eject (GMount *_mount)
+{
+       return FALSE;
+}
+
+static const gchar *
+mock_mount_get_sort_key (GMount *_mount)
+{
+       return NULL;
+}
+
+static void
+mock_mount_mount_iface_init (GMountIface *iface)
+{
+       iface->get_root = mock_mount_get_root;
+       iface->get_name = mock_mount_get_name;
+       iface->get_icon = mock_mount_get_icon;
+       iface->get_symbolic_icon = mock_mount_get_symbolic_icon;
+       iface->get_uuid = mock_mount_get_uuid;
+       iface->get_drive = mock_mount_get_drive;
+       iface->get_volume = mock_mount_get_volume_;
+       iface->can_unmount = mock_mount_can_unmount;
+       iface->can_eject = mock_mount_can_eject;
+       iface->get_sort_key = mock_mount_get_sort_key;
+}
+
+MockVolume *
+mock_mount_get_volume (MockMount *mount)
+{
+       return mount->volume;
+}
diff --git a/tests/functional-tests/mockvolumemonitor/mock-mount.h 
b/tests/functional-tests/mockvolumemonitor/mock-mount.h
new file mode 100644
index 000000000..88bfeff75
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-mount.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+#ifndef __MOCK_MOUNT_H__
+#define __MOCK_MOUNT_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gio/gunixmounts.h>
+
+#include "mock-volume-monitor.h"
+
+G_BEGIN_DECLS
+
+#define MOCK_TYPE_MOUNT  (mock_mount_get_type ())
+G_DECLARE_FINAL_TYPE (MockMount, mock_mount, MOCK, MOUNT, GObject)
+
+
+MockMount        *mock_mount_new           (MockVolumeMonitor *monitor,
+                                            MockVolume        *volume,
+                                            const gchar       *name,
+                                            GFile             *root);
+void              mock_mount_unmounted     (MockMount         *mount);
+
+void              mock_mount_set_volume    (MockMount         *mount,
+                                            MockVolume        *volume);
+void              mock_mount_unset_volume  (MockMount         *mount,
+                                            MockVolume        *volume);
+MockVolume       *mock_mount_get_volume    (MockMount         *mount);
+
+G_END_DECLS
+
+#endif /* __MOCK_MOUNT_H__ */
diff --git a/tests/functional-tests/mockvolumemonitor/mock-volume-monitor.c 
b/tests/functional-tests/mockvolumemonitor/mock-volume-monitor.c
new file mode 100644
index 000000000..91980cd70
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-volume-monitor.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+#include "mock-volume-monitor.h"
+#include "mock-drive.h"
+#include "mock-volume.h"
+#include "mock-mount.h"
+
+struct _MockVolumeMonitor {
+       GNativeVolumeMonitor parent;
+
+       GDBusNodeInfo *node_info;
+       guint bus_token;
+       guint object_token;
+
+       GList *drives;
+       GList *volumes;
+       GList *mounts;
+
+       gint counter;
+};
+
+G_DEFINE_TYPE (MockVolumeMonitor, mock_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR);
+
+
+static void
+add_mock_mount (MockVolumeMonitor *self, const gchar *uri)
+{
+       int id = self->counter ++;
+       g_autofree gchar *drive_name;
+       g_autofree gchar *volume_name;
+       g_autofree gchar *mount_name;
+       MockDrive *drive;
+       MockVolume *volume;
+       MockMount *mount;
+       g_autoptr(GFile) root = NULL;
+
+       g_message ("Adding mock mount at '%s'", uri);
+
+       drive_name = g_strdup_printf ("MockDrive%i", id);
+       volume_name = g_strdup_printf ("MockVolume%i", id);
+       mount_name = g_strdup_printf ("MockMount%i", id);
+
+       root = g_file_new_for_uri (uri);
+
+       drive = mock_drive_new (self, drive_name);
+       volume = mock_volume_new (self, drive, volume_name);
+       mount = mock_mount_new (self, volume, mount_name, root);
+
+       self->drives = g_list_prepend (self->drives, drive);
+       self->volumes = g_list_prepend (self->volumes, volume);
+       self->mounts = g_list_prepend (self->mounts, mount);
+
+       g_signal_emit_by_name (self, "drive-connected", drive);
+       g_signal_emit_by_name (self, "volume-added", volume);
+       g_signal_emit_by_name (self, "mount-added", mount);
+}
+
+static gint
+match_mount_by_root (gconstpointer a, gconstpointer b)
+{
+       GMount *mount = G_MOUNT (a);
+       GFile *root = G_FILE (b);
+
+       return !g_file_equal (g_mount_get_root (mount), root);
+}
+
+static void
+remove_mock_mount (MockVolumeMonitor *self, const gchar *uri)
+{
+       GList *node;
+       g_autoptr(GDrive) drive = NULL;
+       g_autoptr(GVolume) volume = NULL;
+       g_autoptr(MockMount) mount = NULL;
+       g_autoptr(GFile) root = NULL;
+
+       g_message ("Removing mock mount at '%s'", uri);
+
+       root = g_file_new_for_uri (uri);
+       node = g_list_find_custom (self->mounts, root, match_mount_by_root);
+
+       if (!node) {
+               g_warning ("No mount found with root %s", uri);
+               return;
+       }
+
+       mount = MOCK_MOUNT (node->data);
+       volume = g_mount_get_volume (G_MOUNT (mount));
+       drive = g_volume_get_drive (volume);
+
+       mock_mount_unmounted (mount);
+       mock_volume_removed (MOCK_VOLUME (volume));
+       mock_drive_disconnected (MOCK_DRIVE (drive));
+
+       g_signal_emit_by_name (self, "mount-removed", mount);
+       g_signal_emit_by_name (self, "volume-removed", volume);
+       g_signal_emit_by_name (self, "drive-disconnected", drive);
+
+       self->drives = g_list_remove (self->drives, drive);
+       self->volumes = g_list_remove (self->volumes, volume);
+       self->mounts = g_list_remove (self->mounts, mount);
+}
+
+
+/* DBus interface
+ * --------------
+ *
+ * Used by tests to control the MockVolumeMonitor object.
+ */
+
+#define BUS_NAME "org.freedesktop.Tracker3.MockVolumeMonitor"
+#define BUS_PATH "/org/freedesktop/Tracker3/MockVolumeMonitor"
+
+static const gchar dbus_xml[] =
+       "<node>"
+       "  <interface name='org.freedesktop.Tracker3.MockVolumeMonitor'>"
+       "    <method name='AddMount'>"
+       "      <arg type='s' name='path' direction='in' />"
+       "    </method>"
+       "    <method name='RemoveMount'>"
+       "      <arg type='s' name='path' direction='in' />"
+       "    </method>"
+       "  </interface>"
+       "</node>";
+
+
+static void
+on_dbus_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)
+{
+       MockVolumeMonitor *self = MOCK_VOLUME_MONITOR (user_data);
+
+       if (g_strcmp0 (method_name, "AddMount") == 0) {
+               g_autofree gchar *uri = NULL;
+
+               g_variant_get (parameters, "(s)", &uri);
+
+               add_mock_mount (self, uri);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else if (g_strcmp0 (method_name, "RemoveMount") == 0) {
+               g_autofree gchar *uri = NULL;
+
+               g_variant_get (parameters, "(s)", &uri);
+
+               remove_mock_mount (self, uri);
+
+               g_dbus_method_invocation_return_value (invocation, NULL);
+       } else {
+               g_dbus_method_invocation_return_error (invocation,
+                                                      G_DBUS_ERROR,
+                                                      G_DBUS_ERROR_UNKNOWN_METHOD,
+                                                      "Unknown method on DBus interface: '%s'", method_name);
+       }
+
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data)
+{
+       MockVolumeMonitor *self = MOCK_VOLUME_MONITOR (user_data);
+       GError *error = NULL;
+
+       g_message ("Publishing DBus object at " BUS_PATH);
+
+       self->node_info= g_dbus_node_info_new_for_xml (dbus_xml, &error);
+       g_assert_no_error (error);
+
+       self->object_token =
+               g_dbus_connection_register_object (connection,
+                                                  BUS_PATH,
+                                                  self->node_info->interfaces[0],
+                                                  &(GDBusInterfaceVTable) { on_dbus_method_call, NULL, NULL 
},
+                                                  self,
+                                                  NULL,
+                                                  &error);
+}
+
+static void
+on_bus_name_acquired (GDBusConnection *connection,
+                      const gchar *name,
+                      gpointer user_data)
+{
+       g_message ("Acquired name: %s", name);
+}
+
+static void
+on_bus_name_lost (GDBusConnection *connection,
+                  const gchar *name,
+                  gpointer user_data)
+{
+       MockVolumeMonitor *self = MOCK_VOLUME_MONITOR (user_data);
+       g_message ("Lost name: %s", name);
+
+       g_dbus_connection_unregister_object (connection, self->object_token);
+       self->object_token = 0;
+}
+
+/* GVolumeMonitor implementation
+ * -----------------------------
+ *
+ * We inject fake mounts into current process by replacing the usual
+ * GNativeVolumeMonitor implementation with this.
+ */
+
+static gboolean
+is_supported (void)
+{
+       g_debug (__func__);
+       return TRUE;
+}
+
+static GList *
+get_connected_drives (GVolumeMonitor *volume_monitor)
+{
+       MockVolumeMonitor *self = MOCK_VOLUME_MONITOR (volume_monitor);
+       return self->drives;
+}
+
+static GList *
+get_volumes (GVolumeMonitor *volume_monitor)
+{
+       MockVolumeMonitor *self = MOCK_VOLUME_MONITOR (volume_monitor);
+       return self->volumes;
+}
+
+static GList *
+get_mounts (GVolumeMonitor *volume_monitor)
+{
+       MockVolumeMonitor *self = MOCK_VOLUME_MONITOR (volume_monitor);
+       return self->mounts;
+}
+
+static GVolume *
+get_volume_for_uuid (GVolumeMonitor *volume_monitor,
+                     const char     *uuid)
+{
+       return NULL;
+
+}
+
+static GMount *
+get_mount_for_uuid (GVolumeMonitor *volume_monitor,
+                    const char     *uuid)
+{
+       return NULL;
+}
+
+static GVolume *
+adopt_orphan_mount (GMount         *mount,
+                    GVolumeMonitor *volume_monitor)
+{
+       return NULL;
+}
+
+void
+drive_eject_button (GVolumeMonitor *volume_monitor,
+                    GDrive         *drive)
+{
+}
+
+void
+drive_stop_button (GVolumeMonitor *volume_monitor,
+                   GDrive         *drive)
+{
+}
+
+
+static void mock_volume_monitor_init (MockVolumeMonitor *self) {
+       self->bus_token = g_bus_own_name (G_BUS_TYPE_SESSION, BUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE,
+                                         on_bus_acquired,
+                                         on_bus_name_acquired,
+                                         on_bus_name_lost,
+                                         g_object_ref (self),
+                                         g_object_unref);
+}
+
+static void mock_volume_monitor_class_init (MockVolumeMonitorClass *klass) {
+       GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
+
+       monitor_class->is_supported = is_supported;
+       monitor_class->get_connected_drives = get_connected_drives;
+       monitor_class->get_volumes = get_volumes;
+       monitor_class->get_mounts = get_mounts;
+       monitor_class->get_volume_for_uuid = get_volume_for_uuid;
+       monitor_class->get_mount_for_uuid = get_mount_for_uuid;
+       monitor_class->adopt_orphan_mount = adopt_orphan_mount;
+       monitor_class->drive_eject_button = drive_eject_button;
+       monitor_class->drive_stop_button = drive_stop_button;
+}
+
+void g_io_module_load (GIOModule *module) {
+       g_debug (__func__);
+
+       g_type_module_use (G_TYPE_MODULE (module));
+
+       g_io_extension_point_implement (
+           G_NATIVE_VOLUME_MONITOR_EXTENSION_POINT_NAME,
+           MOCK_TYPE_VOLUME_MONITOR, "mockvolumemonitor", 0);
+}
+
+void g_io_module_unload (GIOModule *module) {
+}
diff --git a/tests/functional-tests/mockvolumemonitor/mock-volume-monitor.h 
b/tests/functional-tests/mockvolumemonitor/mock-volume-monitor.h
new file mode 100644
index 000000000..740978edf
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-volume-monitor.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+#ifndef __MOCK_VOLUME_MONITOR_H__
+#define __MOCK_VOLUME_MONITOR_H__
+
+#include <gio/gio.h>
+
+#define MOCK_TYPE_VOLUME_MONITOR mock_volume_monitor_get_type()
+G_DECLARE_FINAL_TYPE (MockVolumeMonitor, mock_volume_monitor, MOCK, VOLUME_MONITOR, GNativeVolumeMonitor)
+
+/* Forward definitions */
+typedef struct _MockDrive MockDrive;
+typedef struct _MockVolume MockVolume;
+typedef struct _MockMount MockMount;
+
+#endif
diff --git a/tests/functional-tests/mockvolumemonitor/mock-volume.c 
b/tests/functional-tests/mockvolumemonitor/mock-volume.c
new file mode 100644
index 000000000..b9773bbaa
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-volume.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "mock-volume.h"
+#include "mock-mount.h"
+#include "mock-drive.h"
+
+struct _MockVolume
+{
+       GObject parent;
+
+       MockVolumeMonitor *monitor; /* owned by volume monitor */
+       MockMount         *mount;   /* owned by volume monitor */
+       MockDrive         *drive;   /* owned by volume monitor */
+
+       gchar *name;
+       gchar *uuid;
+};
+
+static void mock_volume_volume_iface_init (GVolumeIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MockVolume, mock_volume, G_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, mock_volume_volume_iface_init))
+
+static void
+mock_volume_finalize (GObject *object)
+{
+       MockVolume *volume = MOCK_VOLUME (object);
+
+       if (volume->mount != NULL) {
+               mock_mount_unset_volume (volume->mount, volume);
+       }
+
+       if (volume->drive != NULL) {
+               mock_drive_unset_volume (volume->drive, volume);
+       }
+
+       g_free (volume->name);
+       g_free (volume->uuid);
+
+       G_OBJECT_CLASS (mock_volume_parent_class)->finalize (object);
+}
+
+static void
+mock_volume_class_init (MockVolumeClass *klass)
+{
+       GObjectClass *gobject_class;
+
+       gobject_class = G_OBJECT_CLASS (klass);
+       gobject_class->finalize = mock_volume_finalize;
+}
+
+static void
+mock_volume_init (MockVolume *volume)
+{
+}
+
+static void
+emit_changed (MockVolume *volume)
+{
+       g_signal_emit_by_name (volume, "changed");
+       g_signal_emit_by_name (volume->monitor, "volume-changed", volume);
+}
+
+
+MockVolume *
+mock_volume_new (MockVolumeMonitor   *monitor,
+                 MockDrive           *drive,
+                 const gchar         *name)
+{
+       MockVolume *volume;
+
+       volume = g_object_new (MOCK_TYPE_VOLUME, NULL);
+       volume->monitor = monitor;
+
+       volume->drive = drive;
+       if (drive != NULL)
+               mock_drive_set_volume (drive, volume);
+
+       volume->name = g_strdup (name);
+       volume->uuid = g_uuid_string_random ();
+
+       return volume;
+}
+
+void
+mock_volume_removed (MockVolume *volume)
+{
+       if (volume->mount != NULL) {
+               mock_mount_unset_volume (volume->mount, volume);
+               volume->mount = NULL;
+       }
+
+       if (volume->drive != NULL) {
+               mock_drive_unset_volume (volume->drive, volume);
+               volume->drive = NULL;
+       }
+}
+
+void
+mock_volume_set_mount (MockVolume *volume,
+                       MockMount  *mount)
+{
+       if (volume->mount != mount) {
+               if (volume->mount != NULL)
+                       mock_mount_unset_volume (volume->mount, volume);
+
+               volume->mount = mount;
+
+               emit_changed (volume);
+       }
+}
+
+void
+mock_volume_unset_mount (MockVolume *volume,
+                         MockMount  *mount)
+{
+       if (volume->mount == mount) {
+               volume->mount = NULL;
+               emit_changed (volume);
+       }
+}
+
+void
+mock_volume_set_drive (MockVolume *volume,
+                       MockDrive  *drive)
+{
+       if (volume->drive != drive) {
+               if (volume->drive != NULL)
+                       mock_drive_unset_volume (volume->drive, volume);
+               volume->drive = drive;
+               emit_changed (volume);
+       }
+}
+
+void
+mock_volume_unset_drive (MockVolume *volume,
+                         MockDrive  *drive)
+{
+       if (volume->drive == drive) {
+               volume->drive = NULL;
+               emit_changed (volume);
+       }
+}
+
+static GIcon *
+mock_volume_get_icon (GVolume *_volume)
+{
+       return NULL;
+}
+
+static GIcon *
+mock_volume_get_symbolic_icon (GVolume *_volume)
+{
+       return NULL;
+}
+
+static char *
+mock_volume_get_name (GVolume *_volume)
+{
+       MockVolume *volume = MOCK_VOLUME (_volume);
+       return g_strdup (volume->name);
+}
+
+static char *
+mock_volume_get_uuid (GVolume *_volume)
+{
+       MockVolume *volume = MOCK_VOLUME (_volume);
+       return g_strdup (volume->uuid);
+}
+
+static gboolean
+mock_volume_can_mount (GVolume *_volume)
+{
+       return TRUE;
+}
+
+static gboolean
+mock_volume_can_eject (GVolume *_volume)
+{
+       return FALSE;
+}
+
+static gboolean
+mock_volume_should_automount (GVolume *_volume)
+{
+       return TRUE;
+}
+
+static GDrive *
+mock_volume_get_drive (GVolume *_volume)
+{
+       MockVolume *volume = MOCK_VOLUME (_volume);
+       GDrive *drive = NULL;
+
+       if (volume->drive != NULL)
+               drive = G_DRIVE (g_object_ref (volume->drive));
+       return drive;
+}
+
+static GMount *
+mock_volume_get_mount (GVolume *_volume)
+{
+       MockVolume *volume = MOCK_VOLUME (_volume);
+       GMount *mount = NULL;
+
+       if (volume->mount != NULL)
+               mount = G_MOUNT (g_object_ref (volume->mount));
+       return mount;
+}
+
+static gchar *
+mock_volume_get_identifier (GVolume      *_volume,
+                            const gchar  *kind)
+{
+       return g_strdup ("device");
+}
+
+static gchar **
+mock_volume_enumerate_identifiers (GVolume *_volume)
+{
+       return NULL;
+}
+
+static GFile *
+mock_volume_get_activation_root (GVolume *_volume)
+{
+       return NULL;
+}
+
+static const gchar *
+mock_volume_get_sort_key (GVolume *_volume)
+{
+       return NULL;
+}
+
+static void
+mock_volume_volume_iface_init (GVolumeIface *iface)
+{
+       iface->get_name = mock_volume_get_name;
+       iface->get_icon = mock_volume_get_icon;
+       iface->get_symbolic_icon = mock_volume_get_symbolic_icon;
+       iface->get_uuid = mock_volume_get_uuid;
+       iface->get_drive = mock_volume_get_drive;
+       iface->get_mount = mock_volume_get_mount;
+       iface->can_mount = mock_volume_can_mount;
+       iface->can_eject = mock_volume_can_eject;
+       iface->should_automount = mock_volume_should_automount;
+       iface->get_activation_root = mock_volume_get_activation_root;
+       iface->enumerate_identifiers = mock_volume_enumerate_identifiers;
+       iface->get_identifier = mock_volume_get_identifier;
+
+       iface->get_sort_key = mock_volume_get_sort_key;
+}
diff --git a/tests/functional-tests/mockvolumemonitor/mock-volume.h 
b/tests/functional-tests/mockvolumemonitor/mock-volume.h
new file mode 100644
index 000000000..9cb655666
--- /dev/null
+++ b/tests/functional-tests/mockvolumemonitor/mock-volume.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021, Codethink Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * Author: Sam Thursfield <sam afuera me uk>
+ */
+
+#ifndef __MOCK_VOLUME_H__
+#define __MOCK_VOLUME_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gio/gunixmounts.h>
+
+#include "mock-volume-monitor.h"
+
+G_BEGIN_DECLS
+
+#define MOCK_TYPE_VOLUME  (mock_volume_get_type ())
+G_DECLARE_FINAL_TYPE (MockVolume, mock_volume, MOCK, VOLUME, GObject)
+
+MockVolume *mock_volume_new         (MockVolumeMonitor   *monitor,
+                                     MockDrive           *drive,
+                                     const gchar         *name);
+void               mock_volume_removed     (MockVolume          *volume);
+
+GUnixMountPoint   *mock_volume_get_mount_point (MockVolume      *volume);
+
+void               mock_volume_set_mount   (MockVolume          *volume,
+                                            MockMount           *mount);
+void               mock_volume_unset_mount (MockVolume          *volume,
+                                            MockMount           *mount);
+
+void               mock_volume_set_drive   (MockVolume          *volume,
+                                            MockDrive           *drive);
+void               mock_volume_unset_drive (MockVolume          *volume,
+                                            MockDrive           *drive);
+
+G_END_DECLS
+
+#endif /* __MOCK_VOLUME_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]