girepository: Make gi_repository_find_by_gtype() deterministic

When faced with a `GType` which is present in multiple typelibs, the old
implementation was not deterministic, as it iterated over a hash table
of typelibs. The iteration order of a hash table is not deterministic.

Use the new `ordered_typelibs` and `ordered_lazy_typelibs` arrays to
iterate instead, making the order deterministic.

Add a unit test to check this. In particular, to check that symbols
which are present in both `Gio` and `GioUnix` are correctly resolved as
being from `GioUnix`.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Helps: #3303
This commit is contained in:
Philip Withnall
2024-05-02 16:03:24 +01:00
parent 14d196a6c1
commit 81b16a88d1
5 changed files with 103 additions and 25 deletions

View File

@@ -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)
@@ -777,6 +783,67 @@ test_repository_vfunc_info_with_invoker_on_object (RepositoryFixture *fx,
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);
}
}
}
int
main (int argc,
char *argv[])
@@ -804,6 +871,7 @@ main (int argc,
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);
return g_test_run ();
}