Merge branch 'backport-4033-girepository-typelib-determinicity-glib-2-80' into 'glib-2-80'

Backport !4033 “girepository: Keep an ordered list of the loaded typelibs” to glib-2-80

See merge request GNOME/glib!4080
diff --git a/girepository/girepository.c b/girepository/girepository.c
index 24d83ca..0cf6da1 100644
--- a/girepository/girepository.c
+++ b/girepository/girepository.c
@@ -65,6 +65,23 @@
  * The environment variable takes precedence over the default search path
  * and the [method@GIRepository.Repository.prepend_search_path] calls.
  *
+ * ### Namespace ordering
+ *
+ * In situations where namespaces may be searched in order, or returned in a
+ * list, the namespaces will be returned in alphabetical order, with all fully
+ * loaded namespaces being returned before any lazily loaded ones (those loaded
+ * with `GI_REPOSITORY_LOAD_FLAG_LAZY`). This allows for deterministic and
+ * reproducible results.
+ *
+ * Similarly, if a symbol (such as a `GType` or error domain) is being searched
+ * for in the set of loaded namespaces, the namespaces will be searched in that
+ * order. In particular, this means that a symbol which exists in two namespaces
+ * will always be returned from the alphabetically-higher namespace. This should
+ * only happen in the case of `Gio` and `GioUnix`/`GioWin32`, which all refer to
+ * the same `.so` file and expose overlapping sets of symbols. Symbols should
+ * always end up being resolved to `GioUnix` or `GioWin32` if they are platform
+ * dependent, rather than `Gio` itself.
+ *
  * Since: 2.80
  */
 
@@ -98,8 +115,14 @@
   GPtrArray *typelib_search_path;  /* (element-type filename) (owned) */
   GPtrArray *library_paths;  /* (element-type filename) (owned) */
 
+  /* Certain operations require iterating over the typelibs and the iteration
+   * order may affect the results. So keep an ordered list of the typelibs,
+   * alongside the hash table which keep the canonical strong reference to them. */
   GHashTable *typelibs; /* (string) namespace -> GITypelib */
+  GPtrArray *ordered_typelibs;  /* (element-type unowned GITypelib) (owned) (not nullable) */
   GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */
+  GPtrArray *ordered_lazy_typelibs;  /* (element-type unowned GITypelib) (owned) (not nullable) */
+
   GHashTable *info_by_gtype; /* GType -> GIBaseInfo */
   GHashTable *info_by_error_domain; /* GQuark -> GIBaseInfo */
   GHashTable *interfaces_for_gtype; /* GType -> GTypeInterfaceCache */
@@ -187,10 +210,13 @@
     = g_hash_table_new_full (g_str_hash, g_str_equal,
                              (GDestroyNotify) g_free,
                              (GDestroyNotify) gi_typelib_unref);
+  repository->ordered_typelibs = g_ptr_array_new_with_free_func (NULL);
   repository->lazy_typelibs
     = g_hash_table_new_full (g_str_hash, g_str_equal,
                              (GDestroyNotify) g_free,
                              (GDestroyNotify) gi_typelib_unref);
+  repository->ordered_lazy_typelibs = g_ptr_array_new_with_free_func (NULL);
+
   repository->info_by_gtype
     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                              (GDestroyNotify) NULL,
@@ -212,7 +238,10 @@
   GIRepository *repository = GI_REPOSITORY (object);
 
   g_hash_table_destroy (repository->typelibs);
+  g_ptr_array_unref (repository->ordered_typelibs);
   g_hash_table_destroy (repository->lazy_typelibs);
+  g_ptr_array_unref (repository->ordered_lazy_typelibs);
+
   g_hash_table_destroy (repository->info_by_gtype);
   g_hash_table_destroy (repository->info_by_error_domain);
   g_hash_table_destroy (repository->interfaces_for_gtype);
@@ -497,6 +526,29 @@
   return TRUE;
 }
 
+/* Sort typelibs by namespace. The main requirement here is just to make iteration
+ * deterministic, otherwise results can change as a lot of the code here would
+ * just iterate over a `GHashTable`.
+ *
+ * A sub-requirement of this is that namespaces are sorted such that if a GType
+ * or symbol is found in multiple namespaces where one is a prefix of the other,
+ * the longest namespace wins. In practice, this only happens in
+ * Gio/GioUnix/GioWin32, as all three of those namespaces refer to the same
+ * `.so` file and overlapping sets of the same symbols, but we want the platform
+ * specific namespace to be returned in preference to anything else (even though
+ * either namespace is valid).
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/3303 */
+static int
+sort_typelibs_cb (const void *a,
+                  const void *b)
+{
+  GITypelib *typelib_a = *(GITypelib **) a;
+  GITypelib *typelib_b = *(GITypelib **) b;
+
+  return strcmp (gi_typelib_get_namespace (typelib_a),
+                 gi_typelib_get_namespace (typelib_b));
+}
+
 static const char *
 register_internal (GIRepository *repository,
                    const char   *source,
@@ -521,6 +573,8 @@
                                       namespace));
       g_hash_table_insert (repository->lazy_typelibs,
                            build_typelib_key (namespace, source), gi_typelib_ref (typelib));
+      g_ptr_array_add (repository->ordered_lazy_typelibs, typelib);
+      g_ptr_array_sort (repository->ordered_lazy_typelibs, sort_typelibs_cb);
     }
   else
     {
@@ -535,13 +589,20 @@
       if (g_hash_table_lookup_extended (repository->lazy_typelibs,
                                         namespace,
                                         (gpointer)&key, &value))
-        g_hash_table_remove (repository->lazy_typelibs, key);
+        {
+          g_hash_table_remove (repository->lazy_typelibs, key);
+          g_ptr_array_remove (repository->ordered_lazy_typelibs, typelib);
+        }
       else
-        key = build_typelib_key (namespace, source);
+        {
+          key = build_typelib_key (namespace, source);
+        }
 
       g_hash_table_insert (repository->typelibs,
                            g_steal_pointer (&key),
                            gi_typelib_ref (typelib));
+      g_ptr_array_add (repository->ordered_typelibs, typelib);
+      g_ptr_array_sort (repository->ordered_typelibs, sort_typelibs_cb);
     }
 
   /* These types might be resolved now, clear the cache */
@@ -870,32 +931,29 @@
                            NULL, typelib, entry->offset);
 }
 
-typedef struct {
-  const char *gtype_name;
-  GITypelib *result_typelib;
-} FindByGTypeData;
-
 static DirEntry *
-find_by_gtype (GHashTable *table, FindByGTypeData *data, gboolean check_prefix)
+find_by_gtype (GPtrArray   *ordered_table,
+               const char  *gtype_name,
+               gboolean     check_prefix,
+               GITypelib  **out_result_typelib)
 {
-  GHashTableIter iter;
-  gpointer key, value;
-  DirEntry *ret;
-
-  g_hash_table_iter_init (&iter, table);
-  while (g_hash_table_iter_next (&iter, &key, &value))
+  /* Search in reverse order as the longest namespaces will be listed last, and
+   * those are the ones we want to search first. */
+  for (guint i = ordered_table->len; i > 0; i--)
     {
-      GITypelib *typelib = (GITypelib*)value;
+      GITypelib *typelib = g_ptr_array_index (ordered_table, i - 1);
+      DirEntry *ret;
+
       if (check_prefix)
         {
-          if (!gi_typelib_matches_gtype_name_prefix (typelib, data->gtype_name))
+          if (!gi_typelib_matches_gtype_name_prefix (typelib, gtype_name))
             continue;
         }
 
-      ret = gi_typelib_get_dir_entry_by_gtype_name (typelib, data->gtype_name);
+      ret = gi_typelib_get_dir_entry_by_gtype_name (typelib, gtype_name);
       if (ret)
         {
-          data->result_typelib = typelib;
+          *out_result_typelib = typelib;
           return ret;
         }
     }
@@ -924,7 +982,8 @@
 gi_repository_find_by_gtype (GIRepository *repository,
                              GType         gtype)
 {
-  FindByGTypeData data;
+  const char *gtype_name;
+  GITypelib *result_typelib = NULL;
   GIBaseInfo *cached;
   DirEntry *entry;
 
@@ -940,8 +999,7 @@
   if (g_hash_table_contains (repository->unknown_gtypes, (gpointer)gtype))
     return NULL;
 
-  data.gtype_name = g_type_name (gtype);
-  data.result_typelib = NULL;
+  gtype_name = g_type_name (gtype);
 
   /* Inside each typelib, we include the "C prefix" which acts as
    * a namespace mechanism.  For GtkTreeView, the C prefix is Gtk.
@@ -950,25 +1008,25 @@
    * target type does not have this typelib's C prefix. Use this
    * assumption as our first attempt at locating the DirEntry.
    */
-  entry = find_by_gtype (repository->typelibs, &data, TRUE);
+  entry = find_by_gtype (repository->ordered_typelibs, gtype_name, TRUE, &result_typelib);
   if (entry == NULL)
-    entry = find_by_gtype (repository->lazy_typelibs, &data, TRUE);
+    entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, TRUE, &result_typelib);
 
-  /* Not ever class library necessarily specifies a correct c_prefix,
+  /* Not every class library necessarily specifies a correct c_prefix,
    * so take a second pass. This time we will try a global lookup,
    * ignoring prefixes.
    * See http://bugzilla.gnome.org/show_bug.cgi?id=564016
    */
   if (entry == NULL)
-    entry = find_by_gtype (repository->typelibs, &data, FALSE);
+    entry = find_by_gtype (repository->ordered_typelibs, gtype_name, FALSE, &result_typelib);
   if (entry == NULL)
-    entry = find_by_gtype (repository->lazy_typelibs, &data, FALSE);
+    entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, FALSE, &result_typelib);
 
   if (entry != NULL)
     {
       cached = gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
                                  repository,
-                                 NULL, data.result_typelib, entry->offset);
+                                 NULL, result_typelib, entry->offset);
 
       g_hash_table_insert (repository->info_by_gtype,
                            (gpointer) gtype,
@@ -1020,28 +1078,27 @@
                            NULL, typelib, entry->offset);
 }
 
-typedef struct {
-  GIRepository *repository;
-  GQuark domain;
-
-  GITypelib *result_typelib;
-  DirEntry *result;
-} FindByErrorDomainData;
-
-static void
-find_by_error_domain_foreach (gpointer key,
-                              gpointer value,
-                              gpointer datap)
+static DirEntry *
+find_by_error_domain (GPtrArray  *ordered_typelibs,
+                      GQuark      target_domain,
+                      GITypelib **out_typelib)
 {
-  GITypelib *typelib = (GITypelib*)value;
-  FindByErrorDomainData *data = datap;
+  /* Search in reverse order as the longest namespaces will be listed last, and
+   * those are the ones we want to search first. */
+  for (guint i = ordered_typelibs->len; i > 0; i--)
+    {
+      GITypelib *typelib = g_ptr_array_index (ordered_typelibs, i - 1);
+      DirEntry *entry;
 
-  if (data->result != NULL)
-    return;
+      entry = gi_typelib_get_dir_entry_by_error_domain (typelib, target_domain);
+      if (entry != NULL)
+        {
+          *out_typelib = typelib;
+          return entry;
+        }
+    }
 
-  data->result = gi_typelib_get_dir_entry_by_error_domain (typelib, data->domain);
-  if (data->result)
-    data->result_typelib = typelib;
+  return NULL;
 }
 
 /**
@@ -1064,8 +1121,9 @@
 gi_repository_find_by_error_domain (GIRepository *repository,
                                     GQuark        domain)
 {
-  FindByErrorDomainData data;
   GIEnumInfo *cached;
+  DirEntry *result = NULL;
+  GITypelib *result_typelib = NULL;
 
   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 
@@ -1075,20 +1133,15 @@
   if (cached != NULL)
     return (GIEnumInfo *) gi_base_info_ref ((GIBaseInfo *)cached);
 
-  data.repository = repository;
-  data.domain = domain;
-  data.result_typelib = NULL;
-  data.result = NULL;
+  result = find_by_error_domain (repository->ordered_typelibs, domain, &result_typelib);
+  if (result == NULL)
+    result = find_by_error_domain (repository->ordered_lazy_typelibs, domain, &result_typelib);
 
-  g_hash_table_foreach (repository->typelibs, find_by_error_domain_foreach, &data);
-  if (data.result == NULL)
-    g_hash_table_foreach (repository->lazy_typelibs, find_by_error_domain_foreach, &data);
-
-  if (data.result != NULL)
+  if (result != NULL)
     {
-      cached = (GIEnumInfo *) gi_info_new_full (gi_typelib_blob_type_to_info_type (data.result->blob_type),
+      cached = (GIEnumInfo *) gi_info_new_full (gi_typelib_blob_type_to_info_type (result->blob_type),
                                                 repository,
-                                                NULL, data.result_typelib, data.result->offset);
+                                                NULL, result_typelib, result->offset);
 
       g_hash_table_insert (repository->info_by_error_domain,
                            GUINT_TO_POINTER (domain),
@@ -1179,13 +1232,16 @@
 }
 
 static void
-collect_namespaces (gpointer key,
-                    gpointer value,
-                    gpointer data)
+collect_namespaces (GPtrArray  *ordered_typelibs,
+                    char      **names,
+                    size_t     *inout_i)
 {
-  GList **list = data;
-
-  *list = g_list_append (*list, key);
+  for (guint j = 0; j < ordered_typelibs->len; j++)
+    {
+      GITypelib *typelib = g_ptr_array_index (ordered_typelibs, j);
+      const char *namespace = gi_typelib_get_namespace (typelib);
+      names[(*inout_i)++] = g_strdup (namespace);
+    }
 }
 
 /**
@@ -1207,20 +1263,18 @@
 gi_repository_get_loaded_namespaces (GIRepository *repository,
                                      size_t       *n_namespaces_out)
 {
-  GList *l, *list = NULL;
   char **names;
   size_t i;
+  size_t n_typelibs;
 
   g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
 
-  g_hash_table_foreach (repository->typelibs, collect_namespaces, &list);
-  g_hash_table_foreach (repository->lazy_typelibs, collect_namespaces, &list);
-
-  names = g_malloc0 (sizeof (char *) * (g_list_length (list) + 1));
+  n_typelibs = repository->ordered_typelibs->len + repository->ordered_lazy_typelibs->len;
+  names = g_malloc0 (sizeof (char *) * (n_typelibs + 1));
   i = 0;
-  for (l = list; l; l = l->next)
-    names[i++] = g_strdup (l->data);
-  g_list_free (list);
+
+  collect_namespaces (repository->ordered_typelibs, names, &i);
+  collect_namespaces (repository->ordered_lazy_typelibs, names, &i);
 
   if (n_namespaces_out != NULL)
     *n_namespaces_out = i;
diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c
index 3c88a79..57411a8 100644
--- a/girepository/gitypelib.c
+++ b/girepository/gitypelib.c
@@ -316,7 +316,8 @@
     }
   else
     {
-      g_string_overwrite_len (&iter->buf, 0, s, (gssize)len);
+      g_string_overwrite_len (&iter->buf, 0, s, (gssize)len + 1);
+      iter->buf.str[len] = '\0';
       *out_val = iter->buf.str;
     }
   return TRUE;
diff --git a/girepository/introspection/meson.build b/girepository/introspection/meson.build
index b23d048..f3ee28f 100644
--- a/girepository/introspection/meson.build
+++ b/girepository/introspection/meson.build
@@ -261,6 +261,7 @@
       '--identifier-prefix=GWin32'
     ],
   )
+  gio_platform_gir = gio_win32_gir
 else
   gio_unix_gir_c_includes = []
   foreach h: gio_unix_include_headers
@@ -289,6 +290,7 @@
       '--identifier-prefix=GUnix'
     ],
   )
+  gio_platform_gir = gio_unix_gir
 endif
 
 # GIRepository
diff --git a/girepository/tests/meson.build b/girepository/tests/meson.build
index a8e5b3d..6fa947c 100644
--- a/girepository/tests/meson.build
+++ b/girepository/tests/meson.build
@@ -29,6 +29,11 @@
     gio_gir,
   ]
 
+  gio_platform_gir_testing_dep = [
+    gio_gir_testing_dep,
+    gio_platform_gir,
+  ]
+
   girepository_gir_testing_dep = [
     gio_gir_testing_dep,
     girepository_gir,
@@ -46,7 +51,8 @@
       'depends': gobject_gir_testing_dep,
     },
     'repository' : {
-      'depends': gio_gir_testing_dep,
+      'depends': [gio_gir_testing_dep, gio_platform_gir_testing_dep],
+      'dependencies': [libgio_dep],
     },
     'repository-search-paths' : {
       'c_args': '-DGOBJECT_INTROSPECTION_LIBDIR="@0@"'.format(glib_libdir),
diff --git a/girepository/tests/repository.c b/girepository/tests/repository.c
index 5650231..c79b0df 100644
--- a/girepository/tests/repository.c
+++ b/girepository/tests/repository.c
@@ -35,6 +35,12 @@
 #include "glib.h"
 #include "test-common.h"
 
+#if defined(G_OS_UNIX)
+#include "gio/gdesktopappinfo.h"
+#elif defined(G_OS_WIN32)
+#include "gio/gwin32inputstream.h"
+#endif
+
 static void
 test_repository_basic (RepositoryFixture *fx,
                        const void *unused)
@@ -507,12 +513,31 @@
 
   g_test_summary ("Test finding an error quark by error domain");
 
+  /* Find a simple error domain. */
   error_info = gi_repository_find_by_error_domain (fx->repository, G_RESOLVER_ERROR);
   g_assert_nonnull (error_info);
   g_assert_true (GI_IS_ENUM_INFO (error_info));
   g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (error_info)), ==, "ResolverError");
 
   g_clear_pointer (&error_info, gi_base_info_unref);
+
+  /* Find again to check the caching. */
+  error_info = gi_repository_find_by_error_domain (fx->repository, G_RESOLVER_ERROR);
+  g_assert_nonnull (error_info);
+  g_assert_true (GI_IS_ENUM_INFO (error_info));
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (error_info)), ==, "ResolverError");
+
+  g_clear_pointer (&error_info, gi_base_info_unref);
+
+  /* Try and find an unknown error domain. */
+  g_assert_null (gi_repository_find_by_error_domain (fx->repository, GI_REPOSITORY_ERROR));
+
+  /* And check caching for unknown error domains. */
+  g_assert_null (gi_repository_find_by_error_domain (fx->repository, GI_REPOSITORY_ERROR));
+
+  /* It would be good to try and find one which will resolve in both Gio and
+   * GioUnix/GioWin32, but neither of the platform-specific GIRs actually define
+   * any error domains at the moment. */
 }
 
 static void
@@ -777,6 +802,96 @@
   g_clear_pointer (&invoker_info, gi_base_info_unref);
 }
 
+static void
+test_repository_find_by_gtype (RepositoryFixture *fx,
+                               const void        *unused)
+{
+  GIObjectInfo *object_info = NULL;
+
+  g_test_summary ("Test finding a GType");
+
+  object_info = (GIObjectInfo *) gi_repository_find_by_gtype (fx->repository, G_TYPE_OBJECT);
+  g_assert_nonnull (object_info);
+  g_assert_true (GI_IS_OBJECT_INFO (object_info));
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (object_info)), ==, "Object");
+
+  g_clear_pointer (&object_info, gi_base_info_unref);
+
+  /* Find it again; this time it should hit the cache. */
+  object_info = (GIObjectInfo *) gi_repository_find_by_gtype (fx->repository, G_TYPE_OBJECT);
+  g_assert_nonnull (object_info);
+  g_assert_true (GI_IS_OBJECT_INFO (object_info));
+  g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (object_info)), ==, "Object");
+
+  g_clear_pointer (&object_info, gi_base_info_unref);
+
+  /* Try and find an unknown GType. */
+  g_assert_null (gi_repository_find_by_gtype (fx->repository, GI_TYPE_BASE_INFO));
+
+  /* And check caching for unknown GTypes. */
+  g_assert_null (gi_repository_find_by_gtype (fx->repository, GI_TYPE_BASE_INFO));
+
+  /* Now try and find one which will resolve in both Gio and GioUnix/GioWin32.
+   * The longest-named typelib should be returned. */
+  {
+    GType platform_specific_type;
+    const char *expected_name, *expected_namespace;
+
+#if defined(G_OS_UNIX)
+    platform_specific_type = G_TYPE_DESKTOP_APP_INFO;
+    expected_name = "DesktopAppInfo";
+    expected_namespace = "GioUnix";
+#elif defined(G_OS_WIN32)
+    platform_specific_type = G_TYPE_WIN32_INPUT_STREAM;
+    expected_name = "InputStream";
+    expected_namespace = "GioWin32";
+#else
+    platform_specific_type = G_TYPE_INVALID;
+    expected_name = NULL;
+    expected_namespace = NULL;
+#endif
+
+    if (expected_name != NULL)
+      {
+        object_info = (GIObjectInfo *) gi_repository_find_by_gtype (fx->repository, platform_specific_type);
+        g_assert_nonnull (object_info);
+        g_assert_true (GI_IS_OBJECT_INFO (object_info));
+        g_assert_cmpstr (gi_base_info_get_name (GI_BASE_INFO (object_info)), ==, expected_name);
+        g_assert_cmpstr (gi_base_info_get_namespace (GI_BASE_INFO (object_info)), ==, expected_namespace);
+        g_clear_pointer (&object_info, gi_base_info_unref);
+      }
+  }
+}
+
+static void
+test_repository_loaded_namespaces (RepositoryFixture *fx,
+                                   const void        *unused)
+{
+  char **namespaces;
+  size_t n_namespaces;
+
+  /* These should be in alphabetical order */
+#if defined(G_OS_UNIX)
+  const char *expected_namespaces[] = { "GLib", "GModule", "GObject", "Gio", "GioUnix", NULL };
+#elif defined(G_OS_WIN32)
+  const char *expected_namespaces[] = { "GLib", "GModule", "GObject", "Gio", "GioWin32", NULL };
+#else
+  const char *expected_namespaces[] = { "GLib", "GModule", "GObject", "Gio", NULL };
+#endif
+
+  g_test_summary ("Test listing loaded namespaces");
+
+  namespaces = gi_repository_get_loaded_namespaces (fx->repository, &n_namespaces);
+  g_assert_cmpstrv (namespaces, expected_namespaces);
+  g_assert_cmpuint (n_namespaces, ==, g_strv_length ((char **) expected_namespaces));
+  g_strfreev (namespaces);
+
+  /* Test again but without passing `n_namespaces`. */
+  namespaces = gi_repository_get_loaded_namespaces (fx->repository, NULL);
+  g_assert_cmpstrv (namespaces, expected_namespaces);
+  g_strfreev (namespaces);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -794,7 +909,7 @@
   ADD_REPOSITORY_TEST ("/repository/constructor-return-type", test_repository_constructor_return_type, &typelib_load_spec_gobject);
   ADD_REPOSITORY_TEST ("/repository/enum-info-c-identifier", test_repository_enum_info_c_identifier, &typelib_load_spec_glib);
   ADD_REPOSITORY_TEST ("/repository/enum-info-static-methods", test_repository_enum_info_static_methods, &typelib_load_spec_glib);
-  ADD_REPOSITORY_TEST ("/repository/error-quark", test_repository_error_quark, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/error-quark", test_repository_error_quark, &typelib_load_spec_gio_platform);
   ADD_REPOSITORY_TEST ("/repository/flags-info-c-identifier", test_repository_flags_info_c_identifier, &typelib_load_spec_gobject);
   ADD_REPOSITORY_TEST ("/repository/fundamental-ref-func", test_repository_fundamental_ref_func, &typelib_load_spec_gobject);
   ADD_REPOSITORY_TEST ("/repository/instance-method-ownership-transfer", test_repository_instance_method_ownership_transfer, &typelib_load_spec_gio);
@@ -804,6 +919,8 @@
   ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-no-invoker", test_repository_vfunc_info_with_no_invoker, &typelib_load_spec_gobject);
   ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-invoker-on-interface", test_repository_vfunc_info_with_invoker_on_interface, &typelib_load_spec_gio);
   ADD_REPOSITORY_TEST ("/repository/vfunc-info-with-invoker-on-object", test_repository_vfunc_info_with_invoker_on_object, &typelib_load_spec_gio);
+  ADD_REPOSITORY_TEST ("/repository/find-by-gtype", test_repository_find_by_gtype, &typelib_load_spec_gio_platform);
+  ADD_REPOSITORY_TEST ("/repository/loaded-namespaces", test_repository_loaded_namespaces, &typelib_load_spec_gio_platform);
 
   return g_test_run ();
 }
diff --git a/girepository/tests/test-common.h b/girepository/tests/test-common.h
index 9d31998..e7340c6 100644
--- a/girepository/tests/test-common.h
+++ b/girepository/tests/test-common.h
@@ -36,6 +36,11 @@
 static const TypelibLoadSpec typelib_load_spec_glib = { "GLib", "2.0" };
 static const TypelibLoadSpec typelib_load_spec_gobject = { "GObject", "2.0" };
 static const TypelibLoadSpec typelib_load_spec_gio = { "Gio", "2.0" };
+#if defined(G_OS_UNIX)
+static const TypelibLoadSpec typelib_load_spec_gio_platform = { "GioUnix", "2.0" };
+#elif defined(G_OS_WIN32)
+static const TypelibLoadSpec typelib_load_spec_gio_platform = { "GioWin32", "2.0" };
+#endif
 
 void repository_init (int *argc,
                       char **argv[]);