summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Leeds <matthew.leeds@endlessm.com>2020-04-04 23:11:45 -0700
committerAlexander Larsson <alexander.larsson@gmail.com>2020-04-16 09:22:11 +0200
commite847bb31b65d9981a59327cb76bf6b67f29218e4 (patch)
treebe97bb1205e6ee9ba825ab7c73d8013cfdca9919
parentf61f9d17da17f41f322cd44515e67b0c2d353383 (diff)
downloadflatpak-e847bb31b65d9981a59327cb76bf6b67f29218e4.tar.gz
installation: Re-implement list_installed_refs_for_update()
Re-implement flatpak_installation_list_installed_refs_for_update() using a FlatpakTransaction, so we can guarantee it always gives the same set of things to update as the update command. This API is used by GNOME Software and many times in the past g-s has not shown the same list of apps to be updated as the flatpak CLI. See: - https://gitlab.gnome.org/GNOME/gnome-software/issues/539 - https://gitlab.gnome.org/GNOME/gnome-software/-/merge_requests/430 This commit also expands the unit tests for this API, which were already quite good. Now we test that missing subpaths of locale extensions show up as updates, and updates that have been pulled but not deployed show up as well. The latter is a break from how this function used to behave, but it seems unlikely to break any application.
-rw-r--r--common/flatpak-dir-private.h9
-rw-r--r--common/flatpak-dir.c110
-rw-r--r--common/flatpak-installation.c212
-rw-r--r--common/flatpak-utils-private.h6
-rw-r--r--tests/Makefile.am.inc2
-rw-r--r--tests/libtest.sh4
-rwxr-xr-xtests/make-test-runtime.sh10
-rwxr-xr-xtests/test-extensions.sh2
-rw-r--r--tests/testlibrary.c180
9 files changed, 327 insertions, 208 deletions
diff --git a/common/flatpak-dir-private.h b/common/flatpak-dir-private.h
index 1e89d1b3..0f33963f 100644
--- a/common/flatpak-dir-private.h
+++ b/common/flatpak-dir-private.h
@@ -706,11 +706,6 @@ gboolean flatpak_dir_needs_update_for_commit_and_subpaths (FlatpakDir *self,
const char *ref,
const char *target_commit,
const char **opt_subpaths);
-gboolean flatpak_dir_check_if_installed_ref_needs_update (FlatpakDir *self,
- FlatpakRemoteState *state,
- const char *ref,
- GBytes *deploy_data,
- GCancellable *cancellable);
char * flatpak_dir_check_for_update (FlatpakDir *self,
FlatpakRemoteState *state,
const char *ref,
@@ -943,10 +938,6 @@ GPtrArray * flatpak_dir_find_remote_related_for_metadata (FlatpakDir *se
GKeyFile *metakey,
GCancellable *cancellable,
GError **error);
-gboolean flatpak_dir_check_installed_ref_missing_related_ref (FlatpakDir *self,
- FlatpakRemoteState *state,
- const gchar *full_ref,
- GCancellable *cancellable);
GPtrArray * flatpak_dir_find_remote_related (FlatpakDir *dir,
FlatpakRemoteState *state,
const char *ref,
diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c
index 2e2a710a..10cb61e1 100644
--- a/common/flatpak-dir.c
+++ b/common/flatpak-dir.c
@@ -8970,80 +8970,6 @@ flatpak_dir_needs_update_for_commit_and_subpaths (FlatpakDir *self,
return FALSE;
}
-/* This returns true if the remote version of the installed version is
- * newer than the installed, or if some related ref it relies on is
- * missing (which will be fixed by an update run of FlatpakTransaction). */
-gboolean
-flatpak_dir_check_if_installed_ref_needs_update (FlatpakDir *self,
- FlatpakRemoteState *state,
- const char *ref,
- GBytes *deploy_data,
- GCancellable *cancellable)
-{
- g_autofree char *latest_commit = NULL;
- guint64 latest_timestamp;
- guint64 current_timestamp;
- g_autoptr(GFile) deploy_dir = NULL;
- const gchar *current_commit = NULL;
-
- if (flatpak_dir_ref_is_masked (self, ref))
- return FALSE;
-
- current_commit = flatpak_deploy_data_get_commit (deploy_data);
- current_timestamp = flatpak_deploy_data_get_timestamp (deploy_data);
-
- if (!flatpak_remote_state_lookup_ref (state, ref, &latest_commit, &latest_timestamp, NULL, NULL, NULL))
- return FALSE;
-
- /* Check if the latest is newer than the current installed, if so update */
- if (current_timestamp == 0)
- {
- /* This happens during deploy data updates, fall back to commit comparisons */
- if (strcmp (current_commit, latest_commit) != 0)
- return TRUE;
- }
- else
- {
- if (latest_timestamp > current_timestamp ||
- (latest_timestamp == current_timestamp &&
- strcmp (current_commit, latest_commit) != 0))
- return TRUE;
- }
-
- /* The ref itself doesn't need update, but we do some extra checks
- * for related refs that can trigger an update. */
-
- /* Check if all "should-download" related refs for the ref are installed.
- * If not, add the ref in @updates array so that it can be installed via
- * FlatpakTransaction's update-op.
- *
- * This makes sure that the ref (maybe an app or runtime) remains in usable
- * state and fixes itself through an update.
- */
- if (flatpak_dir_check_installed_ref_missing_related_ref (self, state, ref, cancellable))
- return TRUE;
-
- /* This checks if an already installed app has a missing runtime.
- * If so, return that installed ref in the updates list, so that FlatpakTransaction
- * can resolve one of its operation to install the runtime instead.
- *
- * Runtime of an app can go missing if an app upgrade makes an app dependent on a new runtime
- * entirely. We had couple of cases like that in the past, for example, before it was updated
- * to use FlatpakTransaction, updating an app in GNOME Software to a version which needs a
- * different runtime would not install that new runtime, leaving the app unusable.
- */
- if (g_str_has_prefix (ref, "app/"))
- {
- const gchar *runtime = flatpak_deploy_data_get_runtime (deploy_data);
- g_autofree gchar *full_runtime_ref = g_strconcat ("runtime/", runtime, NULL);
- deploy_dir = flatpak_dir_get_if_deployed (self, full_runtime_ref, NULL, cancellable);
- if (deploy_dir == NULL)
- return TRUE;
- }
-
- return FALSE;
-}
-
/* This is called by the old-school non-transaction flatpak_installation_update, so doesn't do a lot. */
char *
flatpak_dir_check_for_update (FlatpakDir *self,
@@ -13565,42 +13491,6 @@ flatpak_dir_find_remote_related_for_metadata (FlatpakDir *self,
return g_steal_pointer (&related);
}
-gboolean
-flatpak_dir_check_installed_ref_missing_related_ref (FlatpakDir *self,
- FlatpakRemoteState *state,
- const gchar *full_ref,
- GCancellable *cancellable)
-{
- g_autoptr(GPtrArray) remote_related_refs = NULL;
- g_autoptr(GError) local_error = NULL;
- guint j;
-
- remote_related_refs = flatpak_dir_find_remote_related (self, state, full_ref,
- cancellable, &local_error);
- if (remote_related_refs == NULL)
- {
- g_warning ("Unable to get remote related refs for %s: %s", full_ref, local_error->message);
- return FALSE;
- }
-
- for (j = 0; j < remote_related_refs->len; j++)
- {
- FlatpakRelated *rel = g_ptr_array_index (remote_related_refs, j);
- g_autoptr(GFile) deploy = NULL;
-
- if (!rel->download || flatpak_dir_ref_is_masked (self, rel->ref))
- continue;
-
- deploy = flatpak_dir_get_if_deployed (self, rel->ref, NULL, cancellable);
- /* If the related extension ref was meant to be auto-installed but was not found to be
- * deployed, return TRUE. It will be pulled in via a FlatpakTransaction's update-op again. */
- if (rel->download && deploy == NULL)
- return TRUE;
- }
-
- return FALSE;
-}
-
GPtrArray *
flatpak_dir_find_remote_related (FlatpakDir *self,
FlatpakRemoteState *state,
diff --git a/common/flatpak-installation.c b/common/flatpak-installation.c
index 52fd41cd..3668ee04 100644
--- a/common/flatpak-installation.c
+++ b/common/flatpak-installation.c
@@ -979,25 +979,48 @@ flatpak_installation_list_installed_refs_by_kind (FlatpakInstallation *self,
}
static gboolean
-ref_check_for_update (FlatpakDir *dir,
- const char *ref,
- GHashTable *remote_states,
- GCancellable *cancellable)
+transaction_ready (FlatpakTransaction *transaction,
+ GHashTable **related_to_ops)
{
- g_autoptr(GBytes) deploy_data = NULL;
- FlatpakRemoteState *state;
- const char *origin = NULL;
+ GList *ops = flatpak_transaction_get_operations (transaction);
- deploy_data = flatpak_dir_get_deploy_data (dir, ref, FLATPAK_DEPLOY_VERSION_CURRENT, cancellable, NULL);
- if (deploy_data == NULL)
- return FALSE;
+ for (GList *l = ops; l != NULL; l = l->next)
+ {
+ FlatpakTransactionOperation *op = l->data;
+ FlatpakTransactionOperation *related_to_op = flatpak_transaction_operation_get_related_to_op (op);
+ FlatpakTransactionOperationType type = flatpak_transaction_operation_get_operation_type (op);
+
+ /* There is currently no way for a set of updates to lead to an
+ * uninstall, but check anyway.
+ */
+ if (type == FLATPAK_TRANSACTION_OPERATION_UNINSTALL)
+ {
+ const char *ref = flatpak_transaction_operation_get_ref (op);
+ g_warning ("Update transaction unexpectedly wants to uninstall %s", ref);
+ continue;
+ }
- origin = flatpak_deploy_data_get_origin (deploy_data);
- state = g_hash_table_lookup (remote_states, origin);
- if (state == NULL)
- return FALSE;
+ g_hash_table_insert (*related_to_ops,
+ g_object_ref (op),
+ related_to_op ? g_object_ref (related_to_op) : NULL);
+ }
+
+ g_list_free_full (ops, g_object_unref);
+
+ /* Abort the transaction; we only wanted to know what it would do */
+ return FALSE;
+}
+
+static gint
+installed_ref_compare (gconstpointer _iref_a,
+ gconstpointer _iref_b)
+{
+ const FlatpakInstalledRef *iref_a = *(const FlatpakInstalledRef **)_iref_a;
+ const FlatpakInstalledRef *iref_b = *(const FlatpakInstalledRef **)_iref_b;
+ g_autofree char *ref_a = flatpak_ref_format_ref (FLATPAK_REF (iref_a));
+ g_autofree char *ref_b = flatpak_ref_format_ref (FLATPAK_REF (iref_b));
- return flatpak_dir_check_if_installed_ref_needs_update (dir, state, ref, deploy_data, cancellable);
+ return strcmp (ref_a, ref_b);
}
/**
@@ -1006,16 +1029,18 @@ ref_check_for_update (FlatpakDir *dir,
* @cancellable: (nullable): a #GCancellable
* @error: return location for a #GError
*
- * Lists the installed references that has a remote update that is not
- * locally available. However, even though an app is not returned by this
- * it can have local updates available that has not been deployed. Look
- * at commit vs latest_commit on installed apps for this.
+ * Lists the installed apps and runtimes that have an update available, either
+ * from the configured remote or locally available but not deployed (see
+ * flatpak_transaction_set_no_deploy()).
*
* This also checks if any of #FlatpakInstalledRef has a missing #FlatpakRelatedRef
* (which has `should-download` set to %TRUE) or runtime. If so, it adds the
* ref to the returning #GPtrArray to pull in the #FlatpakRelatedRef or runtime
* again via an update operation in #FlatpakTransaction.
*
+ * In case more than one app needs an update of the same runtime or extension,
+ * it is possible that only one of the apps will be returned by this function.
+ *
* Returns: (transfer container) (element-type FlatpakInstalledRef): a GPtrArray of
* #FlatpakInstalledRef instances, or %NULL on error
*/
@@ -1024,76 +1049,123 @@ flatpak_installation_list_installed_refs_for_update (FlatpakInstallation *self,
GCancellable *cancellable,
GError **error)
{
- g_autoptr(FlatpakDir) dir_orig = flatpak_installation_get_dir_maybe_no_repo (self);
- g_autoptr(FlatpakDir) dir = NULL;
- g_auto(GStrv) remote_names = NULL;
- g_autoptr(GPtrArray) updates = NULL; /* (element-type FlatpakInstalledRef) */
- g_autoptr(GHashTable) remote_states = NULL; /* (element-type utf8 FlatpakRemoteState) */
- g_auto(GStrv) refs_app = NULL;
- g_auto(GStrv) refs_runtime = NULL;
+ g_autoptr(GPtrArray) installed_refs = NULL; /* (element-type FlatpakInstalledRef) */
+ g_autoptr(GHashTable) installed_refs_hash = NULL; /* (element-type utf8 FlatpakInstalledRef) */
+ g_autoptr(GPtrArray) installed_refs_for_update = NULL; /* (element-type FlatpakInstalledRef) */
+ g_autoptr(GHashTable) installed_refs_for_update_set = NULL; /* (element-type utf8) */
+ g_autoptr(GHashTable) related_to_ops = NULL; /* (element-type FlatpakTransactionOperation FlatpakTransactionOperation) */
+ g_autoptr(FlatpakTransaction) transaction = NULL;
+ g_autoptr(GError) local_error = NULL;
- /* We clone the dir here to make sure we re-read the latest ostree repo config, in case
- it has local changes */
- dir = flatpak_dir_clone (dir_orig);
- if (!flatpak_dir_maybe_ensure_repo (dir, cancellable, error))
+ installed_refs = flatpak_installation_list_installed_refs (self, cancellable, error);
+ if (installed_refs == NULL)
return NULL;
- remote_names = flatpak_dir_list_remotes (dir, cancellable, error);
- if (remote_names == NULL)
+ /* Here we use a FlatpakTransaction to determine what needs updating, and
+ * abort it before actually doing the updates. This ensures we are consistent
+ * with the CLI update command.
+ */
+ transaction = flatpak_transaction_new_for_installation (self, cancellable, error);
+ if (transaction == NULL)
return NULL;
- remote_states = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)flatpak_remote_state_unref);
- for (int i = 0; remote_names[i] != NULL; ++i)
+ installed_refs_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ for (guint i = 0; i < installed_refs->len; i++)
{
- const char *remote = remote_names[i];
- g_autoptr(FlatpakRemoteState) state = NULL;
- g_autoptr(GError) local_error = NULL;
+ FlatpakInstalledRef *installed_ref = g_ptr_array_index (installed_refs, i);
+ g_autofree char *ref = flatpak_ref_format_ref (FLATPAK_REF (installed_ref));
+
+ /* This hash table will be used later for efficient search */
+ g_hash_table_insert (installed_refs_hash, g_strdup (ref), installed_ref);
- if (flatpak_dir_get_remote_disabled (dir, remote))
+ if (flatpak_transaction_add_update (transaction, ref, NULL, NULL, &local_error))
continue;
- state = flatpak_dir_get_remote_state_optional (dir, remote, FALSE, NULL, &local_error);
- if (state == NULL)
+ if (g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_REMOTE_NOT_FOUND))
{
- g_debug ("Update: Failed to read remote %s: %s", remote, local_error->message);
- continue;
+ g_debug ("%s: Unable to update %s: %s", G_STRFUNC, ref, local_error->message);
+ g_clear_error (&local_error);
+ }
+ else
+ {
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return NULL;
}
- g_hash_table_insert (remote_states, (char *)remote, g_steal_pointer (&state));
}
- updates = g_ptr_array_new_with_free_func (g_object_unref);
+ related_to_ops = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, null_safe_g_object_unref);
+
+ g_signal_connect (transaction, "ready", G_CALLBACK (transaction_ready), &related_to_ops);
- if (flatpak_dir_list_refs (dir, "app", &refs_app, cancellable, error))
+ flatpak_transaction_run (transaction, cancellable, &local_error);
+ g_assert (local_error != NULL);
+ if (!g_error_matches (local_error, FLATPAK_ERROR, FLATPAK_ERROR_ABORTED))
{
- for (int i = 0; refs_app[i] != NULL; i++)
- {
- const char *ref = refs_app[i];
- if (ref_check_for_update (dir, ref, remote_states, cancellable))
- {
- g_debug ("%s: Installed ref %s needs update", G_STRFUNC, ref);
- FlatpakInstalledRef *installed_ref = get_ref (dir, ref, cancellable, NULL);
- if (installed_ref)
- g_ptr_array_add (updates, g_object_ref (installed_ref));
- }
- }
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ return NULL;
}
-
- if (flatpak_dir_list_refs (dir, "runtime", &refs_runtime, cancellable, error))
+ g_clear_error (&local_error);
+
+ installed_refs_for_update = g_ptr_array_new_with_free_func (g_object_unref);
+ installed_refs_for_update_set = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* For each ref that would be affected by the transaction, if it is
+ * installed, add it to the list to be returned and otherwise add the ref
+ * that caused it be added. We need to cover all of the following cases:
+ * 1. For an app or runtime that has an update available, add it to the list
+ * (including a locale extension which needs more subpaths downloaded).
+ * 2. For an app or extension that has a missing runtime, add the
+ * app/extension to the list.
+ * 3. For an app that's missing a "should-download" related ref, add the app
+ * to the list.
+ */
+ GLNX_HASH_TABLE_FOREACH_KV (related_to_ops,
+ FlatpakTransactionOperation *, op,
+ FlatpakTransactionOperation *, related_to_op)
{
- for (int i = 0; refs_runtime[i] != NULL; i++)
- {
- const char *ref = refs_runtime[i];
- if (ref_check_for_update (dir, ref, remote_states, cancellable))
- {
- g_debug ("%s: Installed ref %s needs update", G_STRFUNC, ref);
- FlatpakInstalledRef *installed_ref = get_ref (dir, ref, cancellable, NULL);
- if (installed_ref)
- g_ptr_array_add (updates, g_object_ref (installed_ref));
- }
- }
+ const char *op_ref = flatpak_transaction_operation_get_ref (op);
+ FlatpakInstalledRef *installed_ref;
+
+ /* Here we use the existing installed_refs_hash instead of get_ref()
+ * since staying in memory should be more efficient than disk I/O
+ */
+ installed_ref = g_hash_table_lookup (installed_refs_hash, op_ref);
+ if (installed_ref != NULL)
+ {
+ if (!g_hash_table_contains (installed_refs_for_update_set, op_ref))
+ {
+ g_hash_table_add (installed_refs_for_update_set, (char *)op_ref);
+ g_debug ("%s: Installed ref %s needs update", G_STRFUNC, op_ref);
+ g_ptr_array_add (installed_refs_for_update,
+ g_object_ref (installed_ref));
+ }
+ }
+ else if (related_to_op != NULL)
+ {
+ const char *related_op_ref = flatpak_transaction_operation_get_ref (related_to_op);
+ if (!g_hash_table_contains (installed_refs_for_update_set, related_op_ref))
+ {
+ installed_ref = g_hash_table_lookup (installed_refs_hash, related_op_ref);
+ if (installed_ref != NULL)
+ {
+ g_hash_table_add (installed_refs_for_update_set, (char *)related_op_ref);
+ g_debug ("%s: Installed ref %s needs update", G_STRFUNC, related_op_ref);
+ g_ptr_array_add (installed_refs_for_update,
+ g_object_ref (installed_ref));
+ }
+ }
+ }
+
+ /* Note: installed_ref could be NULL if for example op is installing a
+ * related ref of a missing runtime.
+ */
}
- return g_steal_pointer (&updates);
+ /* Remove non-determinism for the sake of the unit tests */
+ g_ptr_array_sort (installed_refs_for_update, installed_ref_compare);
+
+ return g_steal_pointer (&installed_refs_for_update);
}
/* Find all USB and LAN repositories which share the same collection ID as
diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h
index 2004d26a..3fc1a243 100644
--- a/common/flatpak-utils-private.h
+++ b/common/flatpak-utils-private.h
@@ -872,6 +872,12 @@ gboolean flatpak_repo_resolve_rev (OstreeRepo *repo,
GCancellable *cancellable,
GError **error);
+static inline void
+null_safe_g_object_unref (gpointer data)
+{
+ g_clear_object (&data);
+}
+
#define FLATPAK_MESSAGE_ID "c7b39b1e006b464599465e105b361485"
#endif /* __FLATPAK_UTILS_H__ */
diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc
index a7b25cb0..606ca935 100644
--- a/tests/Makefile.am.inc
+++ b/tests/Makefile.am.inc
@@ -198,7 +198,7 @@ tests/test-%.wrap:
tests/runtime-repo: tests/make-test-runtime.sh flatpak
rm -rf tests/runtime-repo
- PATH=$(abs_top_builddir):$${PATH} $(top_srcdir)/tests/make-test-runtime.sh tests/runtime-repo org.test.Platform master ""
+ PATH=$(abs_top_builddir):$${PATH} $(top_srcdir)/tests/make-test-runtime.sh tests/runtime-repo org.test.Platform master "" ""
check_DATA += tests/runtime-repo
diff --git a/tests/libtest.sh b/tests/libtest.sh
index 4c3e5971..d6cf7e67 100644
--- a/tests/libtest.sh
+++ b/tests/libtest.sh
@@ -280,7 +280,7 @@ make_runtime () {
(
flock -s 200
if [ ! -d ${RUNTIME_REPO} ]; then
- $(dirname $0)/make-test-runtime.sh ${RUNTIME_REPO} org.test.Platform ${BRANCH} "" > /dev/null
+ $(dirname $0)/make-test-runtime.sh ${RUNTIME_REPO} org.test.Platform ${BRANCH} "" "" > /dev/null
fi
) 200>${TEST_DATA_DIR}/runtime-repo-lock
fi
@@ -385,7 +385,7 @@ setup_sdk_repo () {
fi
BRANCH=${3:-master}
- GPGARGS="${GPGARGS:-${FL_GPGARGS}}" . $(dirname $0)/make-test-runtime.sh repos/${REPONAME} org.test.Sdk "${BRANCH}" "${COLLECTION_ID}" make mkdir cp touch > /dev/null
+ GPGARGS="${GPGARGS:-${FL_GPGARGS}}" . $(dirname $0)/make-test-runtime.sh repos/${REPONAME} org.test.Sdk "${BRANCH}" "${COLLECTION_ID}" "" make mkdir cp touch > /dev/null
update_repo $REPONAME "${COLLECTION_ID}"
}
diff --git a/tests/make-test-runtime.sh b/tests/make-test-runtime.sh
index 5d2c309b..af289625 100755
--- a/tests/make-test-runtime.sh
+++ b/tests/make-test-runtime.sh
@@ -16,6 +16,9 @@ shift
COLLECTION_ID=$1
shift
+EXTRA="${1-}"
+shift
+
mkdir ${DIR}/files
mkdir ${DIR}/usr
cat > ${DIR}/metadata <<EOF
@@ -74,6 +77,13 @@ for i in `cat $LIBS`; do
done
ln -s bash ${DIR}/usr/bin/sh
+# This only exists so we can update the runtime
+cat > ${DIR}/usr/bin/runtime_hello.sh <<EOF
+#!/bin/sh
+echo "Hello world, from a runtime$EXTRA"
+EOF
+chmod a+x ${DIR}/usr/bin/runtime_hello.sh
+
# We copy the C.UTF8 locale and call it en_US. Its a bit of a lie, but
# the real en_US locale is often not available, because its in the
# local archive.
diff --git a/tests/test-extensions.sh b/tests/test-extensions.sh
index ee859c60..c888fcd2 100755
--- a/tests/test-extensions.sh
+++ b/tests/test-extensions.sh
@@ -95,7 +95,7 @@ EOF
mkdir -p repos
ostree init --repo=repos/test --mode=archive-z2
-$(dirname $0)/make-test-runtime.sh repos/test org.test.Platform master "" bash ls cat echo readlink > /dev/null
+$(dirname $0)/make-test-runtime.sh repos/test org.test.Platform master "" "" bash ls cat echo readlink > /dev/null
$(dirname $0)/make-test-app.sh repos/test "" master "" > /dev/null
# Modify platform metadata
diff --git a/tests/testlibrary.c b/tests/testlibrary.c
index bd6e8d2c..28cf108a 100644
--- a/tests/testlibrary.c
+++ b/tests/testlibrary.c
@@ -239,11 +239,11 @@ test_installation_config (void)
}
static void
-configure_languages (void)
+configure_languages (const char *lang)
{
- char *argv[] = { "flatpak", "config", "--user", "--set", "languages", "de", NULL };
+ const char *argv[] = { "flatpak", "config", "--user", "--set", "languages", lang, NULL };
- run_test_subprocess (argv, RUN_TEST_SUBPROCESS_DEFAULT);
+ run_test_subprocess ((char **)argv, RUN_TEST_SUBPROCESS_DEFAULT);
}
static void
@@ -333,7 +333,7 @@ test_languages_config (void)
g_clear_pointer (&value, g_strfreev);
clean_extra_languages ();
- configure_languages ();
+ configure_languages ("de");
}
static void
@@ -1151,10 +1151,12 @@ test_update_related_refs (void)
g_autoptr(GPtrArray) updatable_refs = NULL;
g_autoptr(GPtrArray) related_refs = NULL;
g_autoptr(GError) error = NULL;
- FlatpakInstalledRef *iref = NULL;
+ g_autoptr(FlatpakInstalledRef) iref = NULL;
g_autoptr(FlatpakInstalledRef) runtime_ref = NULL;
gboolean res;
g_autofree char *app = NULL;
+ g_autofree char *app_locale = NULL;
+ const char * const *subpaths;
app = g_strdup_printf ("app/org.test.Hello/%s/master",
flatpak_get_default_arch ());
@@ -1187,13 +1189,14 @@ test_update_related_refs (void)
G_GNUC_END_IGNORE_DEPRECATIONS
g_assert_no_error (error);
g_assert (FLATPAK_IS_INSTALLED_REF (iref));
- iref = NULL;
+ g_clear_object (&iref);
/* We expect no installed related refs (i.e. org.test.Hello.Locale) at this point */
related_refs = flatpak_installation_list_installed_related_refs_sync (inst, repo_name, app, NULL, &error);
g_assert_cmpint (related_refs->len, ==, 0);
updatable_refs = flatpak_installation_list_installed_refs_for_update (inst, NULL, &error);
+ g_assert_no_error (error);
g_assert_cmpint (updatable_refs->len, ==, 1);
iref = g_ptr_array_index (updatable_refs, 0);
g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (iref)), ==, "org.test.Hello");
@@ -1215,6 +1218,58 @@ test_update_related_refs (void)
iref = flatpak_installation_get_installed_ref (inst, FLATPAK_REF_KIND_RUNTIME, "org.test.Hello.Locale", NULL, NULL, NULL, &error);
g_assert_nonnull (iref);
g_assert_no_error (error);
+
+ /* Now check that when we have only subpaths of the locale extension
+ * installed and we change the configured languages, it shows as needing an
+ * update.
+ */
+ subpaths = flatpak_installed_ref_get_subpaths (iref);
+ g_assert_cmpint (g_strv_length ((char **) subpaths), ==, 1);
+ g_assert_cmpstr (subpaths[0], ==, "/de");
+ g_clear_object (&iref);
+
+ configure_languages ("es");
+ flatpak_installation_drop_caches (inst, NULL, &error);
+ g_assert_no_error (error);
+
+ g_clear_pointer (&updatable_refs, g_ptr_array_unref);
+ updatable_refs = flatpak_installation_list_installed_refs_for_update (inst, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpint (updatable_refs->len, ==, 1);
+ iref = g_ptr_array_index (updatable_refs, 0);
+ g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (iref)), ==, "org.test.Hello.Locale");
+
+ /* Now update org.test.Hello.Locale and check that the subpaths are updated. */
+ g_clear_object (&transaction);
+ transaction = flatpak_transaction_new_for_installation (inst, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (transaction);
+
+ app_locale = g_strdup_printf ("runtime/org.test.Hello.Locale/%s/master",
+ flatpak_get_default_arch ());
+
+ res = flatpak_transaction_add_update (transaction, app_locale, NULL, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+
+ res = flatpak_transaction_run (transaction, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+
+ flatpak_installation_drop_caches (inst, NULL, &error);
+ g_assert_no_error (error);
+ iref = flatpak_installation_get_installed_ref (inst, FLATPAK_REF_KIND_RUNTIME, "org.test.Hello.Locale", NULL, NULL, NULL, &error);
+ g_assert_nonnull (iref);
+ g_assert_no_error (error);
+
+ subpaths = flatpak_installed_ref_get_subpaths (iref);
+ g_assert_cmpint (g_strv_length ((char **) subpaths), ==, 2);
+ g_assert_cmpstr (subpaths[0], ==, "/de");
+ g_assert_cmpstr (subpaths[1], ==, "/es");
+
+ /* Reset things */
+ configure_languages ("de");
+ empty_installation (inst);
}
static void
@@ -1298,8 +1353,8 @@ test_list_remote_related_refs (void)
g_assert_true (should_delete);
g_assert_false (should_autoprune);
- configure_languages();
- clean_extra_languages();
+ configure_languages ("de");
+ clean_extra_languages ();
}
static void
@@ -1564,6 +1619,7 @@ test_install_launch_uninstall (void)
}
static void update_test_app (void);
+static void update_test_runtime (void);
static void update_repo (const char *update_repo_name);
static const char *
@@ -1807,8 +1863,9 @@ test_list_updates (void)
/* Add a previous-id to the deploy file */
mangle_deploy_file (ref);
- /* Update the test app and list the update */
+ /* Update the test app and the runtime and list the updates */
update_test_app ();
+ update_test_runtime ();
update_repo ("test");
/* Drop all in-memory summary caches so we can find the new update */
@@ -1818,15 +1875,18 @@ test_list_updates (void)
refs = flatpak_installation_list_installed_refs_for_update (inst, NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (refs);
- g_assert_cmpint (refs->len, ==, 2);
+ g_assert_cmpint (refs->len, ==, 3);
update_ref = g_ptr_array_index (refs, 0);
g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (update_ref)), ==, "org.test.Hello");
g_assert_cmpint (flatpak_ref_get_kind (FLATPAK_REF (update_ref)), ==, FLATPAK_REF_KIND_APP);
update_ref = g_ptr_array_index (refs, 1);
g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (update_ref)), ==, "org.test.Hello.Locale");
g_assert_cmpint (flatpak_ref_get_kind (FLATPAK_REF (update_ref)), ==, FLATPAK_REF_KIND_RUNTIME);
+ update_ref = g_ptr_array_index (refs, 2);
+ g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (update_ref)), ==, "org.test.Platform");
+ g_assert_cmpint (flatpak_ref_get_kind (FLATPAK_REF (update_ref)), ==, FLATPAK_REF_KIND_RUNTIME);
- /* Install the new update */
+ /* Install the app update */
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
updated_ref = flatpak_installation_update (inst,
FLATPAK_UPDATE_FLAGS_NONE,
@@ -1864,6 +1924,82 @@ test_list_updates (void)
}
static void
+test_list_undeployed_updates (void)
+{
+ g_autoptr(FlatpakInstallation) inst = NULL;
+ g_autoptr(FlatpakTransaction) transaction = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GPtrArray) refs = NULL;
+ FlatpakInstalledRef *update_ref = NULL;
+ g_autoptr(FlatpakInstalledRef) updated_ref = NULL;
+ g_autofree gchar *app = NULL;
+ gboolean res;
+
+ app = g_strdup_printf ("app/org.test.Hello/%s/master",
+ flatpak_get_default_arch ());
+
+ inst = flatpak_installation_new_user (NULL, &error);
+ g_assert_no_error (error);
+
+ empty_installation (inst);
+
+ transaction = flatpak_transaction_new_for_installation (inst, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (transaction);
+
+ /* install org.test.Hello, and have org.test.Hello.Locale and org.test.Platform
+ * added as deps/related
+ */
+ res = flatpak_transaction_add_install (transaction, repo_name, app, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+
+ res = flatpak_transaction_run (transaction, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (res);
+ g_clear_object (&transaction);
+
+ refs = flatpak_installation_list_installed_refs (inst, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (refs);
+ g_assert_cmpint (refs->len, ==, 3);
+ g_clear_pointer (&refs, g_ptr_array_unref);
+
+ update_test_app ();
+ update_repo ("test");
+
+ /* Drop all in-memory summary caches so we can find the new update */
+ flatpak_installation_drop_caches (inst, NULL, &error);
+ g_assert_no_error (error);
+
+ /* Install the app update but don't deploy it */
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ updated_ref = flatpak_installation_update (inst,
+ FLATPAK_UPDATE_FLAGS_NO_DEPLOY,
+ FLATPAK_REF_KIND_APP,
+ "org.test.Hello",
+ flatpak_get_default_arch (), "master",
+ NULL, NULL, NULL, &error);
+ G_GNUC_END_IGNORE_DEPRECATIONS
+ g_assert_no_error (error);
+ g_assert_true (FLATPAK_IS_INSTALLED_REF (updated_ref));
+
+ refs = flatpak_installation_list_installed_refs_for_update (inst, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (refs);
+ g_assert_cmpint (refs->len, ==, 2);
+ update_ref = g_ptr_array_index (refs, 0);
+ g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (update_ref)), ==, "org.test.Hello");
+ g_assert_cmpint (flatpak_ref_get_kind (FLATPAK_REF (update_ref)), ==, FLATPAK_REF_KIND_APP);
+ update_ref = g_ptr_array_index (refs, 1);
+ g_assert_cmpstr (flatpak_ref_get_name (FLATPAK_REF (update_ref)), ==, "org.test.Hello.Locale");
+ g_assert_cmpint (flatpak_ref_get_kind (FLATPAK_REF (update_ref)), ==, FLATPAK_REF_KIND_RUNTIME);
+
+ /* Uninstall the runtime and app */
+ empty_installation (inst);
+}
+
+static void
test_list_updates_offline (void)
{
g_autoptr(FlatpakInstallation) inst = NULL;
@@ -2005,7 +2141,7 @@ make_test_runtime (const char *runtime_repo_name)
g_autofree char *arg0 = NULL;
g_autofree char *arg1 = NULL;
char *argv[] = {
- NULL, NULL, "org.test.Platform", "master", "", NULL
+ NULL, NULL, "org.test.Platform", "master", "", "", NULL
};
arg0 = g_test_build_filename (G_TEST_DIST, "make-test-runtime.sh", NULL);
@@ -2047,6 +2183,19 @@ update_test_app (void)
}
static void
+update_test_runtime (void)
+{
+ g_autofree char *arg0 = NULL;
+ char *argv[] = { NULL, "repos/test", "org.test.Platform", "master", "", "UPDATED", NULL };
+
+ arg0 = g_test_build_filename (G_TEST_DIST, "make-test-runtime.sh", NULL);
+ argv[0] = arg0;
+ argv[4] = repo_collection_id;
+
+ run_test_subprocess (argv, RUN_TEST_SUBPROCESS_DEFAULT);
+}
+
+static void
update_repo (const char *update_repo_name)
{
g_autofree char *arg4 = NULL;
@@ -2230,7 +2379,7 @@ setup_repo (void)
launch_httpd ();
add_remote_user ("test");
add_flatpakrepo ("test");
- configure_languages ();
+ configure_languages ("de");
/* another copy of the same repo, with different url */
g_assert_cmpint (symlink ("test", "repos/copy-of-test"), ==, 0);
@@ -3904,8 +4053,8 @@ test_list_installed_related_refs (void)
g_assert_cmpstr (flatpak_related_ref_get_subpaths (ref)[0], ==, "/de");
g_assert_cmpstr (flatpak_related_ref_get_subpaths (ref)[1], ==, "/en");
- configure_languages();
- clean_extra_languages();
+ configure_languages ("de");
+ clean_extra_languages ();
}
static void
@@ -4113,6 +4262,7 @@ main (int argc, char *argv[])
g_test_add_func ("/library/install-launch-uninstall", test_install_launch_uninstall);
g_test_add_func ("/library/list-refs-in-remote", test_list_refs_in_remotes);
g_test_add_func ("/library/list-updates", test_list_updates);
+ g_test_add_func ("/library/list-undeployed-updates", test_list_undeployed_updates);
g_test_add_func ("/library/list-updates-offline", test_list_updates_offline);
g_test_add_func ("/library/transaction", test_misc_transaction);
g_test_add_func ("/library/transaction-install-uninstall", test_transaction_install_uninstall);