mirror of
https://github.com/ostreedev/ostree.git
synced 2024-12-23 21:35:26 +03:00
ae335f24dc
This is a basic implementation of OstreeRepoFinder which resolves ref names to remote URIs by looking for them on any currently mounted removable storage volumes. The idea is to support OS and app updates via USB stick. Unit tests are included. This bumps libostree’s maximum GLib dependency from 2.44 to 2.50 for g_drive_is_removable(). If GLib 2.50 is not available, the call which needs it will be omitted and the OstreeRepoFinderMount implementation will scan all volumes (not just removable ones); this is a performance hit, but not a functionality hit. Signed-off-by: Philip Withnall <withnall@endlessm.com> Closes: #924 Approved by: cgwalters
401 lines
11 KiB
C
401 lines
11 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||
*
|
||
* Copyright © 2017 Endless Mobile, Inc.
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2 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, write to the
|
||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||
* Boston, MA 02111-1307, USA.
|
||
*
|
||
* Authors:
|
||
* - Philip Withnall <withnall@endlessm.com>
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <gio/gio.h>
|
||
#include <glib.h>
|
||
#include <glib-object.h>
|
||
#include <libglnx.h>
|
||
|
||
#include "test-mock-gio.h"
|
||
|
||
/**
|
||
* SECTION:mock-gio
|
||
* @title: Mock GIO volume interfaces
|
||
* @short_description: Mock implementations of GIO volume, mount and drive
|
||
* interfaces
|
||
* @stability: Unstable
|
||
* @include: tests/test-mock-gio.h
|
||
*
|
||
* A set of classes implementing GIO interfaces for volumes, mounts, drives
|
||
* and volume monitoring, which return mock data to the caller when used. These
|
||
* are designed for use in unit tests, to mock up removable drives when testing
|
||
* code which monitors such drives being added and removed and then queries
|
||
* properties of them.
|
||
*
|
||
* By returning mock drive locations to the caller, for example, the contents of
|
||
* a removable drive may be mocked up using temporary files.
|
||
*
|
||
* Currently, all the mock data returned by these classes to callers is static,
|
||
* set at construction time.
|
||
*
|
||
* Since: 2017.8
|
||
*/
|
||
|
||
/* Mock volume monitor class. This returns a static set of data to the caller,
|
||
* which it was initialised with. */
|
||
struct _OstreeMockVolumeMonitor
|
||
{
|
||
GVolumeMonitor parent_instance;
|
||
|
||
GList *mounts; /* (element-type OstreeMockMount) */
|
||
GList *volumes; /* (element-type OstreeMockVolume) */
|
||
};
|
||
|
||
G_DEFINE_TYPE (OstreeMockVolumeMonitor, ostree_mock_volume_monitor, G_TYPE_VOLUME_MONITOR)
|
||
|
||
static GList *
|
||
ostree_mock_volume_monitor_get_mounts (GVolumeMonitor *monitor)
|
||
{
|
||
OstreeMockVolumeMonitor *self = OSTREE_MOCK_VOLUME_MONITOR (monitor);
|
||
return g_list_copy_deep (self->mounts, (GCopyFunc) g_object_ref, NULL);
|
||
}
|
||
|
||
static GList *
|
||
ostree_mock_volume_monitor_get_volumes (GVolumeMonitor *monitor)
|
||
{
|
||
OstreeMockVolumeMonitor *self = OSTREE_MOCK_VOLUME_MONITOR (monitor);
|
||
return g_list_copy_deep (self->volumes, (GCopyFunc) g_object_ref, NULL);
|
||
}
|
||
|
||
static void
|
||
ostree_mock_volume_monitor_init (OstreeMockVolumeMonitor *self)
|
||
{
|
||
/* Nothing to see here. */
|
||
}
|
||
|
||
static void
|
||
ostree_mock_volume_monitor_dispose (GObject *object)
|
||
{
|
||
OstreeMockVolumeMonitor *self = OSTREE_MOCK_VOLUME_MONITOR (object);
|
||
|
||
g_list_free_full (self->volumes, g_object_unref);
|
||
self->volumes = NULL;
|
||
|
||
g_list_free_full (self->mounts, g_object_unref);
|
||
self->mounts = NULL;
|
||
|
||
G_OBJECT_CLASS (ostree_mock_volume_monitor_parent_class)->dispose (object);
|
||
}
|
||
|
||
static void
|
||
ostree_mock_volume_monitor_class_init (OstreeMockVolumeMonitorClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
|
||
|
||
object_class->dispose = ostree_mock_volume_monitor_dispose;
|
||
|
||
monitor_class->get_mounts = ostree_mock_volume_monitor_get_mounts;
|
||
monitor_class->get_volumes = ostree_mock_volume_monitor_get_volumes;
|
||
}
|
||
|
||
/**
|
||
* ostree_mock_volume_monitor_new:
|
||
* @mounts: (element-type GMount) (transfer none): list of current #GMounts
|
||
* @volumes: (element-type GVolume) (transfer none): list of current #GVolumes
|
||
*
|
||
* Create a new mock #GVolumeMonitor which will return the given static lists of
|
||
* #GMounts and #GVolumes to any caller of g_volume_monitor_get_mounts() or
|
||
* g_volume_monitor_get_volumes().
|
||
*
|
||
* Typically, the elements of @mounts will be #OstreeMockMount objects and the
|
||
* elements of @volumes will be #OstreeMockVolume objects; but this does not
|
||
* have to be the case.
|
||
*
|
||
* Returns: (transfer full): a new #GVolumeMonitor object
|
||
* Since: 2017.8
|
||
*/
|
||
GVolumeMonitor *
|
||
ostree_mock_volume_monitor_new (GList *mounts,
|
||
GList *volumes)
|
||
{
|
||
g_autoptr(OstreeMockVolumeMonitor) monitor = NULL;
|
||
|
||
monitor = g_object_new (OSTREE_TYPE_MOCK_VOLUME_MONITOR, NULL);
|
||
monitor->mounts = g_list_copy_deep (mounts, (GCopyFunc) g_object_ref, NULL);
|
||
monitor->volumes = g_list_copy_deep (volumes, (GCopyFunc) g_object_ref, NULL);
|
||
|
||
return g_steal_pointer (&monitor);
|
||
}
|
||
|
||
/* Mock volume class. This returns a static set of data to the caller, which it
|
||
* was initialised with. */
|
||
struct _OstreeMockVolume
|
||
{
|
||
GObject parent_instance;
|
||
|
||
gchar *name;
|
||
GDrive *drive; /* (owned) (nullable) */
|
||
GMount *mount; /* (owned) (nullable) */
|
||
};
|
||
|
||
static void ostree_mock_volume_iface_init (GVolumeIface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (OstreeMockVolume, ostree_mock_volume, G_TYPE_OBJECT,
|
||
G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, ostree_mock_volume_iface_init))
|
||
|
||
static gchar *
|
||
ostree_mock_volume_get_name (GVolume *volume)
|
||
{
|
||
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (volume);
|
||
return g_strdup (self->name);
|
||
}
|
||
|
||
static GDrive *
|
||
ostree_mock_volume_get_drive (GVolume *volume)
|
||
{
|
||
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (volume);
|
||
return (self->drive != NULL) ? g_object_ref (self->drive) : NULL;
|
||
}
|
||
|
||
static GMount *
|
||
ostree_mock_volume_get_mount (GVolume *volume)
|
||
{
|
||
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (volume);
|
||
return (self->mount != NULL) ? g_object_ref (self->mount) : NULL;
|
||
}
|
||
|
||
static void
|
||
ostree_mock_volume_init (OstreeMockVolume *self)
|
||
{
|
||
/* Nothing to see here. */
|
||
}
|
||
|
||
static void
|
||
ostree_mock_volume_dispose (GObject *object)
|
||
{
|
||
OstreeMockVolume *self = OSTREE_MOCK_VOLUME (object);
|
||
|
||
g_clear_pointer (&self->name, g_free);
|
||
g_clear_object (&self->drive);
|
||
g_clear_object (&self->mount);
|
||
|
||
G_OBJECT_CLASS (ostree_mock_volume_parent_class)->dispose (object);
|
||
}
|
||
|
||
static void
|
||
ostree_mock_volume_class_init (OstreeMockVolumeClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
||
object_class->dispose = ostree_mock_volume_dispose;
|
||
}
|
||
|
||
static void
|
||
ostree_mock_volume_iface_init (GVolumeIface *iface)
|
||
{
|
||
iface->get_name = ostree_mock_volume_get_name;
|
||
iface->get_drive = ostree_mock_volume_get_drive;
|
||
iface->get_mount = ostree_mock_volume_get_mount;
|
||
}
|
||
|
||
/**
|
||
* ostree_mock_volume_new:
|
||
* @name: volume name
|
||
* @drive: (transfer none) (nullable): drive for the volume, or %NULL if none
|
||
* should be associated
|
||
* @mount: (transfer none) (nullable): mount for the volume, or %NULL if it’s
|
||
* not mounted
|
||
*
|
||
* Create a new mock #GVolume which will return the given static @name, @drive
|
||
* and @mount to any caller of its getter methods. There is currently no
|
||
* provision for changing these values dynamically. There is also currently no
|
||
* provision for mocking the other getters of #GVolume.
|
||
*
|
||
* Typically, @drive will be an #OstreeMockDrive object and @mount will be an
|
||
* #OstreeMockMount object; but this does not have to be the case.
|
||
*
|
||
* Returns: (transfer full): a new #GVolume object
|
||
* Since: 2017.8
|
||
*/
|
||
OstreeMockVolume *
|
||
ostree_mock_volume_new (const gchar *name,
|
||
GDrive *drive,
|
||
GMount *mount)
|
||
{
|
||
g_autoptr(OstreeMockVolume) volume = NULL;
|
||
|
||
volume = g_object_new (OSTREE_TYPE_MOCK_VOLUME, NULL);
|
||
volume->name = g_strdup (name);
|
||
volume->drive = (drive != NULL) ? g_object_ref (drive) : NULL;
|
||
volume->mount = (mount != NULL) ? g_object_ref (mount) : NULL;
|
||
|
||
return g_steal_pointer (&volume);
|
||
}
|
||
|
||
/* Mock drive class. This returns a static set of data to the caller, which it
|
||
* was initialised with. */
|
||
struct _OstreeMockDrive
|
||
{
|
||
GObject parent_instance;
|
||
|
||
gboolean is_removable;
|
||
};
|
||
|
||
static void ostree_mock_drive_iface_init (GDriveIface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (OstreeMockDrive, ostree_mock_drive, G_TYPE_OBJECT,
|
||
G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE, ostree_mock_drive_iface_init))
|
||
|
||
#if GLIB_CHECK_VERSION(2, 50, 0)
|
||
static gboolean
|
||
ostree_mock_drive_is_removable (GDrive *drive)
|
||
{
|
||
OstreeMockDrive *self = OSTREE_MOCK_DRIVE (drive);
|
||
return self->is_removable;
|
||
}
|
||
#endif
|
||
|
||
static void
|
||
ostree_mock_drive_init (OstreeMockDrive *self)
|
||
{
|
||
/* Nothing to see here. */
|
||
}
|
||
|
||
static void
|
||
ostree_mock_drive_class_init (OstreeMockDriveClass *klass)
|
||
{
|
||
/* Nothing to see here. */
|
||
}
|
||
|
||
static void
|
||
ostree_mock_drive_iface_init (GDriveIface *iface)
|
||
{
|
||
#if GLIB_CHECK_VERSION(2, 50, 0)
|
||
iface->is_removable = ostree_mock_drive_is_removable;
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* ostree_mock_drive_new:
|
||
* @is_removable: %TRUE if the drive is removable; %FALSE otherwise
|
||
*
|
||
* Create a new mock #GDrive which will return the given static @is_removable to
|
||
* any caller of its getter methods. There is currently no provision for mocking
|
||
* the other getters of #GDrive.
|
||
*
|
||
* Returns: (transfer full): a new #GDrive object
|
||
* Since: 2017.8
|
||
*/
|
||
OstreeMockDrive *
|
||
ostree_mock_drive_new (gboolean is_removable)
|
||
{
|
||
g_autoptr(OstreeMockDrive) drive = NULL;
|
||
|
||
drive = g_object_new (OSTREE_TYPE_MOCK_DRIVE, NULL);
|
||
drive->is_removable = is_removable;
|
||
|
||
return g_steal_pointer (&drive);
|
||
}
|
||
|
||
/* Mock mount class. This returns a static set of data to the caller, which it
|
||
* was initialised with. */
|
||
struct _OstreeMockMount
|
||
{
|
||
GObject parent_instance;
|
||
|
||
gchar *name; /* (owned) */
|
||
GFile *root; /* (owned) */
|
||
};
|
||
|
||
static void ostree_mock_mount_iface_init (GMountIface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (OstreeMockMount, ostree_mock_mount, G_TYPE_OBJECT,
|
||
G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT, ostree_mock_mount_iface_init))
|
||
|
||
static gchar *
|
||
ostree_mock_mount_get_name (GMount *mount)
|
||
{
|
||
OstreeMockMount *self = OSTREE_MOCK_MOUNT (mount);
|
||
return g_strdup (self->name);
|
||
}
|
||
|
||
static GFile *
|
||
ostree_mock_mount_get_root (GMount *mount)
|
||
{
|
||
OstreeMockMount *self = OSTREE_MOCK_MOUNT (mount);
|
||
return g_object_ref (self->root);
|
||
}
|
||
|
||
static void
|
||
ostree_mock_mount_init (OstreeMockMount *self)
|
||
{
|
||
/* Nothing to see here. */
|
||
}
|
||
|
||
static void
|
||
ostree_mock_mount_dispose (GObject *object)
|
||
{
|
||
OstreeMockMount *self = OSTREE_MOCK_MOUNT (object);
|
||
|
||
g_clear_pointer (&self->name, g_free);
|
||
g_clear_object (&self->root);
|
||
|
||
G_OBJECT_CLASS (ostree_mock_mount_parent_class)->dispose (object);
|
||
}
|
||
|
||
static void
|
||
ostree_mock_mount_class_init (OstreeMockMountClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
|
||
object_class->dispose = ostree_mock_mount_dispose;
|
||
}
|
||
|
||
static void
|
||
ostree_mock_mount_iface_init (GMountIface *iface)
|
||
{
|
||
iface->get_name = ostree_mock_mount_get_name;
|
||
iface->get_root = ostree_mock_mount_get_root;
|
||
}
|
||
|
||
/**
|
||
* ostree_mock_mount_new:
|
||
* @name: mount name
|
||
* @root: (transfer none): root path for the mounted file system
|
||
*
|
||
* Create a new mock #GMount which will return the given static @name and @root
|
||
* to any caller of its getter methods. There is currently no provision for
|
||
* mocking the other getters of #GMount.
|
||
*
|
||
* Typically, @root will point to a temporary directory where a mocked file
|
||
* system is present; but this does not have to be the case.
|
||
*
|
||
* Returns: (transfer full): a new #GMount object
|
||
* Since: 2017.8
|
||
*/
|
||
OstreeMockMount *
|
||
ostree_mock_mount_new (const gchar *name,
|
||
GFile *root)
|
||
{
|
||
g_autoptr(OstreeMockMount) mount = NULL;
|
||
|
||
mount = g_object_new (OSTREE_TYPE_MOCK_MOUNT, NULL);
|
||
mount->name = g_strdup (name);
|
||
mount->root = g_object_ref (root);
|
||
|
||
return g_steal_pointer (&mount);
|
||
}
|