summaryrefslogtreecommitdiffstats
path: root/plugins/dummy
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plugins/dummy/gs-plugin-dummy.c1104
-rw-r--r--plugins/dummy/gs-plugin-dummy.h22
-rw-r--r--plugins/dummy/gs-self-test.c966
-rw-r--r--plugins/dummy/meson.build35
4 files changed, 2127 insertions, 0 deletions
diff --git a/plugins/dummy/gs-plugin-dummy.c b/plugins/dummy/gs-plugin-dummy.c
new file mode 100644
index 0000000..ac584af
--- /dev/null
+++ b/plugins/dummy/gs-plugin-dummy.c
@@ -0,0 +1,1104 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2011-2017 Richard Hughes <richard@hughsie.com>
+ * Copyright (C) 2015-2016 Kalev Lember <klember@redhat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+
+#include <gnome-software.h>
+
+#include "gs-plugin-dummy.h"
+
+/*
+ * SECTION:
+ * Provides some dummy data that is useful in self test programs.
+ */
+
+struct _GsPluginDummy {
+ GsPlugin parent;
+
+ guint quirk_id;
+ guint allow_updates_id;
+ gboolean allow_updates_inhibit;
+ GsApp *cached_origin;
+ GHashTable *installed_apps; /* id:1 */
+ GHashTable *available_apps; /* id:1 */
+};
+
+G_DEFINE_TYPE (GsPluginDummy, gs_plugin_dummy, GS_TYPE_PLUGIN)
+
+/* just flip-flop this every few seconds */
+static gboolean
+gs_plugin_dummy_allow_updates_cb (gpointer user_data)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (user_data);
+
+ gs_plugin_set_allow_updates (GS_PLUGIN (self), self->allow_updates_inhibit);
+ self->allow_updates_inhibit = !self->allow_updates_inhibit;
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+gs_plugin_dummy_init (GsPluginDummy *self)
+{
+ GsPlugin *plugin = GS_PLUGIN (self);
+
+ if (g_getenv ("GS_SELF_TEST_DUMMY_ENABLE") == NULL) {
+ g_debug ("disabling '%s' as not in self test",
+ gs_plugin_get_name (plugin));
+ gs_plugin_set_enabled (plugin, FALSE);
+ return;
+ }
+
+ /* need help from appstream */
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "appstream");
+ gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_AFTER, "os-release");
+}
+
+static void
+gs_plugin_dummy_dispose (GObject *object)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (object);
+
+ g_clear_pointer (&self->installed_apps, g_hash_table_unref);
+ g_clear_pointer (&self->available_apps, g_hash_table_unref);
+ g_clear_handle_id (&self->quirk_id, g_source_remove);
+ g_clear_object (&self->cached_origin);
+
+ G_OBJECT_CLASS (gs_plugin_dummy_parent_class)->dispose (object);
+}
+
+static void
+gs_plugin_dummy_setup_async (GsPlugin *plugin,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (plugin);
+ g_autoptr(GTask) task = NULL;
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_dummy_setup_async);
+
+ /* toggle this */
+ if (g_getenv ("GS_SELF_TEST_TOGGLE_ALLOW_UPDATES") != NULL) {
+ self->allow_updates_id = g_timeout_add_seconds (10,
+ gs_plugin_dummy_allow_updates_cb, plugin);
+ }
+
+ /* add source */
+ self->cached_origin = gs_app_new (gs_plugin_get_name (plugin));
+ gs_app_set_kind (self->cached_origin, AS_COMPONENT_KIND_REPOSITORY);
+ gs_app_set_origin_hostname (self->cached_origin, "http://www.bbc.co.uk/");
+ gs_app_set_management_plugin (self->cached_origin, plugin);
+
+ /* add the source to the plugin cache which allows us to match the
+ * unique ID to a GsApp when creating an event */
+ gs_plugin_cache_add (plugin, NULL, self->cached_origin);
+
+ /* keep track of what apps are installed */
+ self->installed_apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ self->available_apps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ g_hash_table_insert (self->available_apps,
+ g_strdup ("chiron.desktop"),
+ GUINT_TO_POINTER (1));
+ g_hash_table_insert (self->available_apps,
+ g_strdup ("zeus.desktop"),
+ GUINT_TO_POINTER (1));
+ g_hash_table_insert (self->available_apps,
+ g_strdup ("zeus-spell.addon"),
+ GUINT_TO_POINTER (1));
+ g_hash_table_insert (self->available_apps,
+ g_strdup ("com.hughski.ColorHug2.driver"),
+ GUINT_TO_POINTER (1));
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+gs_plugin_dummy_setup_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+void
+gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app)
+{
+ if (gs_app_get_id (app) != NULL &&
+ g_str_has_prefix (gs_app_get_id (app), "dummy:")) {
+ gs_app_set_management_plugin (app, plugin);
+ return;
+ }
+ if (g_strcmp0 (gs_app_get_id (app), "mate-spell.desktop") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "zeus.desktop") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "com.hughski.ColorHug2.driver") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "zeus-spell.addon") == 0 ||
+ g_strcmp0 (gs_app_get_source_default (app), "chiron") == 0)
+ gs_app_set_management_plugin (app, plugin);
+}
+
+static gboolean
+gs_plugin_dummy_delay (GsPlugin *plugin,
+ GsApp *app,
+ guint timeout_ms,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ guint i;
+ guint timeout_us = timeout_ms * 10;
+
+ /* do blocking delay in 1% increments */
+ for (i = 0; i < 100; i++) {
+ g_usleep (timeout_us);
+ if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+ gs_utils_error_convert_gio (error);
+ ret = FALSE;
+ break;
+ }
+ if (app != NULL)
+ gs_app_set_progress (app, i);
+ gs_plugin_status_update (plugin, app,
+ GS_PLUGIN_STATUS_DOWNLOADING);
+ }
+ return ret;
+}
+
+typedef struct {
+ GsApp *app; /* (owned) (nullable) */
+ guint percent_complete;
+} DelayData;
+
+static void
+delay_data_free (DelayData *data)
+{
+ g_clear_object (&data->app);
+ g_free (data);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (DelayData, delay_data_free)
+
+static gboolean delay_timeout_cb (gpointer user_data);
+
+/* Simulate a download on app, updating its progress one percentage point at a
+ * time, with an overall interval of @timeout_ms to go from 0% to 100%. The
+ * download is cancelled within @timeout_ms / 100 if @cancellable is cancelled. */
+static void
+gs_plugin_dummy_delay_async (GsPlugin *plugin,
+ GsApp *app,
+ guint timeout_ms,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(DelayData) data = NULL;
+ g_autoptr(GSource) source = NULL;
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_dummy_delay_async);
+
+ data = g_new0 (DelayData, 1);
+ data->app = (app != NULL) ? g_object_ref (app) : NULL;
+ data->percent_complete = 0;
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) delay_data_free);
+
+ source = g_timeout_source_new (timeout_ms / 100);
+ g_task_attach_source (task, source, delay_timeout_cb);
+}
+
+static gboolean
+delay_timeout_cb (gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ GsPlugin *plugin = g_task_get_source_object (task);
+ GCancellable *cancellable = g_task_get_cancellable (task);
+ DelayData *data = g_task_get_task_data (task);
+ g_autoptr(GError) local_error = NULL;
+
+ /* Iterate until 100%. */
+ if (data->percent_complete >= 100) {
+ g_task_return_boolean (task, TRUE);
+ return G_SOURCE_REMOVE;
+ }
+
+ /* Has the task been cancelled? */
+ if (g_cancellable_set_error_if_cancelled (cancellable, &local_error)) {
+ gs_utils_error_convert_gio (&local_error);
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ return G_SOURCE_REMOVE;
+ }
+
+ /* Update the app’s progress and continue. */
+ if (data->app != NULL)
+ gs_app_set_progress (data->app, data->percent_complete);
+ gs_plugin_status_update (plugin, data->app, GS_PLUGIN_STATUS_DOWNLOADING);
+
+ data->percent_complete++;
+
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+gs_plugin_dummy_delay_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+gs_plugin_dummy_poll_cb (gpointer user_data)
+{
+ g_autoptr(GsApp) app = NULL;
+ GsPlugin *plugin = GS_PLUGIN (user_data);
+
+ /* find the app in the per-plugin cache -- this assumes that we can
+ * calculate the same key as used when calling gs_plugin_cache_add() */
+ app = gs_plugin_cache_lookup (plugin, "chiron");
+ if (app == NULL) {
+ g_warning ("app not found in cache!");
+ return FALSE;
+ }
+
+ /* toggle this to animate the hide/show the 3rd party banner */
+ if (!gs_app_has_quirk (app, GS_APP_QUIRK_PROVENANCE)) {
+ g_debug ("about to make app distro-provided");
+ gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
+ } else {
+ g_debug ("about to make app 3rd party");
+ gs_app_remove_quirk (app, GS_APP_QUIRK_PROVENANCE);
+ }
+
+ /* continue polling */
+ return TRUE;
+}
+
+gboolean
+gs_plugin_url_to_app (GsPlugin *plugin,
+ GsAppList *list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *scheme = NULL;
+ g_autoptr(GsApp) app = NULL;
+
+ /* not us */
+ scheme = gs_utils_get_url_scheme (url);
+ if (g_strcmp0 (scheme, "dummy") != 0)
+ return TRUE;
+
+ /* create app */
+ path = gs_utils_get_url_path (url);
+ app = gs_app_new (path);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_set_metadata (app, "GnomeSoftware::Creator",
+ gs_plugin_get_name (plugin));
+ gs_app_list_add (list, app);
+ return TRUE;
+}
+
+static gboolean timeout_cb (gpointer user_data);
+
+/* Simulate a cancellable delay */
+static void
+gs_plugin_dummy_timeout_async (GsPluginDummy *self,
+ guint timeout_ms,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GSource) source = NULL;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_dummy_timeout_async);
+
+ source = g_timeout_source_new (timeout_ms);
+
+ if (cancellable != NULL) {
+ g_autoptr(GSource) cancellable_source = NULL;
+
+ cancellable_source = g_cancellable_source_new (cancellable);
+ g_source_set_dummy_callback (cancellable_source);
+ g_source_add_child_source (source, cancellable_source);
+ }
+
+ g_task_attach_source (task, source, timeout_cb);
+}
+
+static gboolean
+timeout_cb (gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+
+ if (!g_task_return_error_if_cancelled (task))
+ g_task_return_boolean (task, TRUE);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+gs_plugin_dummy_timeout_finish (GsPluginDummy *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+gs_plugin_add_updates (GsPlugin *plugin,
+ GsAppList *list,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsApp *app;
+ GsApp *proxy;
+ g_autoptr(GIcon) ic = NULL;
+
+ /* update UI as this might take some time */
+ gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_WAITING);
+
+ /* spin */
+ if (!gs_plugin_dummy_delay (plugin, NULL, 2000, cancellable, error))
+ return FALSE;
+
+ /* use a generic stock icon */
+ ic = g_themed_icon_new ("drive-harddisk");
+
+ /* add a live updatable normal application */
+ app = gs_app_new ("chiron.desktop");
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Chiron");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "A teaching application");
+ gs_app_set_update_details_text (app, "Do not crash when using libvirt.");
+ gs_app_set_update_urgency (app, AS_URGENCY_KIND_HIGH);
+ gs_app_add_icon (app, ic);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_state (app, GS_APP_STATE_UPDATABLE_LIVE);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_list_add (list, app);
+ g_object_unref (app);
+
+ /* add a offline OS update */
+ app = gs_app_new (NULL);
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "libvirt-glib-devel");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "Development files for libvirt");
+ gs_app_set_update_details_text (app, "Fix several memory leaks.");
+ gs_app_set_update_urgency (app, AS_URGENCY_KIND_LOW);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_GENERIC);
+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE);
+ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM);
+ gs_app_set_state (app, GS_APP_STATE_UPDATABLE);
+ gs_app_add_source (app, "libvirt-glib-devel");
+ gs_app_add_source_id (app, "libvirt-glib-devel;0.0.1;noarch;fedora");
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_list_add (list, app);
+ g_object_unref (app);
+
+ /* add a live OS update */
+ app = gs_app_new (NULL);
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "chiron-libs");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "library for chiron");
+ gs_app_set_update_details_text (app, "Do not crash when using libvirt.");
+ gs_app_set_update_urgency (app, AS_URGENCY_KIND_HIGH);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_GENERIC);
+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE);
+ gs_app_set_scope (app, AS_COMPONENT_SCOPE_SYSTEM);
+ gs_app_set_state (app, GS_APP_STATE_UPDATABLE_LIVE);
+ gs_app_add_source (app, "chiron-libs");
+ gs_app_add_source_id (app, "chiron-libs;0.0.1;i386;updates-testing");
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_list_add (list, app);
+ g_object_unref (app);
+
+ /* add a proxy app update */
+ proxy = gs_app_new ("proxy.desktop");
+ gs_app_set_name (proxy, GS_APP_QUALITY_NORMAL, "Proxy");
+ gs_app_set_summary (proxy, GS_APP_QUALITY_NORMAL, "A proxy app");
+ gs_app_set_update_details_text (proxy, "Update all related apps.");
+ gs_app_set_update_urgency (proxy, AS_URGENCY_KIND_HIGH);
+ gs_app_add_icon (proxy, ic);
+ gs_app_set_kind (proxy, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_add_quirk (proxy, GS_APP_QUIRK_IS_PROXY);
+ gs_app_set_state (proxy, GS_APP_STATE_UPDATABLE_LIVE);
+ gs_app_set_management_plugin (proxy, plugin);
+ gs_app_list_add (list, proxy);
+ g_object_unref (proxy);
+
+ /* add a proxy related app */
+ app = gs_app_new ("proxy-related-app.desktop");
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Related app");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "A related app");
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_state (app, GS_APP_STATE_UPDATABLE_LIVE);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_add_related (proxy, app);
+ g_object_unref (app);
+
+ /* add another proxy related app */
+ app = gs_app_new ("proxy-another-related-app.desktop");
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Another Related app");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "A related app");
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_state (app, GS_APP_STATE_UPDATABLE_LIVE);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_add_related (proxy, app);
+ g_object_unref (app);
+
+ return TRUE;
+}
+
+gboolean
+gs_plugin_app_remove (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (plugin);
+
+ /* only process this app if was created by this plugin */
+ if (!gs_app_has_management_plugin (app, plugin))
+ return TRUE;
+
+ /* remove app */
+ if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0) {
+ gs_app_set_state (app, GS_APP_STATE_REMOVING);
+ if (!gs_plugin_dummy_delay (plugin, app, 500, cancellable, error)) {
+ gs_app_set_state_recover (app);
+ return FALSE;
+ }
+ gs_app_set_state (app, GS_APP_STATE_UNKNOWN);
+ }
+
+ /* keep track */
+ g_hash_table_remove (self->installed_apps, gs_app_get_id (app));
+ g_hash_table_insert (self->available_apps,
+ g_strdup (gs_app_get_id (app)),
+ GUINT_TO_POINTER (1));
+ return TRUE;
+}
+
+gboolean
+gs_plugin_app_install (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (plugin);
+
+ /* only process this app if was created by this plugin */
+ if (!gs_app_has_management_plugin (app, plugin))
+ return TRUE;
+
+ /* install app */
+ if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "zeus.desktop") == 0) {
+ gs_app_set_state (app, GS_APP_STATE_INSTALLING);
+ if (!gs_plugin_dummy_delay (plugin, app, 500, cancellable, error)) {
+ gs_app_set_state_recover (app);
+ return FALSE;
+ }
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
+ }
+
+ /* keep track */
+ g_hash_table_insert (self->installed_apps,
+ g_strdup (gs_app_get_id (app)),
+ GUINT_TO_POINTER (1));
+ g_hash_table_remove (self->available_apps, gs_app_get_id (app));
+
+ return TRUE;
+}
+
+gboolean
+gs_plugin_update_app (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (plugin);
+
+ /* only process this app if was created by this plugin */
+ if (!gs_app_has_management_plugin (app, plugin))
+ return TRUE;
+
+ if (!g_str_has_prefix (gs_app_get_id (app), "proxy")) {
+ /* always fail */
+ g_set_error_literal (error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_DOWNLOAD_FAILED,
+ "no network connection is available");
+ gs_utils_error_add_origin_id (error, self->cached_origin);
+ return FALSE;
+ }
+
+ /* simulate an update for 4 seconds */
+ gs_app_set_state (app, GS_APP_STATE_INSTALLING);
+ for (guint i = 1; i <= 4; ++i) {
+ gs_app_set_progress (app, 25 * i);
+ sleep (1);
+ }
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
+
+ return TRUE;
+}
+
+static gboolean
+refine_app (GsPluginDummy *self,
+ GsApp *app,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* make the local system EOL */
+ if (gs_app_get_metadata_item (app, "GnomeSoftware::CpeName") != NULL)
+ gs_app_set_state (app, GS_APP_STATE_UNAVAILABLE);
+
+ /* state */
+ if (gs_app_get_state (app) == GS_APP_STATE_UNKNOWN) {
+ if (g_hash_table_lookup (self->installed_apps,
+ gs_app_get_id (app)) != NULL)
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
+ if (g_hash_table_lookup (self->available_apps,
+ gs_app_get_id (app)) != NULL)
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+ }
+
+ /* kind */
+ if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "mate-spell.desktop") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "com.hughski.ColorHug2.driver") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "zeus.desktop") == 0) {
+ if (gs_app_get_kind (app) == AS_COMPONENT_KIND_UNKNOWN)
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ }
+
+ /* license */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE) {
+ if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0 ||
+ g_strcmp0 (gs_app_get_id (app), "zeus.desktop") == 0)
+ gs_app_set_license (app, GS_APP_QUALITY_HIGHEST, "GPL-2.0+");
+ }
+
+ /* homepage */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_URL) {
+ if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0) {
+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE,
+ "http://www.test.org/");
+ }
+ }
+
+ /* origin */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN) {
+ if (g_strcmp0 (gs_app_get_id (app), "zeus-spell.addon") == 0)
+ gs_app_set_origin (app, "london-east");
+ }
+
+ /* default */
+ if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0) {
+ if (gs_app_get_name (app) == NULL)
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "tmp");
+ if (gs_app_get_summary (app) == NULL)
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "tmp");
+ if (gs_app_get_icons (app) == NULL) {
+ g_autoptr(GIcon) ic = g_themed_icon_new ("drive-harddisk");
+ gs_app_add_icon (app, ic);
+ }
+ }
+
+ /* description */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION) {
+ if (g_strcmp0 (gs_app_get_id (app), "chiron.desktop") == 0) {
+ gs_app_set_description (app, GS_APP_QUALITY_NORMAL,
+ "long description!");
+ }
+ }
+
+ /* add fake review */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS) {
+ g_autoptr(AsReview) review1 = NULL;
+ g_autoptr(AsReview) review2 = NULL;
+ g_autoptr(GDateTime) dt = NULL;
+
+ dt = g_date_time_new_now_utc ();
+
+ /* set first review */
+ review1 = as_review_new ();
+ as_review_set_rating (review1, 50);
+ as_review_set_reviewer_name (review1, "Angela Avery");
+ as_review_set_summary (review1, "Steep learning curve, but worth it");
+ as_review_set_description (review1, "Best overall 3D application I've ever used overall 3D application I've ever used. Best overall 3D application I've ever used overall 3D application I've ever used. Best overall 3D application I've ever used overall 3D application I've ever used. Best overall 3D application I've ever used overall 3D application I've ever used.");
+ as_review_set_version (review1, "3.16.4");
+ as_review_set_date (review1, dt);
+ gs_app_add_review (app, review1);
+
+ /* set self review */
+ review2 = as_review_new ();
+ as_review_set_rating (review2, 100);
+ as_review_set_reviewer_name (review2, "Just Myself");
+ as_review_set_summary (review2, "I like this application");
+ as_review_set_description (review2, "I'm not very wordy myself.");
+ as_review_set_version (review2, "3.16.3");
+ as_review_set_date (review2, dt);
+ as_review_set_flags (review2, AS_REVIEW_FLAG_SELF);
+ gs_app_add_review (app, review2);
+ }
+
+ /* add fake ratings */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS) {
+ g_autoptr(GArray) ratings = NULL;
+ const gint data[] = { 0, 10, 20, 30, 15, 2 };
+ ratings = g_array_sized_new (FALSE, FALSE, sizeof (gint), 6);
+ g_array_append_vals (ratings, data, 6);
+ gs_app_set_review_ratings (app, ratings);
+ }
+
+ /* add a rating */
+ if (flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING) {
+ gs_app_set_rating (app, 66);
+ }
+
+ return TRUE;
+}
+
+static void
+gs_plugin_dummy_refine_async (GsPlugin *plugin,
+ GsAppList *list,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (plugin);
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GError) local_error = NULL;
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_dummy_refine_async);
+
+ for (guint i = 0; i < gs_app_list_length (list); i++) {
+ GsApp *app = gs_app_list_index (list, i);
+
+ if (!refine_app (self, app, flags, cancellable, &local_error)) {
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ return;
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+gs_plugin_dummy_refine_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void list_apps_timeout_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data);
+
+static void
+gs_plugin_dummy_list_apps_async (GsPlugin *plugin,
+ GsAppQuery *query,
+ GsPluginListAppsFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (plugin);
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GsAppList) list = gs_app_list_new ();
+ GDateTime *released_since = NULL;
+ GsAppQueryTristate is_curated = GS_APP_QUERY_TRISTATE_UNSET;
+ guint max_results = 0;
+ GsCategory *category = NULL;
+ GsAppQueryTristate is_installed = GS_APP_QUERY_TRISTATE_UNSET;
+ const gchar * const *keywords = NULL;
+ GsApp *alternate_of = NULL;
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_dummy_list_apps_async);
+
+ if (query != NULL) {
+ released_since = gs_app_query_get_released_since (query);
+ is_curated = gs_app_query_get_is_curated (query);
+ max_results = gs_app_query_get_max_results (query);
+ category = gs_app_query_get_category (query);
+ is_installed = gs_app_query_get_is_installed (query);
+ keywords = gs_app_query_get_keywords (query);
+ alternate_of = gs_app_query_get_alternate_of (query);
+ }
+
+ /* Currently only support a subset of query properties, and only one set at once.
+ * Also don’t currently support GS_APP_QUERY_TRISTATE_FALSE. */
+ if ((released_since == NULL &&
+ is_curated == GS_APP_QUERY_TRISTATE_UNSET &&
+ category == NULL &&
+ is_installed == GS_APP_QUERY_TRISTATE_UNSET &&
+ keywords == NULL &&
+ alternate_of == NULL) ||
+ is_curated == GS_APP_QUERY_TRISTATE_FALSE ||
+ is_installed == GS_APP_QUERY_TRISTATE_FALSE ||
+ gs_app_query_get_n_properties_set (query) != 1) {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "Unsupported query");
+ return;
+ }
+
+ if (released_since != NULL) {
+ g_autoptr(GIcon) icon = g_themed_icon_new ("chiron.desktop");
+ g_autoptr(GsApp) app = gs_app_new ("chiron.desktop");
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Chiron");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "View and use virtual machines");
+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, "http://www.box.org");
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+ gs_app_add_icon (app, icon);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_management_plugin (app, plugin);
+
+ gs_app_list_add (list, app);
+ }
+
+ if (is_curated != GS_APP_QUERY_TRISTATE_UNSET) {
+ g_autoptr(GsApp) app1 = NULL;
+ g_autoptr(GsApp) app2 = NULL;
+
+ /* Hacky way of letting callers indicate which set of results
+ * they want, for unit testing. */
+ if (max_results == 6) {
+ const gchar *apps[] = { "chiron.desktop", "zeus.desktop" };
+ for (gsize i = 0; i < G_N_ELEMENTS (apps); i++) {
+ g_autoptr(GsApp) app = gs_app_new (apps[i]);
+ gs_app_add_quirk (app, GS_APP_QUIRK_IS_WILDCARD);
+ gs_app_list_add (list, app);
+ }
+ } else {
+ /* add wildcard */
+ app1 = gs_app_new ("zeus.desktop");
+ gs_app_add_quirk (app1, GS_APP_QUIRK_IS_WILDCARD);
+ gs_app_set_metadata (app1, "GnomeSoftware::Creator",
+ gs_plugin_get_name (plugin));
+ gs_app_list_add (list, app1);
+ }
+ }
+
+ if (category != NULL) {
+ g_autoptr(GIcon) icon = g_themed_icon_new ("chiron.desktop");
+ g_autoptr(GsApp) app = gs_app_new ("chiron.desktop");
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Chiron");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "View and use virtual machines");
+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, "http://www.box.org");
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+ gs_app_add_icon (app, icon);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_list_add (list, app);
+ }
+
+ if (is_installed != GS_APP_QUERY_TRISTATE_UNSET) {
+ const gchar *packages[] = { "zeus", "zeus-common", NULL };
+ const gchar *app_ids[] = { "Uninstall Zeus.desktop", NULL };
+
+ /* add all packages */
+ for (gsize i = 0; packages[i] != NULL; i++) {
+ g_autoptr(GsApp) app = gs_app_new (NULL);
+ gs_app_add_source (app, packages[i]);
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_GENERIC);
+ gs_app_set_origin (app, "london-west");
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_list_add (list, app);
+ }
+
+ /* add all app-ids */
+ for (gsize i = 0; app_ids[i] != NULL; i++) {
+ g_autoptr(GsApp) app = gs_app_new (app_ids[i]);
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_list_add (list, app);
+ }
+ }
+
+ if (keywords != NULL) {
+ if (g_strcmp0 (keywords[0], "hang") == 0) {
+ /* hang the plugin for 5 seconds */
+ gs_plugin_dummy_timeout_async (self, 5000, cancellable,
+ list_apps_timeout_cb, g_steal_pointer (&task));
+ return;
+ } else if (g_strcmp0 (keywords[0], "chiron") == 0) {
+ g_autoptr(GsApp) app = NULL;
+
+ /* does the app already exist? */
+ app = gs_plugin_cache_lookup (plugin, "chiron");
+ if (app != NULL) {
+ g_debug ("using %s fom the cache", gs_app_get_id (app));
+ gs_app_list_add (list, app);
+ } else {
+ g_autoptr(GIcon) icon = NULL;
+
+ /* set up a timeout to emulate getting a GFileMonitor callback */
+ self->quirk_id =
+ g_timeout_add_seconds (1, gs_plugin_dummy_poll_cb, plugin);
+
+ /* use a generic stock icon */
+ icon = g_themed_icon_new ("drive-harddisk");
+
+ /* add a live updatable normal application */
+ app = gs_app_new ("chiron.desktop");
+ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Chiron");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "A teaching application");
+ gs_app_add_icon (app, icon);
+ gs_app_set_size_installed (app, GS_SIZE_TYPE_VALID, 42 * 1024 * 1024);
+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 50 * 1024 * 1024);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP);
+ gs_app_set_state (app, GS_APP_STATE_INSTALLED);
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_set_metadata (app, "GnomeSoftware::Creator",
+ gs_plugin_get_name (plugin));
+ gs_app_list_add (list, app);
+
+ /* add to cache so it can be found by the flashing callback */
+ gs_plugin_cache_add (plugin, NULL, app);
+ }
+ } else {
+ /* Don’t do anything */
+ }
+ }
+
+ if (alternate_of != NULL) {
+ if (g_strcmp0 (gs_app_get_id (alternate_of), "zeus.desktop") == 0) {
+ g_autoptr(GsApp) app = gs_app_new ("chiron.desktop");
+ gs_app_list_add (list, app);
+ }
+ }
+
+ g_task_return_pointer (task, g_steal_pointer (&list), (GDestroyNotify) g_object_unref);
+}
+
+static void
+list_apps_timeout_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GsPluginDummy *self = GS_PLUGIN_DUMMY (object);
+ g_autoptr(GTask) task = g_steal_pointer (&user_data);
+ g_autoptr(GError) local_error = NULL;
+
+ /* Return a cancelled error, or an empty app list after hanging. */
+ if (gs_plugin_dummy_timeout_finish (self, result, &local_error))
+ g_task_return_pointer (task, gs_app_list_new (), (GDestroyNotify) g_object_unref);
+ else
+ g_task_return_error (task, g_steal_pointer (&local_error));
+}
+
+static GsAppList *
+gs_plugin_dummy_list_apps_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+gs_plugin_dummy_list_distro_upgrades_async (GsPlugin *plugin,
+ GsPluginListDistroUpgradesFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GIcon) ic = NULL;
+ g_autofree gchar *background_filename = NULL;
+ g_autofree gchar *css = NULL;
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GsAppList) list = gs_app_list_new ();
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_dummy_list_distro_upgrades_async);
+
+ /* use stock icon */
+ ic = g_themed_icon_new ("system-component-addon");
+
+ /* get existing item from the cache */
+ app = gs_plugin_cache_lookup (plugin, "user/*/os-upgrade/org.fedoraproject.release-rawhide.upgrade/*");
+ if (app != NULL) {
+ gs_app_list_add (list, app);
+
+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref);
+ return;
+ }
+
+ app = gs_app_new ("org.fedoraproject.release-rawhide.upgrade");
+ gs_app_set_scope (app, AS_COMPONENT_SCOPE_USER);
+ gs_app_set_kind (app, AS_COMPONENT_KIND_OPERATING_SYSTEM);
+ gs_app_set_bundle_kind (app, AS_BUNDLE_KIND_PACKAGE);
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+ gs_app_set_name (app, GS_APP_QUALITY_LOWEST, "Fedora");
+ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL,
+ "A major upgrade, with new features and added polish.");
+ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE,
+ "https://fedoraproject.org/wiki/Releases/24/Schedule");
+ gs_app_add_quirk (app, GS_APP_QUIRK_NEEDS_REBOOT);
+ gs_app_add_quirk (app, GS_APP_QUIRK_PROVENANCE);
+ gs_app_add_quirk (app, GS_APP_QUIRK_NOT_REVIEWABLE);
+ gs_app_set_version (app, "34");
+ gs_app_set_size_installed (app, GS_SIZE_TYPE_VALID, 256 * 1024 * 1024);
+ gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 1024 * 1024 * 1024);
+ gs_app_set_license (app, GS_APP_QUALITY_LOWEST, "LicenseRef-free");
+ gs_app_set_management_plugin (app, plugin);
+
+ /* Check for a background image in the standard location. */
+ background_filename = gs_utils_get_upgrade_background ("34");
+
+ if (background_filename != NULL)
+ css = g_strconcat ("background: url('file://", background_filename, "');"
+ "background-size: 100% 100%;", NULL);
+ gs_app_set_metadata (app, "GnomeSoftware::UpgradeBanner-css", css);
+
+ gs_app_add_icon (app, ic);
+ gs_app_list_add (list, app);
+
+ gs_plugin_cache_add (plugin, NULL, app);
+
+ g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref);
+}
+
+static GsAppList *
+gs_plugin_dummy_list_distro_upgrades_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+gboolean
+gs_plugin_download_app (GsPlugin *plugin,
+ GsApp *app,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return gs_plugin_dummy_delay (plugin, app, 5100, cancellable, error);
+}
+
+static void refresh_metadata_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data);
+
+static void
+gs_plugin_dummy_refresh_metadata_async (GsPlugin *plugin,
+ guint64 cache_age_secs,
+ GsPluginRefreshMetadataFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+ g_autoptr(GsApp) app = NULL;
+
+ task = g_task_new (plugin, cancellable, callback, user_data);
+ g_task_set_source_tag (task, gs_plugin_dummy_refresh_metadata_async);
+
+ app = gs_app_new (NULL);
+ gs_plugin_dummy_delay_async (plugin, app, 3100, cancellable, refresh_metadata_cb, g_steal_pointer (&task));
+}
+
+static void
+refresh_metadata_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GsPlugin *plugin = GS_PLUGIN (source_object);
+ g_autoptr(GTask) task = g_steal_pointer (&user_data);
+ g_autoptr(GError) local_error = NULL;
+
+ if (!gs_plugin_dummy_delay_finish (plugin, result, &local_error))
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ else
+ g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+gs_plugin_dummy_refresh_metadata_finish (GsPlugin *plugin,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+gboolean
+gs_plugin_app_upgrade_download (GsPlugin *plugin, GsApp *app,
+ GCancellable *cancellable, GError **error)
+{
+ /* only process this app if was created by this plugin */
+ if (!gs_app_has_management_plugin (app, plugin))
+ return TRUE;
+
+ g_debug ("starting download");
+ gs_app_set_state (app, GS_APP_STATE_INSTALLING);
+ if (!gs_plugin_dummy_delay (plugin, app, 5000, cancellable, error)) {
+ gs_app_set_state_recover (app);
+ return FALSE;
+ }
+ gs_app_set_state (app, GS_APP_STATE_UPDATABLE);
+ return TRUE;
+}
+
+gboolean
+gs_plugin_app_upgrade_trigger (GsPlugin *plugin, GsApp *app,
+ GCancellable *cancellable, GError **error)
+{
+ /* only process this app if was created by this plugin */
+ if (!gs_app_has_management_plugin (app, plugin))
+ return TRUE;
+
+ /* NOP */
+ return TRUE;
+}
+
+gboolean
+gs_plugin_update_cancel (GsPlugin *plugin, GsApp *app,
+ GCancellable *cancellable, GError **error)
+{
+ return TRUE;
+}
+
+static void
+gs_plugin_dummy_class_init (GsPluginDummyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass);
+
+ object_class->dispose = gs_plugin_dummy_dispose;
+
+ plugin_class->setup_async = gs_plugin_dummy_setup_async;
+ plugin_class->setup_finish = gs_plugin_dummy_setup_finish;
+ plugin_class->refine_async = gs_plugin_dummy_refine_async;
+ plugin_class->refine_finish = gs_plugin_dummy_refine_finish;
+ plugin_class->list_apps_async = gs_plugin_dummy_list_apps_async;
+ plugin_class->list_apps_finish = gs_plugin_dummy_list_apps_finish;
+ plugin_class->refresh_metadata_async = gs_plugin_dummy_refresh_metadata_async;
+ plugin_class->refresh_metadata_finish = gs_plugin_dummy_refresh_metadata_finish;
+ plugin_class->list_distro_upgrades_async = gs_plugin_dummy_list_distro_upgrades_async;
+ plugin_class->list_distro_upgrades_finish = gs_plugin_dummy_list_distro_upgrades_finish;
+}
+
+GType
+gs_plugin_query_type (void)
+{
+ return GS_TYPE_PLUGIN_DUMMY;
+}
diff --git a/plugins/dummy/gs-plugin-dummy.h b/plugins/dummy/gs-plugin-dummy.h
new file mode 100644
index 0000000..6ec64a8
--- /dev/null
+++ b/plugins/dummy/gs-plugin-dummy.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2021 Endless OS Foundation LLC
+ *
+ * Author: Philip Withnall <pwithnall@endlessos.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#pragma once
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PLUGIN_DUMMY (gs_plugin_dummy_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsPluginDummy, gs_plugin_dummy, GS, PLUGIN_DUMMY, GsPlugin)
+
+G_END_DECLS
diff --git a/plugins/dummy/gs-self-test.c b/plugins/dummy/gs-self-test.c
new file mode 100644
index 0000000..c6ce9bd
--- /dev/null
+++ b/plugins/dummy/gs-self-test.c
@@ -0,0 +1,966 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ * vi:set noexpandtab tabstop=8 shiftwidth=8:
+ *
+ * Copyright (C) 2013-2017 Richard Hughes <richard@hughsie.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include "config.h"
+
+#include <glib/gstdio.h>
+#include <locale.h>
+
+#include "gnome-software-private.h"
+
+#include "gs-test.h"
+
+const gchar * const allowlist[] = {
+ "appstream",
+ "dummy",
+ "generic-updates",
+ "hardcoded-blocklist",
+ "icons",
+ "provenance",
+ "provenance-license",
+ NULL
+};
+
+static guint _status_changed_cnt = 0;
+
+typedef struct {
+ GError *error;
+ GMainLoop *loop;
+} GsDummyTestHelper;
+
+static GsDummyTestHelper *
+gs_dummy_test_helper_new (void)
+{
+ return g_new0 (GsDummyTestHelper, 1);
+}
+
+static void
+gs_dummy_test_helper_free (GsDummyTestHelper *helper)
+{
+ if (helper->error != NULL)
+ g_error_free (helper->error);
+ if (helper->loop != NULL)
+ g_main_loop_unref (helper->loop);
+ g_free (helper);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GsDummyTestHelper, gs_dummy_test_helper_free)
+
+static void
+gs_plugin_loader_status_changed_cb (GsPluginLoader *plugin_loader,
+ GsApp *app,
+ GsPluginStatus status,
+ gpointer user_data)
+{
+ _status_changed_cnt++;
+}
+
+static void
+gs_plugins_dummy_install_func (GsPluginLoader *plugin_loader)
+{
+ gboolean ret;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GError) error = NULL;
+ GsPlugin *plugin;
+
+ /* install */
+ app = gs_app_new ("chiron.desktop");
+ plugin = gs_plugin_loader_find_plugin (plugin_loader, "dummy");
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_INSTALL,
+ "app", app,
+ NULL);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_INSTALLED);
+
+ /* remove */
+ g_object_unref (plugin_job);
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REMOVE,
+ "app", app,
+ NULL);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_AVAILABLE);
+}
+
+static void
+gs_plugins_dummy_error_func (GsPluginLoader *plugin_loader)
+{
+ const GError *app_error;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GPtrArray) events = NULL;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GsPluginEvent) event = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ GsPlugin *plugin;
+
+ /* drop all caches */
+ gs_utils_rmtree (g_getenv ("GS_SELF_TEST_CACHEDIR"), NULL);
+ gs_test_reinitialise_plugin_loader (plugin_loader, allowlist, NULL);
+
+ /* update, which should cause an error to be emitted */
+ app = gs_app_new ("chiron.desktop");
+ plugin = gs_plugin_loader_find_plugin (plugin_loader, "dummy");
+ gs_app_set_management_plugin (app, plugin);
+ gs_app_set_state (app, GS_APP_STATE_AVAILABLE);
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE,
+ "app", app,
+ NULL);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ /* get last active event */
+ event = gs_plugin_loader_get_event_default (plugin_loader);
+ g_assert (event != NULL);
+ g_assert (gs_plugin_event_get_app (event) == app);
+
+ /* check all the events */
+ events = gs_plugin_loader_get_events (plugin_loader);
+ g_assert_cmpint (events->len, ==, 1);
+ event = g_ptr_array_index (events, 0);
+ g_assert (gs_plugin_event_get_app (event) == app);
+ app_error = gs_plugin_event_get_error (event);
+ g_assert (app_error != NULL);
+ g_assert_error (app_error,
+ GS_PLUGIN_ERROR,
+ GS_PLUGIN_ERROR_DOWNLOAD_FAILED);
+}
+
+static void
+gs_plugins_dummy_refine_func (GsPluginLoader *plugin_loader)
+{
+ gboolean ret;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ GsPlugin *plugin;
+
+ /* get the extra bits */
+ app = gs_app_new ("chiron.desktop");
+ plugin = gs_plugin_loader_find_plugin (plugin_loader, "dummy");
+ gs_app_set_management_plugin (app, plugin);
+ plugin_job = gs_plugin_job_refine_new_for_app (app,
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_URL);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpstr (gs_app_get_license (app), ==, "GPL-2.0+");
+ g_assert_cmpstr (gs_app_get_description (app), !=, NULL);
+ g_assert_cmpstr (gs_app_get_url (app, AS_URL_KIND_HOMEPAGE), ==, "http://www.test.org/");
+}
+
+static void
+gs_plugins_dummy_metadata_quirks (GsPluginLoader *plugin_loader)
+{
+ gboolean ret;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ GsPlugin *plugin;
+
+ /* get the extra bits */
+ app = gs_app_new ("chiron.desktop");
+ plugin = gs_plugin_loader_find_plugin (plugin_loader, "dummy");
+ gs_app_set_management_plugin (app, plugin);
+ plugin_job = gs_plugin_job_refine_new_for_app (app, GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpstr (gs_app_get_description (app), !=, NULL);
+
+ /* check the not-launchable quirk */
+
+ g_assert (!gs_app_has_quirk(app, GS_APP_QUIRK_NOT_LAUNCHABLE));
+
+ gs_app_set_metadata (app, "GnomeSoftware::quirks::not-launchable", "true");
+
+ g_object_unref (plugin_job);
+ plugin_job = gs_plugin_job_refine_new_for_app (app, GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert (gs_app_has_quirk(app, GS_APP_QUIRK_NOT_LAUNCHABLE));
+
+ gs_app_set_metadata (app, "GnomeSoftware::quirks::not-launchable", NULL);
+ gs_app_set_metadata (app, "GnomeSoftware::quirks::not-launchable", "false");
+
+ g_object_unref (plugin_job);
+ plugin_job = gs_plugin_job_refine_new_for_app (app, GS_PLUGIN_REFINE_FLAGS_REQUIRE_DESCRIPTION);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert (!gs_app_has_quirk(app, GS_APP_QUIRK_NOT_LAUNCHABLE));
+}
+
+static void
+gs_plugins_dummy_key_colors_func (GsPluginLoader *plugin_loader)
+{
+ GArray *array;
+ gboolean ret;
+ guint i;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* get the extra bits */
+ app = gs_app_new ("chiron.desktop");
+ plugin_job = gs_plugin_job_refine_new_for_app (app, GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+ array = gs_app_get_key_colors (app);
+ g_assert_cmpint (array->len, <=, 3);
+ g_assert_cmpint (array->len, >, 0);
+
+ /* check values are in range */
+ for (i = 0; i < array->len; i++) {
+ const GdkRGBA *kc = &g_array_index (array, GdkRGBA, i);
+ g_assert_cmpfloat (kc->red, >=, 0.f);
+ g_assert_cmpfloat (kc->red, <=, 1.f);
+ g_assert_cmpfloat (kc->green, >=, 0.f);
+ g_assert_cmpfloat (kc->green, <=, 1.f);
+ g_assert_cmpfloat (kc->blue, >=, 0.f);
+ g_assert_cmpfloat (kc->blue, <=, 1.f);
+ g_assert_cmpfloat (kc->alpha, >=, 0.f);
+ g_assert_cmpfloat (kc->alpha, <=, 1.f);
+ }
+}
+
+static void
+gs_plugins_dummy_updates_func (GsPluginLoader *plugin_loader)
+{
+ GsApp *app;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+
+ /* get the updates list */
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_GET_UPDATES,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS,
+ NULL);
+ list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list != NULL);
+
+ /* make sure there are three entries */
+ g_assert_cmpint (gs_app_list_length (list), ==, 3);
+ app = gs_app_list_index (list, 0);
+ g_assert_cmpstr (gs_app_get_id (app), ==, "chiron.desktop");
+ g_assert_cmpint (gs_app_get_kind (app), ==, AS_COMPONENT_KIND_DESKTOP_APP);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_UPDATABLE_LIVE);
+ g_assert_cmpstr (gs_app_get_update_details_markup (app), ==, "Do not crash when using libvirt.");
+ g_assert_cmpint (gs_app_get_update_urgency (app), ==, AS_URGENCY_KIND_HIGH);
+
+ /* get the virtual non-apps OS update */
+ app = gs_app_list_index (list, 2);
+ g_assert_cmpstr (gs_app_get_id (app), ==, "org.gnome.Software.OsUpdate");
+ g_assert_cmpstr (gs_app_get_name (app), ==, "System Updates");
+ g_assert_cmpstr (gs_app_get_summary (app), ==, "General system updates, such as security or bug fixes, and performance improvements.");
+ g_assert_cmpint (gs_app_get_kind (app), ==, AS_COMPONENT_KIND_GENERIC);
+ g_assert_cmpint (gs_app_get_special_kind (app), ==, GS_APP_SPECIAL_KIND_OS_UPDATE);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_UPDATABLE);
+ g_assert_cmpint (gs_app_list_length (gs_app_get_related (app)), ==, 2);
+
+ /* get the virtual non-apps OS update */
+ app = gs_app_list_index (list, 1);
+ g_assert_cmpstr (gs_app_get_id (app), ==, "proxy.desktop");
+ g_assert (gs_app_has_quirk (app, GS_APP_QUIRK_IS_PROXY));
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_UPDATABLE_LIVE);
+ g_assert_cmpint (gs_app_list_length (gs_app_get_related (app)), ==, 2);
+}
+
+static void
+gs_plugins_dummy_distro_upgrades_func (GsPluginLoader *plugin_loader)
+{
+ GsApp *app;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+
+ /* get the updates list */
+ plugin_job = gs_plugin_job_list_distro_upgrades_new (GS_PLUGIN_LIST_DISTRO_UPGRADES_FLAGS_NONE,
+ GS_PLUGIN_REFINE_FLAGS_NONE);
+ list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list != NULL);
+
+ /* make sure there is one entry */
+ g_assert_cmpint (gs_app_list_length (list), ==, 1);
+ app = gs_app_list_index (list, 0);
+ g_assert_cmpstr (gs_app_get_id (app), ==, "org.fedoraproject.release-rawhide.upgrade");
+ g_assert_cmpint (gs_app_get_kind (app), ==, AS_COMPONENT_KIND_OPERATING_SYSTEM);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_AVAILABLE);
+
+ /* this should be set with a higher priority by AppStream */
+ g_assert_cmpstr (gs_app_get_summary (app), ==, "Release specific tagline");
+
+ /* download the update */
+ g_object_unref (plugin_job);
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPGRADE_DOWNLOAD,
+ "app", app,
+ NULL);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_UPDATABLE);
+
+ /* trigger the update */
+ g_object_unref (plugin_job);
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPGRADE_TRIGGER,
+ "app", app,
+ NULL);
+ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_UPDATABLE);
+}
+
+static gboolean
+filter_valid_cb (GsApp *app,
+ gpointer user_data)
+{
+ return gs_plugin_loader_app_is_valid (app, GS_PLUGIN_REFINE_FLAGS_NONE);
+}
+
+static void
+gs_plugins_dummy_installed_func (GsPluginLoader *plugin_loader)
+{
+ GsApp *app;
+ GsApp *addon;
+ g_autoptr(GsAppList) addons = NULL;
+ g_autofree gchar *menu_path = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list = NULL;
+ g_autoptr(GsAppQuery) query = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GIcon) icon = NULL;
+ GsPluginRefineFlags refine_flags;
+
+ /* get installed packages */
+ refine_flags = (GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_ADDONS |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_KUDOS |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_CATEGORIES |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_PROVENANCE);
+
+ query = gs_app_query_new ("is-installed", GS_APP_QUERY_TRISTATE_TRUE,
+ "refine-flags", refine_flags,
+ "dedupe-flags", GS_PLUGIN_JOB_DEDUPE_FLAGS_DEFAULT,
+ "filter-func", filter_valid_cb,
+ NULL);
+ plugin_job = gs_plugin_job_list_apps_new (query, GS_PLUGIN_LIST_APPS_FLAGS_NONE);
+ list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list != NULL);
+
+ /* make sure there is one entry */
+ g_assert_cmpint (gs_app_list_length (list), ==, 1);
+ app = gs_app_list_index (list, 0);
+ g_assert_cmpstr (gs_app_get_id (app), ==, "zeus.desktop");
+ g_assert_cmpint (gs_app_get_kind (app), ==, AS_COMPONENT_KIND_DESKTOP_APP);
+ g_assert_cmpint (gs_app_get_state (app), ==, GS_APP_STATE_INSTALLED);
+ g_assert_cmpstr (gs_app_get_name (app), ==, "Zeus");
+ g_assert_cmpstr (gs_app_get_source_default (app), ==, "zeus");
+ icon = gs_app_get_icon_for_size (app, 48, 1, NULL);
+ g_assert_nonnull (icon);
+ g_clear_object (&icon);
+
+ /* check various bitfields */
+ g_assert (gs_app_has_quirk (app, GS_APP_QUIRK_PROVENANCE));
+ g_assert_cmpstr (gs_app_get_license (app), ==, "GPL-2.0+");
+ g_assert (gs_app_get_license_is_free (app));
+
+ /* check kudos */
+ g_assert_true (gs_app_has_kudo (app, GS_APP_KUDO_MY_LANGUAGE));
+
+ /* check categories */
+ g_assert (gs_app_has_category (app, "Player"));
+ g_assert (gs_app_has_category (app, "AudioVideo"));
+ g_assert (!gs_app_has_category (app, "ImageProcessing"));
+ g_assert (gs_app_get_menu_path (app) != NULL);
+ menu_path = g_strjoinv ("->", gs_app_get_menu_path (app));
+ g_assert_cmpstr (menu_path, ==, "Create->Music Players");
+
+ /* check addon */
+ addons = gs_app_dup_addons (app);
+ g_assert_nonnull (addons);
+ g_assert_cmpint (gs_app_list_length (addons), ==, 1);
+ addon = gs_app_list_index (addons, 0);
+ g_assert_cmpstr (gs_app_get_id (addon), ==, "zeus-spell.addon");
+ g_assert_cmpint (gs_app_get_kind (addon), ==, AS_COMPONENT_KIND_ADDON);
+ g_assert_cmpint (gs_app_get_state (addon), ==, GS_APP_STATE_AVAILABLE);
+ g_assert_cmpstr (gs_app_get_name (addon), ==, "Spell Check");
+ g_assert_cmpstr (gs_app_get_source_default (addon), ==, "zeus-spell");
+ g_assert_cmpstr (gs_app_get_license (addon), ==,
+ "LicenseRef-free=https://www.debian.org/");
+ icon = gs_app_get_icon_for_size (addon, 48, 1, NULL);
+ g_assert_null (icon);
+}
+
+static void
+gs_plugins_dummy_search_func (GsPluginLoader *plugin_loader)
+{
+ GsApp *app;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GsAppQuery) query = NULL;
+ const gchar *keywords[2] = { NULL, };
+
+ /* get search result based on addon keyword */
+ keywords[0] = "zeus";
+ query = gs_app_query_new ("keywords", keywords,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+ "dedupe-flags", GS_PLUGIN_JOB_DEDUPE_FLAGS_DEFAULT,
+ "sort-func", gs_utils_app_sort_match_value,
+ NULL);
+ plugin_job = gs_plugin_job_list_apps_new (query, GS_PLUGIN_LIST_APPS_FLAGS_NONE);
+ list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list != NULL);
+
+ /* make sure there is at least one entry, the parent app, which must be first */
+ g_assert_cmpint (gs_app_list_length (list), >=, 1);
+ app = gs_app_list_index (list, 0);
+ g_assert_cmpstr (gs_app_get_id (app), ==, "zeus.desktop");
+ g_assert_cmpint (gs_app_get_kind (app), ==, AS_COMPONENT_KIND_DESKTOP_APP);
+}
+
+static void
+gs_plugins_dummy_search_alternate_func (GsPluginLoader *plugin_loader)
+{
+ GsApp *app_tmp;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list = NULL;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GsAppQuery) query = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+
+ /* get search result based on addon keyword */
+ app = gs_app_new ("zeus.desktop");
+ query = gs_app_query_new ("alternate-of", app,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+ "dedupe-flags", GS_PLUGIN_JOB_DEDUPE_FLAGS_DEFAULT,
+ "sort-func", gs_utils_app_sort_priority,
+ NULL);
+ plugin_job = gs_plugin_job_list_apps_new (query, GS_PLUGIN_LIST_APPS_FLAGS_NONE);
+ list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list != NULL);
+
+ /* make sure there is the original app, and the alternate */
+ g_assert_cmpint (gs_app_list_length (list), ==, 2);
+ app_tmp = gs_app_list_index (list, 0);
+ g_assert_cmpstr (gs_app_get_id (app_tmp), ==, "chiron.desktop");
+ g_assert_cmpint (gs_app_get_kind (app_tmp), ==, AS_COMPONENT_KIND_DESKTOP_APP);
+ app_tmp = gs_app_list_index (list, 1);
+ g_assert_cmpstr (gs_app_get_id (app_tmp), ==, "zeus.desktop");
+ g_assert_cmpint (gs_app_get_kind (app_tmp), ==, AS_COMPONENT_KIND_DESKTOP_APP);
+}
+
+static void
+gs_plugins_dummy_url_to_app_func (GsPluginLoader *plugin_loader)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+
+ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_URL_TO_APP,
+ "search", "dummy://chiron.desktop",
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+ NULL);
+ app = gs_plugin_loader_job_process_app (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (app != NULL);
+ g_assert_cmpstr (gs_app_get_id (app), ==, "chiron.desktop");
+ g_assert_cmpint (gs_app_get_kind (app), ==, AS_COMPONENT_KIND_DESKTOP_APP);
+}
+
+static void
+gs_plugins_dummy_plugin_cache_func (GsPluginLoader *plugin_loader)
+{
+ GsApp *app1;
+ GsApp *app2;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list1 = NULL;
+ g_autoptr(GsAppList) list2 = NULL;
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+
+ /* ensure we get the same results back from calling the methods twice */
+ plugin_job = gs_plugin_job_list_distro_upgrades_new (GS_PLUGIN_LIST_DISTRO_UPGRADES_FLAGS_NONE,
+ GS_PLUGIN_REFINE_FLAGS_NONE);
+ list1 = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list1 != NULL);
+ g_assert_cmpint (gs_app_list_length (list1), ==, 1);
+ app1 = gs_app_list_index (list1, 0);
+
+ g_object_unref (plugin_job);
+ plugin_job = gs_plugin_job_list_distro_upgrades_new (GS_PLUGIN_LIST_DISTRO_UPGRADES_FLAGS_NONE,
+ GS_PLUGIN_REFINE_FLAGS_NONE);
+ list2 = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list2 != NULL);
+ g_assert_cmpint (gs_app_list_length (list2), ==, 1);
+ app2 = gs_app_list_index (list2, 0);
+
+ /* make sure there is one GObject */
+ g_assert_cmpstr (gs_app_get_id (app1), ==, gs_app_get_id (app2));
+ g_assert (app1 == app2);
+}
+
+static void
+gs_plugins_dummy_wildcard_func (GsPluginLoader *plugin_loader)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list1 = NULL;
+ g_autoptr(GsAppList) list2 = NULL;
+ const gchar *expected_apps2[] = { "chiron.desktop", "zeus.desktop", NULL };
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GsAppQuery) query = NULL;
+
+ /* use the plugin's default curated list, indicated by setting max-results=5 */
+ query = gs_app_query_new ("is-curated", GS_APP_QUERY_TRISTATE_TRUE,
+ "max-results", 5,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+ NULL);
+ plugin_job = gs_plugin_job_list_apps_new (query, GS_PLUGIN_LIST_APPS_FLAGS_NONE);
+
+ list1 = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list1 != NULL);
+ g_assert_cmpint (gs_app_list_length (list1), ==, 1);
+ g_object_unref (plugin_job);
+ g_object_unref (query);
+
+ /* use the plugin’s second list, indicated by setting max-results=6 */
+ query = gs_app_query_new ("is-curated", GS_APP_QUERY_TRISTATE_TRUE,
+ "max-results", 6,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+ NULL);
+ plugin_job = gs_plugin_job_list_apps_new (query, GS_PLUGIN_LIST_APPS_FLAGS_NONE);
+
+ list2 = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (error);
+ g_assert (list2 != NULL);
+
+ g_assert_cmpint (gs_app_list_length (list2), ==, g_strv_length ((gchar **) expected_apps2));
+
+ for (guint i = 0; i < gs_app_list_length (list2); ++i) {
+ GsApp *app = gs_app_list_index (list2, i);
+ g_assert (g_strv_contains (expected_apps2, gs_app_get_id (app)));
+ }
+}
+
+static void
+plugin_job_action_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+ GsDummyTestHelper *helper = (GsDummyTestHelper *) user_data;
+
+ gs_plugin_loader_job_action_finish (plugin_loader, res, &helper->error);
+ if (helper->loop != NULL)
+ g_main_loop_quit (helper->loop);
+}
+
+static void
+gs_plugins_dummy_limit_parallel_ops_func (GsPluginLoader *plugin_loader)
+{
+ g_autoptr(GsAppList) list = NULL;
+ GsApp *app1 = NULL;
+ g_autoptr(GsApp) app2 = NULL;
+ g_autoptr(GsApp) app3 = NULL;
+ GsPlugin *plugin;
+ g_autoptr(GsPluginJob) plugin_job1 = NULL;
+ g_autoptr(GsPluginJob) plugin_job2 = NULL;
+ g_autoptr(GsPluginJob) plugin_job3 = NULL;
+ g_autoptr(GMainContext) context = NULL;
+ g_autoptr(GsDummyTestHelper) helper1 = gs_dummy_test_helper_new ();
+ g_autoptr(GsDummyTestHelper) helper2 = gs_dummy_test_helper_new ();
+ g_autoptr(GsDummyTestHelper) helper3 = gs_dummy_test_helper_new ();
+
+ /* drop all caches */
+ gs_utils_rmtree (g_getenv ("GS_SELF_TEST_CACHEDIR"), NULL);
+ gs_test_reinitialise_plugin_loader (plugin_loader, allowlist, NULL);
+
+ /* get the updates list */
+ plugin_job1 = gs_plugin_job_list_distro_upgrades_new (GS_PLUGIN_LIST_DISTRO_UPGRADES_FLAGS_NONE,
+ GS_PLUGIN_REFINE_FLAGS_NONE);
+ list = gs_plugin_loader_job_process (plugin_loader, plugin_job1, NULL, &helper3->error);
+ gs_test_flush_main_context ();
+ g_assert_no_error (helper3->error);
+ g_assert (list != NULL);
+ g_assert_cmpint (gs_app_list_length (list), ==, 1);
+ app1 = gs_app_list_index (list, 0);
+ g_assert_cmpstr (gs_app_get_id (app1), ==, "org.fedoraproject.release-rawhide.upgrade");
+ g_assert_cmpint (gs_app_get_kind (app1), ==, AS_COMPONENT_KIND_OPERATING_SYSTEM);
+ g_assert_cmpint (gs_app_get_state (app1), ==, GS_APP_STATE_AVAILABLE);
+
+ /* allow only one operation at a time */
+ gs_plugin_loader_set_max_parallel_ops (plugin_loader, 1);
+
+ app2 = gs_app_new ("chiron.desktop");
+ plugin = gs_plugin_loader_find_plugin (plugin_loader, "dummy");
+ gs_app_set_management_plugin (app2, plugin);
+ gs_app_set_state (app2, GS_APP_STATE_AVAILABLE);
+
+ /* use "proxy" prefix so the update function succeeds... */
+ app3 = gs_app_new ("proxy-zeus.desktop");
+ gs_app_set_management_plugin (app3, plugin);
+ gs_app_set_state (app3, GS_APP_STATE_UPDATABLE_LIVE);
+
+ context = g_main_context_new ();
+ helper3->loop = g_main_loop_new (context, FALSE);
+ g_main_context_push_thread_default (context);
+
+ /* call a few operations at the "same time" */
+
+ /* download an upgrade */
+ g_object_unref (plugin_job1);
+ plugin_job1 = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPGRADE_DOWNLOAD,
+ "app", app1,
+ NULL);
+ gs_plugin_loader_job_process_async (plugin_loader,
+ plugin_job1,
+ NULL,
+ plugin_job_action_cb,
+ helper1);
+
+ /* install an app */
+ plugin_job2 = gs_plugin_job_newv (GS_PLUGIN_ACTION_INSTALL,
+ "app", app2,
+ NULL);
+ gs_plugin_loader_job_process_async (plugin_loader,
+ plugin_job2,
+ NULL,
+ plugin_job_action_cb,
+ helper2);
+
+ /* update an app */
+ plugin_job3 = gs_plugin_job_newv (GS_PLUGIN_ACTION_UPDATE,
+ "app", app3,
+ NULL);
+ gs_plugin_loader_job_process_async (plugin_loader,
+ plugin_job3,
+ NULL,
+ plugin_job_action_cb,
+ helper3);
+
+ /* since we have only 1 parallel installation op possible,
+ * verify the last operations are pending */
+ g_assert_cmpint (gs_app_get_state (app2), ==, GS_APP_STATE_QUEUED_FOR_INSTALL);
+ g_assert_cmpint (gs_app_get_pending_action (app2), ==, GS_PLUGIN_ACTION_INSTALL);
+ g_assert_cmpint (gs_app_get_state (app3), ==, GS_APP_STATE_UPDATABLE_LIVE);
+ g_assert_cmpint (gs_app_get_pending_action (app3), ==, GS_PLUGIN_ACTION_UPDATE);
+
+ /* wait for the 2nd installation to finish, it means the 1st should have been
+ * finished too */
+ g_main_loop_run (helper3->loop);
+ g_main_context_pop_thread_default (context);
+
+ gs_test_flush_main_context ();
+ g_assert_no_error (helper1->error);
+ g_assert_no_error (helper2->error);
+ g_assert_no_error (helper3->error);
+
+ g_assert_cmpint (gs_app_get_state (app1), ==, GS_APP_STATE_UPDATABLE);
+ g_assert_cmpint (gs_app_get_state (app2), ==, GS_APP_STATE_INSTALLED);
+ g_assert_cmpint (gs_app_get_state (app3), ==, GS_APP_STATE_INSTALLED);
+
+ /* set the default max parallel ops */
+ gs_plugin_loader_set_max_parallel_ops (plugin_loader, 0);
+}
+
+static void
+gs_plugins_dummy_app_size_calc_func (GsPluginLoader *loader)
+{
+ g_autoptr(GsApp) app1 = NULL;
+ g_autoptr(GsApp) app2 = NULL;
+ g_autoptr(GsApp) runtime = NULL;
+ guint64 value = 0;
+
+ app1 = gs_app_new ("app1");
+ gs_app_set_state (app1, GS_APP_STATE_AVAILABLE);
+ gs_app_set_size_download (app1, GS_SIZE_TYPE_VALID, 1);
+ gs_app_set_size_installed (app1, GS_SIZE_TYPE_VALID, 1000);
+ g_assert_cmpint (gs_app_get_size_download (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 1);
+ g_assert_cmpint (gs_app_get_size_download_dependencies (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+ g_assert_cmpint (gs_app_get_size_installed (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 1000);
+ g_assert_cmpint (gs_app_get_size_installed_dependencies (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+
+ app2 = gs_app_new ("app2");
+ gs_app_set_state (app2, GS_APP_STATE_AVAILABLE);
+ gs_app_set_size_download (app2, GS_SIZE_TYPE_VALID, 20);
+ gs_app_set_size_installed (app2, GS_SIZE_TYPE_VALID, 20000);
+ g_assert_cmpint (gs_app_get_size_download (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 20);
+ g_assert_cmpint (gs_app_get_size_download_dependencies (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+ g_assert_cmpint (gs_app_get_size_installed (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 20000);
+ g_assert_cmpint (gs_app_get_size_installed_dependencies (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+
+ runtime = gs_app_new ("runtime");
+ gs_app_set_state (runtime, GS_APP_STATE_AVAILABLE);
+ gs_app_set_size_download (runtime, GS_SIZE_TYPE_VALID, 300);
+ gs_app_set_size_installed (runtime, GS_SIZE_TYPE_VALID, 300000);
+ g_assert_cmpint (gs_app_get_size_download (runtime, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 300);
+ g_assert_cmpint (gs_app_get_size_download_dependencies (runtime, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+ g_assert_cmpint (gs_app_get_size_installed (runtime, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 300000);
+ g_assert_cmpint (gs_app_get_size_installed_dependencies (runtime, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+
+ gs_app_set_runtime (app1, runtime);
+ g_assert_cmpint (gs_app_get_size_download (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 1);
+ g_assert_cmpint (gs_app_get_size_download_dependencies (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 300);
+ g_assert_cmpint (gs_app_get_size_installed (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 1000);
+ g_assert_cmpint (gs_app_get_size_installed_dependencies (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+
+ gs_app_set_runtime (app2, runtime);
+ g_assert_cmpint (gs_app_get_size_download (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 20);
+ g_assert_cmpint (gs_app_get_size_download_dependencies (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 300);
+ g_assert_cmpint (gs_app_get_size_installed (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 20000);
+ g_assert_cmpint (gs_app_get_size_installed_dependencies (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+
+ gs_app_add_related (app1, app2);
+ g_assert_cmpint (gs_app_get_size_download (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 1);
+ g_assert_cmpint (gs_app_get_size_download_dependencies (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 320);
+ g_assert_cmpint (gs_app_get_size_installed (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 1000);
+ g_assert_cmpint (gs_app_get_size_installed_dependencies (app1, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 20000);
+
+ g_assert_cmpint (gs_app_get_size_download (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 20);
+ g_assert_cmpint (gs_app_get_size_download_dependencies (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 300);
+ g_assert_cmpint (gs_app_get_size_installed (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 20000);
+ g_assert_cmpint (gs_app_get_size_installed_dependencies (app2, &value), ==, GS_SIZE_TYPE_VALID);
+ g_assert_cmpint (value, ==, 0);
+}
+
+int
+main (int argc, char **argv)
+{
+ g_autofree gchar *tmp_root = NULL;
+ gboolean ret;
+ int retval;
+ g_autofree gchar *xml = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsPluginLoader) plugin_loader = NULL;
+
+ /* While we use %G_TEST_OPTION_ISOLATE_DIRS to create temporary directories
+ * for each of the tests, we want to use the system MIME registry, assuming
+ * that it exists and correctly has shared-mime-info installed. */
+ g_content_type_set_mime_dirs (NULL);
+
+ /* Force the GTK resources to be registered, needed for fallback icons. */
+ gtk_init_check ();
+
+ /* Similarly, add the system-wide icon theme path before it’s
+ * overwritten by %G_TEST_OPTION_ISOLATE_DIRS. */
+ gs_test_expose_icon_theme_paths ();
+
+ gs_test_init (&argc, &argv);
+ g_setenv ("GS_XMLB_VERBOSE", "1", TRUE);
+
+ /* set all the things required as a dummy test harness */
+ setlocale (LC_MESSAGES, "en_GB.UTF-8");
+ g_setenv ("GS_SELF_TEST_DUMMY_ENABLE", "1", TRUE);
+ g_setenv ("GS_SELF_TEST_PROVENANCE_SOURCES", "london*,boston", TRUE);
+ g_setenv ("GS_SELF_TEST_PROVENANCE_LICENSE_SOURCES", "london*,boston", TRUE);
+ g_setenv ("GS_SELF_TEST_PROVENANCE_LICENSE_URL", "https://www.debian.org/", TRUE);
+
+ /* Use a common cache directory for all tests, since the appstream
+ * plugin uses it and cannot be reinitialised for each test. */
+ tmp_root = g_dir_make_tmp ("gnome-software-dummy-test-XXXXXX", NULL);
+ g_assert (tmp_root != NULL);
+ g_setenv ("GS_SELF_TEST_CACHEDIR", tmp_root, TRUE);
+
+ xml = g_strdup ("<?xml version=\"1.0\"?>\n"
+ "<components version=\"0.9\">\n"
+ " <component type=\"desktop\">\n"
+ " <id>chiron.desktop</id>\n"
+ " <name>Chiron</name>\n"
+ " <pkgname>chiron</pkgname>\n"
+ " </component>\n"
+ " <component type=\"desktop\">\n"
+ " <id>zeus.desktop</id>\n"
+ " <name>Zeus</name>\n"
+ " <summary>A teaching application</summary>\n"
+ " <pkgname>zeus</pkgname>\n"
+ " <icon type=\"stock\">drive-harddisk</icon>\n"
+ " <categories>\n"
+ " <category>AudioVideo</category>\n"
+ " <category>Player</category>\n"
+ " </categories>\n"
+ " <languages>\n"
+ " <lang percentage=\"100\">en_GB</lang>\n"
+ " </languages>\n"
+ " </component>\n"
+ " <component type=\"desktop\">\n"
+ " <id>mate-spell.desktop</id>\n"
+ " <name>Spell</name>\n"
+ " <summary>A spelling application for MATE</summary>\n"
+ " <pkgname>mate-spell</pkgname>\n"
+ " <icon type=\"stock\">drive-harddisk</icon>\n"
+ " <project_group>MATE</project_group>\n"
+ " </component>\n"
+ " <component type=\"addon\">\n"
+ " <id>zeus-spell.addon</id>\n"
+ " <extends>zeus.desktop</extends>\n"
+ " <name>Spell Check</name>\n"
+ " <summary>Check the spelling when teaching</summary>\n"
+ " <pkgname>zeus-spell</pkgname>\n"
+ " </component>\n"
+ " <component type=\"desktop\">\n"
+ " <id>Uninstall Zeus.desktop</id>\n"
+ " <name>Uninstall Zeus</name>\n"
+ " <summary>Uninstall the teaching application</summary>\n"
+ " <icon type=\"stock\">drive-harddisk</icon>\n"
+ " </component>\n"
+ " <component type=\"os-upgrade\">\n"
+ " <id>org.fedoraproject.release-rawhide.upgrade</id>\n"
+ " <name>Fedora Rawhide</name>\n"
+ " <summary>Release specific tagline</summary>\n"
+ " <pkgname>fedora-release</pkgname>\n"
+ " </component>\n"
+ " <info>\n"
+ " <scope>user</scope>\n"
+ " </info>\n"
+ "</components>\n");
+ g_setenv ("GS_SELF_TEST_APPSTREAM_XML", xml, TRUE);
+
+ /* we can only load this once per process */
+ plugin_loader = gs_plugin_loader_new (NULL, NULL);
+ g_signal_connect (plugin_loader, "status-changed",
+ G_CALLBACK (gs_plugin_loader_status_changed_cb), NULL);
+ gs_plugin_loader_add_location (plugin_loader, LOCALPLUGINDIR);
+ gs_plugin_loader_add_location (plugin_loader, LOCALPLUGINDIR_CORE);
+ ret = gs_plugin_loader_setup (plugin_loader,
+ allowlist,
+ NULL,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert (!gs_plugin_loader_get_enabled (plugin_loader, "notgoingtoexist"));
+ g_assert (gs_plugin_loader_get_enabled (plugin_loader, "appstream"));
+ g_assert (gs_plugin_loader_get_enabled (plugin_loader, "dummy"));
+
+ /* plugin tests go here */
+ g_test_add_data_func ("/gnome-software/plugins/dummy/wildcard",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_wildcard_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/plugin-cache",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_plugin_cache_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/key-colors",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_key_colors_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/search",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_search_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/search-alternate",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_search_alternate_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/url-to-app",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_url_to_app_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/install",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_install_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/error",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_error_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/installed",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_installed_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/refine",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_refine_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/updates",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_updates_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/distro-upgrades",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_distro_upgrades_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/metadata-quirks",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_metadata_quirks);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/limit-parallel-ops",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_limit_parallel_ops_func);
+ g_test_add_data_func ("/gnome-software/plugins/dummy/app-size-calc",
+ plugin_loader,
+ (GTestDataFunc) gs_plugins_dummy_app_size_calc_func);
+ retval = g_test_run ();
+
+ /* Clean up. */
+ gs_utils_rmtree (tmp_root, NULL);
+
+ return retval;
+}
diff --git a/plugins/dummy/meson.build b/plugins/dummy/meson.build
new file mode 100644
index 0000000..5b098db
--- /dev/null
+++ b/plugins/dummy/meson.build
@@ -0,0 +1,35 @@
+cargs = ['-DG_LOG_DOMAIN="GsPluginDummy"']
+cargs += ['-DLOCALPLUGINDIR="' + meson.current_build_dir() + '"']
+cargs += ['-DLOCALPLUGINDIR_CORE="' + meson.current_build_dir() + '/../core"']
+
+shared_module(
+ 'gs_plugin_dummy',
+ sources : 'gs-plugin-dummy.c',
+ include_directories : [
+ include_directories('../..'),
+ include_directories('../../lib'),
+ ],
+ install : true,
+ install_dir: plugin_dir,
+ c_args : cargs,
+ dependencies : [plugin_libs],
+)
+
+if get_option('tests')
+ e = executable(
+ 'gs-self-test-dummy',
+ compiled_schemas,
+ sources : [
+ 'gs-self-test.c'
+ ],
+ include_directories : [
+ include_directories('../..'),
+ include_directories('../../lib'),
+ ],
+ dependencies : [
+ plugin_libs,
+ ],
+ c_args : cargs,
+ )
+ test('gs-self-test-dummy', e, suite: ['plugins', 'dummy'], env: test_env)
+endif