diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2022-11-07 19:28:13 +0100 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2022-11-08 13:14:56 +0100 |
commit | 133540763cd0a2208cb41fff18fe7d99716ca4aa (patch) | |
tree | 7cbf24237bd6466eb344961a212f559489c3bbb0 | |
parent | e13eb9010c10432354d0f45191a4d8a95905b084 (diff) |
libnm: test that Gir data matches actual exportslr/unbreak-gir
This verifies that what's in our public headers has version nodes, and
that they match Since: tags.
Not pretty (because python) but discovered a *lot* of issues.
-rw-r--r-- | Makefile.am | 11 | ||||
-rw-r--r-- | src/libnm-client-impl/meson.build | 1 | ||||
-rw-r--r-- | src/libnm-client-impl/tests/meson.build | 12 | ||||
-rwxr-xr-x | src/libnm-client-impl/tests/test-gir.py | 153 |
4 files changed, 175 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index 7c003df637..e3cc290b93 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2109,8 +2109,16 @@ src_libnm_client_impl_tests_programs_req_introspection = \ src/libnm-client-impl/tests/test-remote-settings-client \ src/libnm-client-impl/tests/test-secret-agent +check-local-libnm-gir: $(builddir)/src/libnm-client-impl/NM-1.0.gir +check-local-libnm-gir: $(srcdir)/src/libnm-client-impl/libnm.ver +check-local-libnm-gir: + "$(PYTHON)" $(srcdir)/src/libnm-client-impl/tests/test-gir.py \ + --gir $(builddir)/src/libnm-client-impl/NM-1.0.gir \ + --ver $(srcdir)/src/libnm-client-impl/libnm.ver + if HAVE_INTROSPECTION check_programs += $(src_libnm_client_impl_tests_programs_req_introspection) +check_local += check-local-libnm-gir else check_programs_norun += $(src_libnm_client_impl_tests_programs_req_introspection) endif @@ -2164,7 +2172,8 @@ $(src_libnm_client_impl_tests_test_remote_settings_client_OBJECTS): $(src_libnm_ $(src_libnm_client_impl_tests_test_secret_agent_OBJECTS): $(src_libnm_client_impl_NM_1_0_typelib) EXTRA_DIST += \ - src/libnm-client-impl/tests/meson.build + src/libnm-client-impl/tests/meson.build \ + src/libnm-client-impl/tests/test-gir.py ############################################################################### diff --git a/src/libnm-client-impl/meson.build b/src/libnm-client-impl/meson.build index 46464a6328..d72ed545b4 100644 --- a/src/libnm-client-impl/meson.build +++ b/src/libnm-client-impl/meson.build @@ -236,5 +236,4 @@ if enable_introspection ], depends: libnm_gir, ) - endif diff --git a/src/libnm-client-impl/tests/meson.build b/src/libnm-client-impl/tests/meson.build index 3f2e78a74f..0c0e188b77 100644 --- a/src/libnm-client-impl/tests/meson.build +++ b/src/libnm-client-impl/tests/meson.build @@ -44,3 +44,15 @@ foreach test_unit: test_units args: test_args + [exe.full_path()], ) endforeach + +if enable_introspection + test( + 'check-local-libnm-gir', + python, + args: [ + join_paths(meson.source_root(), 'src', 'libnm-client-impl', 'tests', 'test-gir.py'), + '--gir', libnm_gir[0], + '--ver', join_paths(meson.source_root(), 'src', 'libnm-client-impl', 'libnm.ver'), + ], + ) +endif diff --git a/src/libnm-client-impl/tests/test-gir.py b/src/libnm-client-impl/tests/test-gir.py new file mode 100755 index 0000000000..d91849b8fe --- /dev/null +++ b/src/libnm-client-impl/tests/test-gir.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2022 Red Hat, Inc. +# + +from __future__ import print_function +import xml.etree.ElementTree as ET +import argparse +import sys + +C_NS = "http://www.gtk.org/introspection/c/1.0" +CORE_NS = "http://www.gtk.org/introspection/core/1.0" +GLIB_NS = "http://www.gtk.org/introspection/glib/1.0" + + +def syms_from_gir(girfile): + def xml_symbols(xml, types): + ret = [] + for t in types: + ret += xml.findall("./{%s}namespace/{%s}%s" % (CORE_NS, CORE_NS, t)) + ret += xml.findall("./{%s}namespace/*/{%s}%s" % (CORE_NS, CORE_NS, t)) + return ret + + girxml = ET.parse(girfile) + c_syms = {} + for sym in xml_symbols(girxml, ("constructor", "function", "method")): + c_syms[sym.get("{%s}identifier" % C_NS)] = sym.get("version") + + for sym in xml_symbols( + girxml, ("bitfield", "class", "enumeration", "interface", "record") + ): + get_type = sym.get("{%s}get-type" % GLIB_NS) + if get_type is None: + continue + version = sym.get("version") + + if version is None: + # FIXME: The get_type() functions should be exported in the same + # version the type itself appeared. However, a large number of + # classes lack Since: tags in their doc blocks. Fall back to using + # the tag on _new() method for the test to be able to proceed + # reasonably. This should be fixed eventually. + constructor = sym.find("./{%s}constructor" % CORE_NS) + if constructor is not None: + version = constructor.get("version") + + c_syms[get_type] = version + return c_syms + + +# Older Python doesn't have str.removesuffix() +def str_removesuffix(string, suffix): + try: + return string.removesuffix(suffix) + except AttributeError: + if string.endswith(suffix): + return string[: -len(suffix)] + else: + return string + + +# Older Python doesn't have str.removeprefix() +def str_removeprefix(string, prefix): + try: + return string.removeprefix(prefix) + except AttributeError: + if string.startswith(prefix): + return string[len(prefix) :] + else: + return string + + +def syms_from_ver(verfile): + c_syms = {} + for line in open(verfile).readlines(): + line = line.strip() + + if line.endswith("{"): + line = str_removesuffix(line, " {") + line = str_removeprefix(line, "libnm_") + (major, minor, micro) = line.split("_") + if int(major) > 1 or int(minor) > 0: + if int(micro) > 0: + # Snap to next major version. Perhaps not + # exactly correct, but good for all symbols + # we export but nm_ethtool_optname_is_feature(). + minor = str(int(minor) + 2) + version = major + "." + minor + else: + version = None + elif ( + line.endswith(";") + and not line.startswith("}") + and not line.startswith("#") + and not line == "*;" + ): + c_syms[str_removesuffix(line, ";")] = version + + # This one is... messy. + c_syms["nm_ethtool_optname_is_feature"] = "1.20" + + return c_syms + + +parser = argparse.ArgumentParser() +parser.add_argument( + "--gir", + metavar="FILE", + help="NM-1.0.gir file", + required=True, +) +parser.add_argument( + "--ver", + metavar="FILE", + help="libnm.ver file", + required=True, +) + +args = parser.parse_args() + +gir_syms = syms_from_gir(args.gir) +ver_syms = syms_from_ver(args.ver) + +exit_code = 0 + +for (gir_sym, gir_ver) in gir_syms.items(): + if gir_sym not in ver_syms: + exit_code = 1 + print( + 'FAIL: "%s" found in "%s", but is not exported. Needs adding to "%s"?' + % (gir_sym, args.gir, args.ver), + file=sys.stderr, + ) + continue + if gir_ver != ver_syms[gir_sym]: + exit_code = 1 + print( + 'FAIL: "%s" exported in version "%s" but documented as available since "%s"' + % (gir_sym, ver_syms[gir_sym], gir_ver), + file=sys.stderr, + ) + +# In python2, dict.keys() returns lists, not sets. Cast them. +for sym in set(ver_syms.keys()) - set(gir_syms.keys()): + exit_code = 1 + print( + 'FAIL: "%s" found in "%s", but not in "%s". Maybe the doc comment is wrong or g-ir-scanner messed up?' + % (sym, args.ver, args.gir), + file=sys.stderr, + ) + +sys.exit(exit_code) |