[gnome-software] Add several tests for installing, removing and updating flatpaks



commit 715e46aba21cfcdfacd0ee44cddba02a8264f677
Author: Richard Hughes <richard hughsie com>
Date:   Sun Feb 19 09:59:17 2017 +0000

    Add several tests for installing, removing and updating flatpaks
    
    Also, fix several of the bugs found.

 .../flatpak/app-missing-runtime/org.test.Chiron    |    1 +
 .../flatpak/app-update/org.test.Chiron/.gitignore  |    1 +
 .../app-update/org.test.Chiron/files/bin/chiron.sh |    2 +
 .../icons/flatpak/128x128/org.test.Chiron.png      |  Bin 0 -> 373 bytes
 .../icons/flatpak/64x64/org.test.Chiron.png        |  Bin 0 -> 168 bytes
 .../share/app-info/xmls/org.test.Chiron.xml.gz     |  Bin 0 -> 434 bytes
 .../share/appdata/org.test.Chiron.appdata.xml      |    0
 .../share/applications/org.test.Chiron.desktop     |    1 +
 .../icons/hicolor/128x128/apps/org.test.Chiron.png |    1 +
 .../org.test.Chiron}/metadata                      |    0
 .../app-with-runtime/org.test.Chiron/.gitignore    |    2 +
 .../org.test.Chiron}/files/bin/chiron.sh           |    0
 .../share/appdata/org.test.Chiron.appdata.xml      |    3 -
 .../share/applications/org.test.Chiron.desktop     |    0
 .../icons/hicolor/128x128/apps/org.test.Chiron.png |  Bin 334 -> 334 bytes
 .../org.test.Chiron}/metadata                      |    0
 .../org.test.Runtime/files/.empty                  |    0
 .../org.test.Runtime/files/.gitignore              |    0
 .../share/metainfo/org.test.Runtime.metainfo.xml   |    0
 .../org.test.Runtime/metadata                      |    0
 .../org.test.Runtime/usr/share/libtest/README      |    0
 data/tests/flatpak/build-flatpak.sh                |   13 -
 data/tests/flatpak/build.py                        |   69 ++++
 src/gs-plugin-loader.c                             |   29 ++
 src/gs-plugin-loader.h                             |    3 +
 src/gs-plugin.c                                    |    2 +
 src/gs-self-test.c                                 |  356 +++++++++++++++++++-
 src/plugins/gs-appstream.c                         |    4 +
 src/plugins/gs-flatpak.c                           |   49 +++-
 src/plugins/gs-plugin-appstream.c                  |   25 ++-
 src/plugins/gs-plugin-flatpak.c                    |    3 +
 31 files changed, 532 insertions(+), 32 deletions(-)
---
diff --git a/data/tests/flatpak/app-missing-runtime/org.test.Chiron 
b/data/tests/flatpak/app-missing-runtime/org.test.Chiron
new file mode 120000
index 0000000..d9384e4
--- /dev/null
+++ b/data/tests/flatpak/app-missing-runtime/org.test.Chiron
@@ -0,0 +1 @@
+../app-with-runtime/org.test.Chiron/
\ No newline at end of file
diff --git a/data/tests/flatpak/app-update/org.test.Chiron/.gitignore 
b/data/tests/flatpak/app-update/org.test.Chiron/.gitignore
new file mode 120000
index 0000000..7f06bb9
--- /dev/null
+++ b/data/tests/flatpak/app-update/org.test.Chiron/.gitignore
@@ -0,0 +1 @@
+../app-with-runtime/org.test.Chiron/.gitignore
\ No newline at end of file
diff --git a/data/tests/flatpak/app-update/org.test.Chiron/files/bin/chiron.sh 
b/data/tests/flatpak/app-update/org.test.Chiron/files/bin/chiron.sh
new file mode 100644
index 0000000..dfed21c
--- /dev/null
+++ b/data/tests/flatpak/app-update/org.test.Chiron/files/bin/chiron.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo "Hello world, with upgrades"
diff --git 
a/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/icons/flatpak/128x128/org.test.Chiron.png
 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/icons/flatpak/128x128/org.test.Chiron.png
new file mode 100644
index 0000000..5a244e6
Binary files /dev/null and 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/icons/flatpak/128x128/org.test.Chiron.png
 differ
diff --git 
a/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/icons/flatpak/64x64/org.test.Chiron.png 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/icons/flatpak/64x64/org.test.Chiron.png
new file mode 100644
index 0000000..ed8d273
Binary files /dev/null and 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/icons/flatpak/64x64/org.test.Chiron.png 
differ
diff --git a/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/xmls/org.test.Chiron.xml.gz 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/xmls/org.test.Chiron.xml.gz
new file mode 100644
index 0000000..e40f14b
Binary files /dev/null and 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/app-info/xmls/org.test.Chiron.xml.gz differ
diff --git a/data/tests/flatpak/chiron/files/share/appdata/org.test.Chiron.appdata.xml 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/appdata/org.test.Chiron.appdata.xml
similarity index 100%
copy from data/tests/flatpak/chiron/files/share/appdata/org.test.Chiron.appdata.xml
copy to data/tests/flatpak/app-update/org.test.Chiron/files/share/appdata/org.test.Chiron.appdata.xml
diff --git a/data/tests/flatpak/app-update/org.test.Chiron/files/share/applications/org.test.Chiron.desktop 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/applications/org.test.Chiron.desktop
new file mode 120000
index 0000000..2b06818
--- /dev/null
+++ b/data/tests/flatpak/app-update/org.test.Chiron/files/share/applications/org.test.Chiron.desktop
@@ -0,0 +1 @@
+../../../../../app-missing-runtime/org.test.Chiron/files/share/applications/org.test.Chiron.desktop
\ No newline at end of file
diff --git 
a/data/tests/flatpak/app-update/org.test.Chiron/files/share/icons/hicolor/128x128/apps/org.test.Chiron.png 
b/data/tests/flatpak/app-update/org.test.Chiron/files/share/icons/hicolor/128x128/apps/org.test.Chiron.png
new file mode 120000
index 0000000..9c37986
--- /dev/null
+++ b/data/tests/flatpak/app-update/org.test.Chiron/files/share/icons/hicolor/128x128/apps/org.test.Chiron.png
@@ -0,0 +1 @@
+../../../../../../../../app-missing-runtime/org.test.Chiron/files/share/icons/hicolor/128x128/apps/org.test.Chiron.png
\ No newline at end of file
diff --git a/data/tests/flatpak/chiron/metadata b/data/tests/flatpak/app-update/org.test.Chiron/metadata
similarity index 100%
copy from data/tests/flatpak/chiron/metadata
copy to data/tests/flatpak/app-update/org.test.Chiron/metadata
diff --git a/data/tests/flatpak/app-with-runtime/org.test.Chiron/.gitignore 
b/data/tests/flatpak/app-with-runtime/org.test.Chiron/.gitignore
new file mode 100644
index 0000000..fea15c0
--- /dev/null
+++ b/data/tests/flatpak/app-with-runtime/org.test.Chiron/.gitignore
@@ -0,0 +1,2 @@
+export
+files/share/app-info
diff --git a/data/tests/flatpak/chiron/files/bin/chiron.sh 
b/data/tests/flatpak/app-with-runtime/org.test.Chiron/files/bin/chiron.sh
similarity index 100%
rename from data/tests/flatpak/chiron/files/bin/chiron.sh
rename to data/tests/flatpak/app-with-runtime/org.test.Chiron/files/bin/chiron.sh
diff --git a/data/tests/flatpak/chiron/files/share/appdata/org.test.Chiron.appdata.xml 
b/data/tests/flatpak/app-with-runtime/org.test.Chiron/files/share/appdata/org.test.Chiron.appdata.xml
similarity index 83%
rename from data/tests/flatpak/chiron/files/share/appdata/org.test.Chiron.appdata.xml
rename to data/tests/flatpak/app-with-runtime/org.test.Chiron/files/share/appdata/org.test.Chiron.appdata.xml
index b6d5138..0d912a8 100644
--- a/data/tests/flatpak/chiron/files/share/appdata/org.test.Chiron.appdata.xml
+++ b/data/tests/flatpak/app-with-runtime/org.test.Chiron/files/share/appdata/org.test.Chiron.appdata.xml
@@ -9,9 +9,6 @@
   <description><p>Long description.</p></description>
   <url type="homepage">http://127.0.0.1/</url>
   <releases>
-    <release date="2015-02-13" version="1.2.4">
-      <description><p>This is best.</p></description>
-    </release>
     <release date="2014-12-15" version="1.2.3">
       <description><p>This is better.</p></description>
     </release>
diff --git a/data/tests/flatpak/chiron/files/share/applications/org.test.Chiron.desktop 
b/data/tests/flatpak/app-with-runtime/org.test.Chiron/files/share/applications/org.test.Chiron.desktop
similarity index 100%
rename from data/tests/flatpak/chiron/files/share/applications/org.test.Chiron.desktop
rename to data/tests/flatpak/app-with-runtime/org.test.Chiron/files/share/applications/org.test.Chiron.desktop
diff --git a/data/tests/flatpak/chiron/metadata b/data/tests/flatpak/app-with-runtime/org.test.Chiron/metadata
similarity index 100%
rename from data/tests/flatpak/chiron/metadata
rename to data/tests/flatpak/app-with-runtime/org.test.Chiron/metadata
diff --git a/data/tests/flatpak/org.test.Runtime/files/.empty 
b/data/tests/flatpak/app-with-runtime/org.test.Runtime/files/.empty
similarity index 100%
rename from data/tests/flatpak/org.test.Runtime/files/.empty
rename to data/tests/flatpak/app-with-runtime/org.test.Runtime/files/.empty
diff --git a/data/tests/flatpak/org.test.Runtime/files/.gitignore 
b/data/tests/flatpak/app-with-runtime/org.test.Runtime/files/.gitignore
similarity index 100%
rename from data/tests/flatpak/org.test.Runtime/files/.gitignore
rename to data/tests/flatpak/app-with-runtime/org.test.Runtime/files/.gitignore
diff --git a/data/tests/flatpak/org.test.Runtime/files/share/metainfo/org.test.Runtime.metainfo.xml 
b/data/tests/flatpak/app-with-runtime/org.test.Runtime/files/share/metainfo/org.test.Runtime.metainfo.xml
similarity index 100%
rename from data/tests/flatpak/org.test.Runtime/files/share/metainfo/org.test.Runtime.metainfo.xml
rename to 
data/tests/flatpak/app-with-runtime/org.test.Runtime/files/share/metainfo/org.test.Runtime.metainfo.xml
diff --git a/data/tests/flatpak/org.test.Runtime/metadata 
b/data/tests/flatpak/app-with-runtime/org.test.Runtime/metadata
similarity index 100%
rename from data/tests/flatpak/org.test.Runtime/metadata
rename to data/tests/flatpak/app-with-runtime/org.test.Runtime/metadata
diff --git a/data/tests/flatpak/org.test.Runtime/usr/share/libtest/README 
b/data/tests/flatpak/app-with-runtime/org.test.Runtime/usr/share/libtest/README
similarity index 100%
rename from data/tests/flatpak/org.test.Runtime/usr/share/libtest/README
rename to data/tests/flatpak/app-with-runtime/org.test.Runtime/usr/share/libtest/README
diff --git a/data/tests/flatpak/build.py b/data/tests/flatpak/build.py
new file mode 100755
index 0000000..b29579e
--- /dev/null
+++ b/data/tests/flatpak/build.py
@@ -0,0 +1,69 @@
+#!/bin/python
+
+import subprocess
+import os
+import shutil
+
+def build_flatpak(appid, srcdir, repodir, cleanrepodir=True):
+    print 'Building %s from %s into %s' % (appid, srcdir, repodir)
+
+    # delete repodir
+    if cleanrepodir and os.path.exists(repodir):
+        print "Deleting %s" % repodir
+        #shutil.rmtree(repodir)
+
+    # delete exportdir
+    exportdir = os.path.join(srcdir, appid, 'export')
+    if os.path.exists(exportdir):
+        print "Deleting %s" % exportdir
+        shutil.rmtree(exportdir)
+
+    # finish the build
+    argv = ['flatpak', 'build-finish']
+    argv.append(os.path.join(srcdir, appid))
+    subprocess.call(argv)
+
+    # compose AppStream data
+    argv = ['appstream-compose']
+    argv.append('--origin=flatpak')
+    argv.append('--basename=%s' % appid)
+    argv.append('--prefix=%s' % os.path.join(srcdir, appid, 'files'))
+    argv.append('--output-dir=%s' % os.path.join(srcdir, appid, 'files/share/app-info/xmls'))
+    argv.append(appid)
+    subprocess.call(argv)
+
+    # export into repo
+    argv = ['flatpak', 'build-export']
+    argv.append(repodir)
+    argv.append(os.path.join(srcdir, appid))
+    argv.append('--update-appstream')
+    if appid.find('Runtime') != -1:
+        argv.append('--runtime')
+    subprocess.call(argv)
+
+# normal app with runtime in same remote
+build_flatpak('org.test.Chiron',
+              'app-with-runtime',
+              'app-with-runtime/repo')
+build_flatpak('org.test.Runtime',
+              'app-with-runtime',
+              'app-with-runtime/repo',
+              cleanrepodir=False)
+
+# app referencing remote that cannot be found
+build_flatpak('org.test.Chiron',
+              'app-with-runtime',
+              'app-missing-runtime/repo')
+
+# app with an update
+build_flatpak('org.test.Chiron',
+              'app-with-runtime',
+              'app-update/repo')
+build_flatpak('org.test.Runtime',
+              'app-with-runtime',
+              'app-update/repo',
+              cleanrepodir=False)
+build_flatpak('org.test.Chiron',
+              'app-update',
+              'app-update/repo',
+              cleanrepodir=False)
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 29f9c1c..1d1b9df 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -3494,6 +3494,35 @@ gs_plugin_loader_plugin_dir_changed_cb (GFileMonitor *monitor,
 }
 
 /**
+ * gs_plugin_loader_setup_again:
+ * @plugin_loader: a #GsPluginLoader
+ *
+ * Calls setup on each plugin. This should only be used from the self tests
+ * and in a controlled way.
+ */
+void
+gs_plugin_loader_setup_again (GsPluginLoader *plugin_loader)
+{
+       GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+       g_autoptr(GsPluginLoaderJob) job = gs_plugin_loader_job_new (plugin_loader);
+       job->action = GS_PLUGIN_ACTION_SETUP;
+       job->failure_flags = GS_PLUGIN_FAILURE_FLAGS_NO_CONSOLE;
+       job->function_name = "gs_plugin_setup";
+       for (guint i = 0; i < priv->plugins->len; i++) {
+               GsPlugin *plugin = g_ptr_array_index (priv->plugins, i);
+               g_autoptr(GError) error_local = NULL;
+               if (!gs_plugin_get_enabled (plugin))
+                       continue;
+               if (!gs_plugin_loader_call_vfunc (job, plugin, NULL, NULL,
+                                                 NULL, &error_local)) {
+                       g_warning ("resetup of %s failed: %s",
+                                  gs_plugin_get_name (plugin),
+                                  error_local->message);
+               }
+       }
+}
+
+/**
  * gs_plugin_loader_setup:
  * @plugin_loader: a #GsPluginLoader
  * @whitelist: list of plugin names, or %NULL
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 212cc65..279be97 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -277,6 +277,9 @@ AsProfile   *gs_plugin_loader_get_profile           (GsPluginLoader *plugin_loader);
 GsApp          *gs_plugin_loader_app_create            (GsPluginLoader *plugin_loader,
                                                         const gchar    *unique_id);
 
+/* only useful from the self tests */
+void            gs_plugin_loader_setup_again           (GsPluginLoader *plugin_loader);
+
 G_END_DECLS
 
 #endif /* __GS_PLUGIN_LOADER_H */
diff --git a/src/gs-plugin.c b/src/gs-plugin.c
index 4823a34..d858632 100644
--- a/src/gs-plugin.c
+++ b/src/gs-plugin.c
@@ -345,6 +345,8 @@ gs_plugin_get_symbol (GsPlugin *plugin, const gchar *function_name)
        gpointer func = NULL;
        g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&priv->vfuncs_mutex);
 
+       g_return_val_if_fail (function_name != NULL, NULL);
+
        /* disabled plugins shouldn't be checked */
        if (!priv->enabled)
                return NULL;
diff --git a/src/gs-self-test.c b/src/gs-self-test.c
index 824ca8e..da2ef7a 100644
--- a/src/gs-self-test.c
+++ b/src/gs-self-test.c
@@ -1092,7 +1092,7 @@ gs_plugin_loader_flatpak_repo_func (GsPluginLoader *plugin_loader)
 }
 
 static void
-gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
+gs_plugin_loader_flatpak_app_with_runtime_func (GsPluginLoader *plugin_loader)
 {
        GsApp *app;
        GsApp *runtime;
@@ -1115,12 +1115,15 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
        g_autoptr(GsAppList) list = NULL;
        g_autoptr(GsAppList) sources = NULL;
 
+       /* drop all caches */
+       gs_plugin_loader_setup_again (plugin_loader);
+
        /* no flatpak, abort */
        if (!gs_plugin_loader_get_enabled (plugin_loader, "flatpak"))
                return;
 
        /* no files to use */
-       repodir_fn = gs_test_get_filename ("tests/flatpak/repo");
+       repodir_fn = gs_test_get_filename ("tests/flatpak/app-with-runtime/repo");
        if (repodir_fn == NULL ||
            !g_file_test (repodir_fn, G_FILE_TEST_EXISTS)) {
                g_test_skip ("no flatpak test repo");
@@ -1143,7 +1146,7 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
 
        /* add a remote */
        app_source = gs_app_new ("test");
-       testdir = gs_test_get_filename ("tests/flatpak");
+       testdir = gs_test_get_filename ("tests/flatpak/app-with-runtime");
        if (testdir == NULL)
                return;
        testdir_repourl = g_strdup_printf ("file://%s/repo", testdir);
@@ -1196,6 +1199,7 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
                                        "Bingo",
                                        GS_PLUGIN_REFINE_FLAGS_REQUIRE_ORIGIN_HOSTNAME |
                                        GS_PLUGIN_REFINE_FLAGS_REQUIRE_PERMISSIONS |
+                                       GS_PLUGIN_REFINE_FLAGS_REQUIRE_VERSION |
                                        GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
                                        GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
                                        NULL,
@@ -1215,10 +1219,9 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
                         GS_APP_KUDO_SANDBOXED_SECURE |
                         GS_APP_KUDO_SANDBOXED);
        g_assert_cmpstr (gs_app_get_origin_hostname (app), ==, "");
-       g_assert_cmpstr (gs_app_get_update_version (app), ==, "1.2.4");
-       g_assert_cmpstr (gs_app_get_update_details (app), ==,
-                        "Version 1.2.4:\nThis is best.\n\n"
-                        "Version 1.2.3:\nThis is better.");
+       g_assert_cmpstr (gs_app_get_version (app), ==, "master");
+       g_assert_cmpstr (gs_app_get_update_version (app), ==, NULL);
+       g_assert_cmpstr (gs_app_get_update_details (app), ==, NULL);
        g_assert_cmpint (gs_app_get_update_urgency (app), ==, AS_URGENCY_KIND_UNKNOWN);
 
        /* install, also installing runtime */
@@ -1230,6 +1233,7 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
        g_assert_no_error (error);
        g_assert (ret);
        g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_INSTALLED);
+       g_assert_cmpstr (gs_app_get_version (app), ==, "1.2.3");
 
        /* check the application exists in the right places */
        metadata_fn = g_build_filename (root,
@@ -1317,6 +1321,334 @@ gs_plugin_loader_flatpak_func (GsPluginLoader *plugin_loader)
 }
 
 static void
+gs_plugin_loader_flatpak_app_missing_runtime_func (GsPluginLoader *plugin_loader)
+{
+       GsApp *app;
+       gboolean ret;
+       g_autofree gchar *repodir_fn = NULL;
+       g_autofree gchar *testdir = NULL;
+       g_autofree gchar *testdir_repourl = NULL;
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GsApp) app_source = NULL;
+       g_autoptr(GsAppList) list = NULL;
+
+       /* drop all caches */
+       gs_plugin_loader_setup_again (plugin_loader);
+
+       /* no flatpak, abort */
+       if (!gs_plugin_loader_get_enabled (plugin_loader, "flatpak"))
+               return;
+
+       /* no files to use */
+       repodir_fn = gs_test_get_filename ("tests/flatpak/app-missing-runtime/repo");
+       if (repodir_fn == NULL ||
+           !g_file_test (repodir_fn, G_FILE_TEST_EXISTS)) {
+               g_test_skip ("no flatpak test repo");
+               return;
+       }
+
+       /* add a remote */
+       app_source = gs_app_new ("test");
+       testdir = gs_test_get_filename ("tests/flatpak/app-missing-runtime");
+       if (testdir == NULL)
+               return;
+       testdir_repourl = g_strdup_printf ("file://%s/repo", testdir);
+       gs_app_set_kind (app_source, AS_APP_KIND_SOURCE);
+       gs_app_set_management_plugin (app_source, "flatpak");
+       gs_app_set_state (app_source, AS_APP_STATE_AVAILABLE);
+       gs_app_set_metadata (app_source, "flatpak::url", testdir_repourl);
+       ret = gs_plugin_loader_app_action (plugin_loader, app_source,
+                                          GS_PLUGIN_ACTION_INSTALL,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                          NULL,
+                                          &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+       g_assert_cmpint (gs_app_get_state (app_source), ==, AS_APP_STATE_INSTALLED);
+
+       /* refresh the appstream metadata */
+       ret = gs_plugin_loader_refresh (plugin_loader,
+                                       G_MAXUINT,
+                                       GS_PLUGIN_REFRESH_FLAGS_METADATA,
+                                       GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                       NULL,
+                                       &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+
+       /* find available application */
+       list = gs_plugin_loader_search (plugin_loader,
+                                       "Bingo",
+                                       GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+                                       GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                       NULL,
+                                       &error);
+       g_assert_no_error (error);
+       g_assert (list != NULL);
+
+       /* make sure there is one entry, the flatpak app */
+       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.test.Chiron.desktop");
+       g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE);
+
+       /* install, also installing runtime */
+       ret = gs_plugin_loader_app_action (plugin_loader, app,
+                                          GS_PLUGIN_ACTION_INSTALL,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY |
+                                          GS_PLUGIN_FAILURE_FLAGS_NO_CONSOLE,
+                                          NULL,
+                                          &error);
+       g_assert_error (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_FAILED);
+       g_assert (!ret);
+       g_clear_error (&error);
+       g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE);
+
+       /* remove the remote */
+       ret = gs_plugin_loader_app_action (plugin_loader, app_source,
+                                          GS_PLUGIN_ACTION_REMOVE,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                          NULL,
+                                          &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+       g_assert_cmpint (gs_app_get_state (app_source), ==, AS_APP_STATE_AVAILABLE);
+}
+
+static void
+update_app_progress_notify_cb (GsApp *app, GParamSpec *pspec, gpointer user_data)
+{
+       g_debug ("progress now %u%%", gs_app_get_progress (app));
+       if (user_data != NULL) {
+               guint *tmp = (guint *) user_data;
+               (*tmp)++;
+       }
+}
+
+static void
+update_app_state_notify_cb (GsApp *app, GParamSpec *pspec, gpointer user_data)
+{
+       AsAppState state = gs_app_get_state (app);
+       g_debug ("state now %s", as_app_state_to_string (state));
+       if (state == AS_APP_STATE_INSTALLING) {
+               gboolean *tmp = (gboolean *) user_data;
+               *tmp = TRUE;
+       }
+}
+
+static void
+update_app_action_finish_sync (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
+       GMainLoop *loop = (GMainLoop *) user_data;
+       gboolean ret;
+       g_autoptr(GError) error = NULL;
+       ret = gs_plugin_loader_app_action_finish (plugin_loader, res, &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+       g_main_loop_quit (loop);
+}
+
+static void
+gs_plugin_loader_flatpak_app_update_func (GsPluginLoader *plugin_loader)
+{
+       GsApp *app;
+       GsApp *app_tmp;
+       GsApp *runtime;
+       gboolean got_progress_installing = FALSE;
+       gboolean ret;
+       guint notify_progress_id;
+       guint notify_state_id;
+       guint progress_cnt = 0;
+       g_autofree gchar *repodir1_fn = NULL;
+       g_autofree gchar *repodir2_fn = NULL;
+       g_autoptr(GError) error = NULL;
+       g_autoptr(GsApp) app_source = NULL;
+       g_autoptr(GsAppList) list = NULL;
+       g_autoptr(GsAppList) list_updates = NULL;
+       g_autoptr(GMainLoop) loop = g_main_loop_new (NULL, FALSE);
+
+       /* drop all caches */
+       gs_plugin_loader_setup_again (plugin_loader);
+
+       /* no flatpak, abort */
+       if (!gs_plugin_loader_get_enabled (plugin_loader, "flatpak"))
+               return;
+
+       /* no files to use */
+       repodir1_fn = gs_test_get_filename ("tests/flatpak/app-with-runtime/repo");
+       if (repodir1_fn == NULL ||
+           !g_file_test (repodir1_fn, G_FILE_TEST_EXISTS)) {
+               g_test_skip ("no flatpak test repo");
+               return;
+       }
+       repodir2_fn = gs_test_get_filename ("tests/flatpak/app-update/repo");
+       if (repodir2_fn == NULL ||
+           !g_file_test (repodir2_fn, G_FILE_TEST_EXISTS)) {
+               g_test_skip ("no flatpak test repo");
+               return;
+       }
+
+       /* add indirection so we can switch this after install */
+       g_assert (symlink (repodir1_fn, "/var/tmp/self-test/repo") == 0);
+
+       /* add a remote */
+       app_source = gs_app_new ("test");
+       gs_app_set_kind (app_source, AS_APP_KIND_SOURCE);
+       gs_app_set_management_plugin (app_source, "flatpak");
+       gs_app_set_state (app_source, AS_APP_STATE_AVAILABLE);
+       gs_app_set_metadata (app_source, "flatpak::url", "file:///var/tmp/self-test/repo");
+       ret = gs_plugin_loader_app_action (plugin_loader, app_source,
+                                          GS_PLUGIN_ACTION_INSTALL,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                          NULL,
+                                          &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+       g_assert_cmpint (gs_app_get_state (app_source), ==, AS_APP_STATE_INSTALLED);
+
+       /* refresh the appstream metadata */
+       ret = gs_plugin_loader_refresh (plugin_loader,
+                                       G_MAXUINT,
+                                       GS_PLUGIN_REFRESH_FLAGS_METADATA,
+                                       GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                       NULL,
+                                       &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+
+       /* find available application */
+       list = gs_plugin_loader_search (plugin_loader,
+                                       "Bingo",
+                                       GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+                                       GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                       NULL,
+                                       &error);
+       g_assert_no_error (error);
+       g_assert (list != NULL);
+
+       /* make sure there is one entry, the flatpak app */
+       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.test.Chiron.desktop");
+       g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_AVAILABLE);
+
+       /* install, also installing runtime */
+       ret = gs_plugin_loader_app_action (plugin_loader, app,
+                                          GS_PLUGIN_ACTION_INSTALL,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY |
+                                          GS_PLUGIN_FAILURE_FLAGS_NO_CONSOLE,
+                                          NULL,
+                                          &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+       g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_INSTALLED);
+       g_assert_cmpstr (gs_app_get_version (app), ==, "1.2.3");
+       g_assert_cmpstr (gs_app_get_update_version (app), ==, NULL);
+       g_assert_cmpstr (gs_app_get_update_details (app), ==, NULL);
+
+       /* switch to the new repo */
+       g_assert (unlink ("/var/tmp/self-test/repo") == 0);
+       g_assert (symlink (repodir2_fn, "/var/tmp/self-test/repo") == 0);
+
+       /* refresh the appstream metadata */
+       ret = gs_plugin_loader_refresh (plugin_loader,
+                                       0, /* force now */
+                                       GS_PLUGIN_REFRESH_FLAGS_METADATA |
+                                       GS_PLUGIN_REFRESH_FLAGS_PAYLOAD,
+                                       GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                       NULL,
+                                       &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+
+       /* get the updates list */
+       list_updates = gs_plugin_loader_get_updates (plugin_loader,
+                                                    GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON |
+                                                    GS_PLUGIN_REFINE_FLAGS_REQUIRE_UPDATE_DETAILS,
+                                                    GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                                    NULL,
+                                                    &error);
+       g_assert_no_error (error);
+       g_assert (list_updates != NULL);
+
+       /* make sure there are two entries */
+       g_assert_cmpint (gs_app_list_length (list_updates), >, 3);
+       for (guint i = 0; i < gs_app_list_length (list_updates); i++) {
+               app_tmp = gs_app_list_index (list_updates, i);
+               g_debug ("got update %s", gs_app_get_unique_id (app_tmp));
+       }
+
+       /* check they are the same GObject */
+       app_tmp = gs_app_list_lookup (list_updates, "*/flatpak/test/*/org.test.Chiron.desktop/*");
+       g_assert (app_tmp == app);
+       g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_UPDATABLE_LIVE);
+       g_assert_cmpstr (gs_app_get_update_details (app), ==, "Version 1.2.4:\nThis is best.\n\nVersion 
1.2.3:\nThis is better.");
+       g_assert_cmpstr (gs_app_get_update_version (app), ==, "1.2.4");
+
+       /* care about signals */
+       notify_state_id =
+               g_signal_connect (app, "notify::state",
+                                 G_CALLBACK (update_app_state_notify_cb),
+                                 &got_progress_installing);
+       notify_progress_id =
+               g_signal_connect (app, "notify::progress",
+                                 G_CALLBACK (update_app_progress_notify_cb),
+                                 &progress_cnt);
+
+       /* use a mainloop so we get the events in the default context */
+       gs_plugin_loader_app_action_async (plugin_loader, app,
+                                          GS_PLUGIN_ACTION_UPDATE,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY |
+                                          GS_PLUGIN_FAILURE_FLAGS_NO_CONSOLE,
+                                          NULL,
+                                          update_app_action_finish_sync,
+                                          loop);
+       g_main_loop_run (loop);
+       g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_INSTALLED);
+       g_assert_cmpstr (gs_app_get_version (app), ==, "1.2.4");
+       g_assert_cmpstr (gs_app_get_update_version (app), ==, NULL);
+       g_assert_cmpstr (gs_app_get_update_details (app), ==, NULL);
+       g_assert (got_progress_installing);
+       //g_assert_cmpint (progress_cnt, >, 20); //FIXME: bug in OSTree
+
+       /* no longer care */
+       g_signal_handler_disconnect (app, notify_state_id);
+       g_signal_handler_disconnect (app, notify_progress_id);
+
+       /* remove the app */
+       ret = gs_plugin_loader_app_action (plugin_loader, app,
+                                          GS_PLUGIN_ACTION_REMOVE,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                          NULL,
+                                          &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+
+       /* remove the runtime */
+       runtime = gs_app_get_runtime (app);
+       g_assert (runtime != NULL);
+       g_assert_cmpstr (gs_app_get_unique_id (runtime), ==, 
"user/flatpak/test/runtime/org.test.Runtime.runtime/master");
+       ret = gs_plugin_loader_app_action (plugin_loader, runtime,
+                                          GS_PLUGIN_ACTION_REMOVE,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                          NULL,
+                                          &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+
+       /* remove the remote */
+       ret = gs_plugin_loader_app_action (plugin_loader, app_source,
+                                          GS_PLUGIN_ACTION_REMOVE,
+                                          GS_PLUGIN_FAILURE_FLAGS_FATAL_ANY,
+                                          NULL,
+                                          &error);
+       g_assert_no_error (error);
+       g_assert (ret);
+       g_assert_cmpint (gs_app_get_state (app_source), ==, AS_APP_STATE_AVAILABLE);
+}
+
+static void
 gs_plugin_loader_plugin_cache_func (GsPluginLoader *plugin_loader)
 {
        GsApp *app1;
@@ -1656,9 +1988,15 @@ main (int argc, char **argv)
        g_test_add_data_func ("/gnome-software/plugin-loader{repos}",
                              plugin_loader,
                              (GTestDataFunc) gs_plugin_loader_repos_func);
-       g_test_add_data_func ("/gnome-software/plugin-loader{flatpak}",
+       g_test_add_data_func ("/gnome-software/plugin-loader{flatpak-app-with-runtime}",
+                             plugin_loader,
+                             (GTestDataFunc) gs_plugin_loader_flatpak_app_with_runtime_func);
+       g_test_add_data_func ("/gnome-software/plugin-loader{flatpak-app-missing-runtime}",
+                             plugin_loader,
+                             (GTestDataFunc) gs_plugin_loader_flatpak_app_missing_runtime_func);
+       g_test_add_data_func ("/gnome-software/plugin-loader{flatpak-app-update-runtime}",
                              plugin_loader,
-                             (GTestDataFunc) gs_plugin_loader_flatpak_func);
+                             (GTestDataFunc) gs_plugin_loader_flatpak_app_update_func);
        g_test_add_data_func ("/gnome-software/plugin-loader{fwupd}",
                              plugin_loader,
                              (GTestDataFunc) gs_plugin_loader_fwupd_func);
diff --git a/src/plugins/gs-appstream.c b/src/plugins/gs-appstream.c
index 43a50ea..40be14a 100644
--- a/src/plugins/gs-appstream.c
+++ b/src/plugins/gs-appstream.c
@@ -392,6 +392,10 @@ gs_appstream_refine_app_updates (GsPlugin *plugin,
        GPtrArray *releases;
        g_autoptr(GPtrArray) updates_list = NULL;
 
+       /* only for UPDATABLE apps */
+       if (!gs_app_is_updatable (app))
+               return TRUE;
+
        /* make a list of valid updates */
        updates_list = g_ptr_array_new ();
        releases = as_app_get_releases (item);
diff --git a/src/plugins/gs-flatpak.c b/src/plugins/gs-flatpak.c
index 227c841..ef2aca8 100644
--- a/src/plugins/gs-flatpak.c
+++ b/src/plugins/gs-flatpak.c
@@ -43,6 +43,7 @@ struct _GsFlatpak {
        AsAppScope               scope;
        GsPlugin                *plugin;
        AsStore                 *store;
+       guint                    changed_id;
 };
 
 G_DEFINE_TYPE (GsFlatpak, gs_flatpak, G_TYPE_OBJECT)
@@ -440,8 +441,9 @@ gs_flatpak_setup (GsFlatpak *self, GCancellable *cancellable, GError **error)
                gs_plugin_flatpak_error_convert (error);
                return FALSE;
        }
-       g_signal_connect (self->monitor, "changed",
-                         G_CALLBACK (gs_plugin_flatpak_changed_cb), self);
+       self->changed_id =
+               g_signal_connect (self->monitor, "changed",
+                                 G_CALLBACK (gs_plugin_flatpak_changed_cb), self);
 
        /* ensure the legacy AppStream symlink cache is deleted */
        if (!gs_flatpak_symlinks_cleanup (self->installation, cancellable, error))
@@ -1029,6 +1031,9 @@ gs_flatpak_add_updates (GsFlatpak *self, GsAppList *list,
                        continue;
                }
                gs_app_set_state (app, AS_APP_STATE_UPDATABLE_LIVE);
+               gs_app_set_update_details (app, NULL);
+               gs_app_set_update_version (app, NULL);
+               gs_app_set_update_urgency (app, AS_URGENCY_KIND_UNKNOWN);
                gs_app_set_size_download (app, 0);
                gs_app_list_add (list_tmp, app);
        }
@@ -1918,6 +1923,19 @@ gs_plugin_refine_item_size (GsFlatpak *self,
        return TRUE;
 }
 
+static void
+gs_flatpak_refine_appstream_release (AsApp *item, GsApp *app)
+{
+       AsRelease *rel = as_app_get_release_default (item);
+       if (!gs_app_is_installed (app))
+               return;
+       if (rel == NULL)
+               return;
+       if (as_release_get_version (rel) == NULL)
+               return;
+       gs_app_set_version (app, as_release_get_version (rel));
+}
+
 static gboolean
 gs_flatpak_refine_appstream (GsFlatpak *self, GsApp *app, GError **error)
 {
@@ -1938,7 +1956,13 @@ gs_flatpak_refine_appstream (GsFlatpak *self, GsApp *app, GError **error)
                                              AS_STORE_SEARCH_FLAG_USE_WILDCARDS);
        if (item == NULL)
                return TRUE;
-       return gs_appstream_refine_app (self->plugin, app, item, error);
+       if (!gs_appstream_refine_app (self->plugin, app, item, error))
+               return FALSE;
+
+       /* use the default release as the version number */
+       gs_flatpak_refine_appstream_release (item, app);
+
+       return TRUE;
 }
 
 gboolean
@@ -2271,6 +2295,7 @@ install_runtime_for_app (GsFlatpak *self,
                                                     gs_flatpak_progress_cb, app,
                                                     cancellable, error);
                if (xref == NULL) {
+                       gs_plugin_flatpak_error_convert (error);
                        gs_app_set_state_recover (runtime);
                        gs_app_set_state_recover (app);
                        return FALSE;
@@ -2358,6 +2383,11 @@ gs_flatpak_app_install (GsFlatpak *self,
 
        /* state is known */
        gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+
+       /* set new version */
+       if (!gs_flatpak_refine_appstream (self, app, error))
+               return FALSE;
+
        return TRUE;
 }
 
@@ -2396,6 +2426,14 @@ gs_flatpak_update_app (GsFlatpak *self,
 
        /* state is known */
        gs_app_set_state (app, AS_APP_STATE_INSTALLED);
+       gs_app_set_update_version (app, NULL);
+       gs_app_set_update_details (app, NULL);
+       gs_app_set_update_urgency (app, AS_URGENCY_KIND_UNKNOWN);
+
+       /* set new version */
+       if (!gs_flatpak_refine_appstream (self, app, error))
+               return FALSE;
+
        return TRUE;
 }
 
@@ -2929,6 +2967,11 @@ gs_flatpak_finalize (GObject *object)
        g_return_if_fail (GS_IS_FLATPAK (object));
        self = GS_FLATPAK (object);
 
+       if (self->changed_id > 0) {
+               g_signal_handler_disconnect (self->monitor, self->changed_id);
+               self->changed_id = 0;
+       }
+
        g_object_unref (self->plugin);
        g_object_unref (self->store);
        g_hash_table_unref (self->broken_remotes);
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index 7b54cb9..bb5bee4 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -39,6 +39,7 @@
 struct GsPluginData {
        AsStore                 *store;
        GHashTable              *app_hash_old;
+       guint                    store_changed_id;
 };
 
 #define GS_PLUGIN_NUMBER_CHANGED_RELOAD        10
@@ -186,7 +187,10 @@ void
 gs_plugin_destroy (GsPlugin *plugin)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
-       g_hash_table_unref (priv->app_hash_old);
+       if (priv->store_changed_id != 0)
+               g_signal_handler_disconnect (priv->store, priv->store_changed_id);
+       if (priv->app_hash_old != NULL)
+               g_hash_table_unref (priv->app_hash_old);
        g_object_unref (priv->store);
 }
 
@@ -250,6 +254,18 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
        guint i;
        g_autoptr(GHashTable) origins = NULL;
 
+       /* setup_again from the tests */
+       as_store_remove_all (priv->store);
+       if (priv->app_hash_old != NULL) {
+               if (priv->app_hash_old != NULL)
+                       g_hash_table_unref (priv->app_hash_old);
+               priv->app_hash_old = NULL;
+       }
+       if (priv->store_changed_id != 0) {
+               g_signal_handler_disconnect (priv->store, priv->store_changed_id);
+               priv->store_changed_id = 0;
+       }
+
        /* Parse the XML */
        if (g_getenv ("GNOME_SOFTWARE_PREFER_LOCAL") != NULL) {
                as_store_set_add_flags (priv->store,
@@ -293,9 +309,10 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
        priv->app_hash_old = gs_plugin_appstream_create_app_hash (priv->store);
 
        /* watch for changes */
-       g_signal_connect (priv->store, "changed",
-                         G_CALLBACK (gs_plugin_appstream_store_changed_cb),
-                         plugin);
+       priv->store_changed_id =
+               g_signal_connect (priv->store, "changed",
+                                 G_CALLBACK (gs_plugin_appstream_store_changed_cb),
+                                 plugin);
 
        /* ensure the token cache */
        as_store_load_search_cache (priv->store);
diff --git a/src/plugins/gs-plugin-flatpak.c b/src/plugins/gs-plugin-flatpak.c
index b6220a7..4da4320 100644
--- a/src/plugins/gs-plugin-flatpak.c
+++ b/src/plugins/gs-plugin-flatpak.c
@@ -131,6 +131,9 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
 {
        GsPluginData *priv = gs_plugin_get_data (plugin);
 
+       /* clear in case we're called from resetup in the self tests */
+       g_ptr_array_set_size (priv->flatpaks, 0);
+
        /* we use a permissions helper to elevate privs */
        if (priv->has_system_helper && priv->destdir_for_tests == NULL) {
                g_autoptr(FlatpakInstallation) installation = NULL;


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