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[]);