summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2020-10-28 10:08:21 +0100
committerAlexander Larsson <alexl@redhat.com>2020-10-28 17:10:09 +0100
commit9b2de3f75d9df734e5df42f105cca0c7e69ef91a (patch)
treed02087d7e84b2f6671c30e8170977f5d28fb2592
parent8c49b48ee6a558a01512a48324400e54524e49cd (diff)
downloadflatpak-9b2de3f75d9df734e5df42f105cca0c7e69ef91a.tar.gz
utils: Add FlatpakDecomposed helper
This is a ref-counted version of a ref string that is guaranteed to be in the proper form, and is pre-decomposed for efficient partial matches.
-rw-r--r--common/flatpak-utils-private.h43
-rw-r--r--common/flatpak-utils.c431
-rw-r--r--tests/testcommon.c147
3 files changed, 620 insertions, 1 deletions
diff --git a/common/flatpak-utils-private.h b/common/flatpak-utils-private.h
index dbd3816e..a4642ef0 100644
--- a/common/flatpak-utils-private.h
+++ b/common/flatpak-utils-private.h
@@ -216,6 +216,49 @@ char **flatpak_decompose_ref (const char *ref,
char * flatpak_get_arch_for_ref (const char *ref);
const char *flatpak_get_compat_arch_reverse (const char *compat_arch);
+
+typedef struct _FlatpakDecomposed FlatpakDecomposed;
+FlatpakDecomposed *flatpak_decomposed_new_from_ref (const char *ref,
+ GError **error);
+FlatpakDecomposed *flatpak_decomposed_new_from_refspec (const char *refspec,
+ GError **error);
+FlatpakDecomposed *flatpak_decomposed_new_from_ref_take (char *ref,
+ GError **error);
+FlatpakDecomposed *flatpak_decomposed_new_from_refspec_take (char *refspec,
+ GError **error);
+FlatpakDecomposed *flatpak_decomposed_ref (FlatpakDecomposed *ref);
+void flatpak_decomposed_unref (FlatpakDecomposed *ref);
+const char * flatpak_decomposed_peek_ref (FlatpakDecomposed *ref);
+const char * flatpak_decomposed_peek_refspec (FlatpakDecomposed *ref);
+char * flatpak_decomposed_get_ref (FlatpakDecomposed *ref);
+char * flatpak_decomposed_get_refspec (FlatpakDecomposed *ref);
+char * flatpak_decomposed_get_remote (FlatpakDecomposed *ref);
+gboolean flatpak_decomposed_equal (FlatpakDecomposed *ref_a,
+ FlatpakDecomposed *ref_b);
+guint flatpak_decomposed_hash (FlatpakDecomposed *ref);
+gboolean flatpak_decomposed_is_app (FlatpakDecomposed *ref);
+gboolean flatpak_decomposed_is_runtime (FlatpakDecomposed *ref);
+FlatpakKinds flatpak_decomposed_get_kind (FlatpakDecomposed *ref);
+const char * flatpak_decomposed_peek_id (FlatpakDecomposed *ref);
+char * flatpak_decomposed_get_id (FlatpakDecomposed *ref);
+gboolean flatpak_decomposed_is_id (FlatpakDecomposed *ref,
+ const char *id);
+gboolean flatpak_decomposed_is_id_fuzzy (FlatpakDecomposed *ref,
+ const char *id);
+gboolean flatpak_decomposed_id_is_subref (FlatpakDecomposed *ref);
+const char * flatpak_decomposed_peek_arch (FlatpakDecomposed *ref);
+char * flatpak_decomposed_get_arch (FlatpakDecomposed *ref);
+gboolean flatpak_decomposed_is_arch (FlatpakDecomposed *ref,
+ const char *arch);
+gboolean flatpak_decomposed_is_arches (FlatpakDecomposed *ref,
+ const char **arches);
+const char * flatpak_decomposed_peek_branch (FlatpakDecomposed *ref);
+char * flatpak_decomposed_get_branch (FlatpakDecomposed *ref);
+gboolean flatpak_decomposed_is_branch (FlatpakDecomposed *ref,
+ const char *branch);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakDecomposed, flatpak_decomposed_unref)
+
char * flatpak_filter_glob_to_regexp (const char *glob, gboolean runtime_only, GError **error);
gboolean flatpak_parse_filters (const char *data,
GRegex **allow_refs_out,
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index ecd206ff..089bdd81 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -1113,7 +1113,7 @@ str_has_suffix (const gchar *str, gsize str_len,
if (str_len < suffix_len)
return FALSE;
- return strcmp (str + str_len - suffix_len, suffix) == 0;
+ return strncmp (str + str_len - suffix_len, suffix, suffix_len) == 0;
}
@@ -1459,6 +1459,435 @@ flatpak_decompose_ref (const char *full_ref,
return g_steal_pointer (&parts);
}
+struct _FlatpakDecomposed {
+ int ref_count;
+ guint16 ref_offset;
+ guint16 id_offset;
+ guint16 arch_offset;
+ guint16 branch_offset;
+ char *ref;
+};
+
+static gboolean
+is_valid_initial_remote_name_character (gint c)
+{
+ return
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '_');
+}
+
+static gboolean
+is_valid_remote_name_character (gint c)
+{
+ return
+ is_valid_initial_remote_name_character (c) ||
+ c == '-' ||
+ c == '.';
+}
+
+static gboolean
+is_valid_remote_name (const char *remote,
+ gsize len)
+{
+ const char *end;
+
+ if (len == 0)
+ return FALSE;
+
+ end = remote + len;
+
+ if (!is_valid_initial_remote_name_character (*remote++))
+ return FALSE;
+
+ while (remote < end)
+ {
+ char c = *remote++;
+ if (!is_valid_remote_name_character (c))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static FlatpakDecomposed *
+_flatpak_decomposed_new (char *ref,
+ gboolean allow_refspec,
+ gboolean take,
+ GError **error)
+{
+ g_autoptr(GError) local_error = NULL;
+ const char *p;
+ const char *slash;
+ gsize ref_offset;
+ gsize id_offset;
+ gsize arch_offset;
+ gsize branch_offset;
+ gsize len;
+ FlatpakDecomposed *decomposed;
+
+ /* We want to use uint16 to store offset, so fail on uselessly large refs */
+ len = strlen (ref);
+ if (len > 0xffff)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Ref too long"));
+ return NULL;
+ }
+
+ p = ref;
+ if (allow_refspec)
+ {
+ const char *colon = strchr (p, ':');
+ if (colon != NULL)
+ {
+ if (!is_valid_remote_name (ref, colon - ref))
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Invalid remote name"));
+ return NULL;
+ }
+ p = colon + 1;
+ }
+ }
+ ref_offset = p - ref;
+
+ if (g_str_has_prefix (p, "app/"))
+ p += strlen ("app/");
+ else if (g_str_has_prefix (p, "runtime/"))
+ p += strlen ("runtime/");
+ else
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("%s is not application or runtime"), ref);
+ return NULL;
+ }
+
+ id_offset = p - ref;
+
+ slash = strchr (p, '/');
+ if (slash == NULL)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Wrong number of components in %s"), ref);
+ return NULL;
+ }
+
+ if (!flatpak_is_valid_name (p, slash - p, &local_error))
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Invalid name %.*s: %s"), (int)(slash - p), p, local_error->message);
+ return NULL;
+ }
+
+ p = slash + 1;
+
+ arch_offset = p - ref;
+
+ slash = strchr (p, '/');
+ if (slash == NULL)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Wrong number of components in %s"), ref);
+ return NULL;
+ }
+
+ if (slash - p == 0) /* empty arch */
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Invalid arch %.*s"), (int)(slash - p), p);
+ return NULL;
+ }
+
+ p = slash + 1;
+ branch_offset = p - ref;
+
+ slash = strchr (p, '/');
+ if (slash != NULL)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Wrong number of components in %s"), ref);
+ return NULL;
+ }
+
+ if (!flatpak_is_valid_branch (p, -1, &local_error))
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_REF, _("Invalid branch %s: %s"), p, local_error->message);
+ return NULL;
+ }
+
+ decomposed = g_malloc (sizeof (struct _FlatpakDecomposed) + len); /* len == strlen, and then the terminating zero fits in data[1] */
+ decomposed->ref_count = 1;
+ decomposed->ref_offset = (guint16)ref_offset;
+ decomposed->id_offset = (guint16)id_offset;
+ decomposed->arch_offset = (guint16)arch_offset;
+ decomposed->branch_offset = (guint16)branch_offset;
+ decomposed->ref = take ? ref : g_strdup (ref);
+
+ return decomposed;
+}
+
+FlatpakDecomposed *
+flatpak_decomposed_new_from_ref (const char *ref,
+ GError **error)
+{
+ return _flatpak_decomposed_new ((char *)ref, FALSE, FALSE, error);
+}
+
+FlatpakDecomposed *
+flatpak_decomposed_new_from_refspec (const char *refspec,
+ GError **error)
+{
+ return _flatpak_decomposed_new ((char *)refspec, TRUE, FALSE, error);
+}
+
+FlatpakDecomposed *
+flatpak_decomposed_new_from_ref_take (char *ref,
+ GError **error)
+{
+ return _flatpak_decomposed_new (ref, FALSE, TRUE, error);
+}
+
+FlatpakDecomposed *
+flatpak_decomposed_new_from_refspec_take (char *refspec,
+ GError **error)
+{
+ return _flatpak_decomposed_new (refspec, TRUE, TRUE, error);
+}
+
+FlatpakDecomposed *
+flatpak_decomposed_ref (FlatpakDecomposed *ref)
+{
+ g_atomic_int_inc (&ref->ref_count);
+ return ref;
+}
+
+void
+flatpak_decomposed_unref (FlatpakDecomposed *ref)
+{
+ if (g_atomic_int_dec_and_test (&ref->ref_count))
+ {
+ g_free (ref->ref);
+ g_free (ref);
+ }
+}
+
+const char *
+flatpak_decomposed_peek_ref (FlatpakDecomposed *ref)
+{
+ return (const char *)&ref->ref[ref->ref_offset];
+}
+
+char *
+flatpak_decomposed_get_ref (FlatpakDecomposed *ref)
+{
+ return g_strdup (flatpak_decomposed_peek_ref (ref));
+}
+
+const char *
+flatpak_decomposed_peek_refspec (FlatpakDecomposed *ref)
+{
+ return (const char *)&ref->ref[0];
+}
+
+char *
+flatpak_decomposed_get_refspec (FlatpakDecomposed *ref)
+{
+ return g_strdup (flatpak_decomposed_peek_refspec (ref));
+}
+
+char *
+flatpak_decomposed_get_remote (FlatpakDecomposed *ref)
+{
+ if (ref->ref_offset == 0)
+ return NULL;
+
+ return g_strndup (ref->ref, ref->ref_offset - 1);
+}
+
+gboolean
+flatpak_decomposed_equal (FlatpakDecomposed *ref_a,
+ FlatpakDecomposed *ref_b)
+{
+ return strcmp (ref_a->ref, ref_b->ref) == 0;
+}
+
+guint
+flatpak_decomposed_hash (FlatpakDecomposed *ref)
+{
+ return g_str_hash (ref->ref);
+}
+
+gboolean
+flatpak_decomposed_is_app (FlatpakDecomposed *ref)
+{
+ return ref->ref[0] == 'a';
+}
+
+gboolean
+flatpak_decomposed_is_runtime (FlatpakDecomposed *ref)
+{
+ return ref->ref[0] == 'r';
+}
+
+FlatpakKinds
+flatpak_decomposed_get_kind (FlatpakDecomposed *ref)
+{
+ if (flatpak_decomposed_is_app (ref))
+ return FLATPAK_KINDS_APP;
+ else
+ return FLATPAK_KINDS_RUNTIME;
+}
+
+/* A slashed string ends at '/' instead of nul */
+static gboolean
+slashed_str_equal (const char *slashed_str, const char *str)
+{
+ char c;
+ while ((c = *str) != 0)
+ {
+ char s_c = *slashed_str;
+ if (s_c == '/')
+ return FALSE; /* slashed_str stopped early */
+
+ if (s_c != c)
+ return FALSE; /* slashed_str not same */
+
+ str++;
+ slashed_str++;
+ }
+
+ if (*slashed_str != '/') /* str stopped early */
+ return FALSE;
+
+ return TRUE;
+}
+
+/* These are for refs, so ascii case only */
+static gboolean
+slashed_str_strcasestr (const char *haystack,
+ gsize haystack_len,
+ const char *needle)
+{
+ gssize needle_len = strlen (needle);
+
+ if (needle_len > haystack_len)
+ return FALSE;
+
+ if (needle_len == 0)
+ return TRUE;
+
+ for (gssize i = 0; i <= haystack_len - needle_len; i++)
+ {
+ if (g_ascii_strncasecmp (haystack + i, needle, needle_len) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+const char *
+flatpak_decomposed_peek_id (FlatpakDecomposed *ref)
+{
+ return &ref->ref[ref->id_offset];
+}
+
+char *
+flatpak_decomposed_get_id (FlatpakDecomposed *ref)
+{
+ const char *ref_id = flatpak_decomposed_peek_id (ref);
+
+ return g_strndup (ref_id, ref->arch_offset - ref->id_offset - 1);
+}
+
+gboolean
+flatpak_decomposed_is_id (FlatpakDecomposed *ref,
+ const char *id)
+{
+ const char *ref_id = flatpak_decomposed_peek_id (ref);
+
+ return slashed_str_equal (ref_id, id);
+}
+
+/* See if the given id looks similar to this ref. The
+ * Levenshtein distance constant was chosen pretty arbitrarily. */
+gboolean
+flatpak_decomposed_is_id_fuzzy (FlatpakDecomposed *ref,
+ const char *id)
+{
+ const char *ref_id = flatpak_decomposed_peek_id (ref);
+ gsize ref_id_len = ref->arch_offset - ref->id_offset - 1;
+
+ if (slashed_str_strcasestr (ref_id, ref_id_len, id))
+ return TRUE;
+
+ return flatpak_levenshtein_distance (id, -1, ref_id, ref_id_len) <= 2;
+}
+
+gboolean
+flatpak_decomposed_id_is_subref (FlatpakDecomposed *ref)
+{
+ const char *ref_id = flatpak_decomposed_peek_id (ref);
+ gsize ref_id_len = ref->arch_offset - ref->id_offset - 1;
+
+ return flatpak_id_has_subref_suffix (ref_id, ref_id_len);
+}
+
+const char *
+flatpak_decomposed_peek_arch (FlatpakDecomposed *ref)
+{
+ return &ref->ref[ref->arch_offset];
+}
+
+char *
+flatpak_decomposed_get_arch (FlatpakDecomposed *ref)
+{
+ const char *ref_arch = flatpak_decomposed_peek_arch (ref);
+
+ return g_strndup (ref_arch, ref->branch_offset - ref->arch_offset - 1);
+}
+
+gboolean
+flatpak_decomposed_is_arch (FlatpakDecomposed *ref,
+ const char *arch)
+{
+ const char *ref_arch = flatpak_decomposed_peek_arch (ref);
+
+ return slashed_str_equal (ref_arch, arch);
+}
+
+gboolean
+flatpak_decomposed_is_arches (FlatpakDecomposed *ref,
+ const char **arches)
+{
+ const char *ref_arch = flatpak_decomposed_peek_arch (ref);
+
+ for (int i = 0; arches[i] != NULL; i++)
+ {
+ if (slashed_str_equal (ref_arch, arches[i]))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+const char *
+flatpak_decomposed_peek_branch (FlatpakDecomposed *ref)
+{
+ return &ref->ref[ref->branch_offset];
+}
+
+char *
+flatpak_decomposed_get_branch (FlatpakDecomposed *ref)
+{
+ const char *ref_branch = flatpak_decomposed_peek_branch (ref);
+
+ return g_strdup (ref_branch);
+}
+
+gboolean
+flatpak_decomposed_is_branch (FlatpakDecomposed *ref,
+ const char *branch)
+{
+ const char *ref_branch = flatpak_decomposed_peek_branch (ref);
+
+ return strcmp (ref_branch, branch) == 0;
+}
+
static const char *
next_element (const char **partial_ref)
{
diff --git a/tests/testcommon.c b/tests/testcommon.c
index 21df877b..b28cda04 100644
--- a/tests/testcommon.c
+++ b/tests/testcommon.c
@@ -100,6 +100,152 @@ test_valid_name (void)
g_assert_true (flatpak_is_valid_name ("org.flat_pak__.te--st", -1, NULL));
}
+static void
+test_decompose (void)
+{
+ g_autoptr(FlatpakDecomposed) app_ref = NULL;
+ g_autoptr(FlatpakDecomposed) runtime_ref = NULL;
+ g_autoptr(FlatpakDecomposed) refspec = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree char *app_id = NULL;
+ g_autofree char *app_arch = NULL;
+ g_autofree char *app_branch = NULL;
+ g_autofree char *runtime_id = NULL;
+ g_autofree char *runtime_arch = NULL;
+ g_autofree char *runtime_branch = NULL;
+
+ g_assert_null (flatpak_decomposed_new_from_ref ("app/wrong/x86_64/master", &error));
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ g_assert_null (flatpak_decomposed_new_from_ref ("app/org.the.app//master", &error));
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ g_assert_null (flatpak_decomposed_new_from_ref ("app/org.the.app/x86_64/@foo", &error));
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ g_assert_null (flatpak_decomposed_new_from_ref ("wrong/org.the.wrong/x86_64/master", &error));
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ g_assert_null (flatpak_decomposed_new_from_ref ("app/org.the.app/x86_64/master/extra", &error));
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ g_assert_null (flatpak_decomposed_new_from_ref ("app/org.the.app/x86_64", &error));
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ runtime_ref = flatpak_decomposed_new_from_ref ("runtime/org.the.runtime/x86_64/master", &error);
+ g_assert (runtime_ref != NULL);
+ g_assert_null (error);
+
+ g_assert_cmpstr (flatpak_decomposed_peek_ref (runtime_ref), ==, "runtime/org.the.runtime/x86_64/master");
+ g_assert_cmpstr (flatpak_decomposed_peek_refspec (runtime_ref), ==, "runtime/org.the.runtime/x86_64/master");
+ g_assert (flatpak_decomposed_equal (runtime_ref, runtime_ref));
+ g_assert (flatpak_decomposed_hash (runtime_ref) == g_str_hash ("runtime/org.the.runtime/x86_64/master"));
+ g_assert (!flatpak_decomposed_is_app (runtime_ref));
+ g_assert (flatpak_decomposed_is_runtime (runtime_ref));
+ g_assert (flatpak_decomposed_get_kind (runtime_ref) == FLATPAK_KINDS_RUNTIME);
+
+ g_assert_cmpstr (flatpak_decomposed_peek_id (runtime_ref), ==, "org.the.runtime/x86_64/master");
+ runtime_id = flatpak_decomposed_get_id (runtime_ref);
+ g_assert_cmpstr (runtime_id, ==, "org.the.runtime");
+ g_assert (flatpak_decomposed_is_id (runtime_ref, "org.the.runtime"));
+ g_assert (!flatpak_decomposed_is_id (runtime_ref, "org.the.runtim"));
+ g_assert (!flatpak_decomposed_is_id (runtime_ref, "org.the.runtimee"));
+
+ g_assert_cmpstr (flatpak_decomposed_peek_arch (runtime_ref), ==, "x86_64/master");
+ runtime_arch = flatpak_decomposed_get_arch (runtime_ref);
+ g_assert_cmpstr (runtime_arch, ==, "x86_64");
+ g_assert (flatpak_decomposed_is_arch (runtime_ref, "x86_64"));
+ g_assert (!flatpak_decomposed_is_arch (runtime_ref, "x86_6"));
+ g_assert (!flatpak_decomposed_is_arch (runtime_ref, "x86_644"));
+
+ g_assert_cmpstr (flatpak_decomposed_peek_branch (runtime_ref), ==, "master");
+ runtime_branch = flatpak_decomposed_get_branch (runtime_ref);
+ g_assert_cmpstr (runtime_branch, ==, "master");
+ g_assert (flatpak_decomposed_is_branch (runtime_ref, "master"));
+ g_assert (!flatpak_decomposed_is_arch (runtime_ref, "maste"));
+ g_assert (!flatpak_decomposed_is_arch (runtime_ref, "masterr"));
+
+ app_ref = flatpak_decomposed_new_from_ref ("app/org.the.app/x86_64/master", &error);
+ g_assert (app_ref != NULL);
+ g_assert_null (error);
+
+ g_assert_cmpstr (flatpak_decomposed_peek_ref (app_ref), ==, "app/org.the.app/x86_64/master");
+ g_assert_cmpstr (flatpak_decomposed_peek_refspec (app_ref), ==, "app/org.the.app/x86_64/master");
+ g_assert (flatpak_decomposed_equal (app_ref, app_ref));
+ g_assert (!flatpak_decomposed_equal (app_ref, runtime_ref));
+ g_assert (flatpak_decomposed_hash (app_ref) == g_str_hash ("app/org.the.app/x86_64/master"));
+ g_assert (flatpak_decomposed_is_app (app_ref));
+ g_assert (!flatpak_decomposed_is_runtime (app_ref));
+ g_assert (flatpak_decomposed_get_kind (app_ref) == FLATPAK_KINDS_APP);
+
+ g_assert_cmpstr (flatpak_decomposed_peek_id (app_ref), ==, "org.the.app/x86_64/master");
+ app_id = flatpak_decomposed_get_id (app_ref);
+ g_assert_cmpstr (app_id, ==, "org.the.app");
+ g_assert (flatpak_decomposed_is_id (app_ref, "org.the.app"));
+ g_assert (!flatpak_decomposed_is_id (app_ref, "org.the.ap"));
+ g_assert (!flatpak_decomposed_is_id (app_ref, "org.the.appp"));
+
+ g_assert_cmpstr (flatpak_decomposed_peek_arch (app_ref), ==, "x86_64/master");
+ app_arch = flatpak_decomposed_get_arch (app_ref);
+ g_assert_cmpstr (app_arch, ==, "x86_64");
+ g_assert (flatpak_decomposed_is_arch (app_ref, "x86_64"));
+ g_assert (!flatpak_decomposed_is_arch (app_ref, "x86_6"));
+ g_assert (!flatpak_decomposed_is_arch (app_ref, "x86_644"));
+
+ g_assert_cmpstr (flatpak_decomposed_peek_branch (app_ref), ==, "master");
+ app_branch = flatpak_decomposed_get_branch (app_ref);
+ g_assert_cmpstr (app_branch, ==, "master");
+ g_assert (flatpak_decomposed_is_branch (app_ref, "master"));
+ g_assert (!flatpak_decomposed_is_arch (app_ref, "maste"));
+ g_assert (!flatpak_decomposed_is_arch (app_ref, "masterr"));
+
+ refspec = flatpak_decomposed_new_from_ref ("remote:app/org.the.app/x86_64/master", &error);
+ g_assert (refspec == NULL);
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ refspec = flatpak_decomposed_new_from_refspec ("remote/broken:app/org.the.app/x86_64/master", &error);
+ g_assert (refspec == NULL);
+ g_assert (error != NULL);
+ g_assert (error->domain == FLATPAK_ERROR);
+ g_assert (error->code == FLATPAK_ERROR_INVALID_REF);
+ g_clear_error (&error);
+
+ refspec = flatpak_decomposed_new_from_refspec ("remote:app/org.the.app/x86_64/master", &error);
+ g_assert (refspec != NULL);
+ g_assert_null (error);
+
+ g_assert_cmpstr (flatpak_decomposed_peek_ref (refspec), ==, "app/org.the.app/x86_64/master");
+ g_assert_cmpstr (flatpak_decomposed_peek_refspec (refspec), ==, "remote:app/org.the.app/x86_64/master");
+ g_autofree char *refspec_remote = flatpak_decomposed_get_remote (refspec);
+ g_assert_cmpstr (refspec_remote, ==, "remote");
+ g_autofree char *refspec_ref = flatpak_decomposed_get_ref (refspec);
+ g_assert_cmpstr (refspec_ref, ==, "app/org.the.app/x86_64/master");
+ g_autofree char *refspec_refspec = flatpak_decomposed_get_refspec (refspec);
+ g_assert_cmpstr (refspec_refspec, ==, "remote:app/org.the.app/x86_64/master");
+}
+
+
typedef struct
{
const gchar *str;
@@ -1202,6 +1348,7 @@ main (int argc, char *argv[])
g_test_add_func ("/common/filter", test_filter);
g_test_add_func ("/common/dconf-app-id", test_dconf_app_id);
g_test_add_func ("/common/dconf-paths", test_dconf_paths);
+ g_test_add_func ("/common/decompose-ref", test_decompose);
g_test_add_func ("/app/looks-like-branch", test_looks_like_branch);
g_test_add_func ("/app/columns", test_columns);