summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2021-04-20 13:01:20 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2022-01-20 15:18:07 +1000
commitd92310acd6ac9a1dfdfe46c2694996c4e7ad679d (patch)
tree41bc59876b8b8e6090892b6c62c99bfddd1edacd
parenta1ed3ec6d24d6511379350bfa6d972f5e3073bb5 (diff)
downloadxkeyboard-config-d92310acd6ac9a1dfdfe46c2694996c4e7ad679d.tar.gz
test: add a test case for ISO codes
Some languages need to be special-cased, pycountry doesn't list them. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--tests/test_rules_xml.py108
2 files changed, 108 insertions, 4 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 528c702..4614bd0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -26,7 +26,7 @@ variables:
# Changing the tag will rebuild the container images. The value is just a
# string, but we use the date for human benefits.
- FDO_DISTRIBUTION_TAG: '2021-12-17.1'
+ FDO_DISTRIBUTION_TAG: '2022-01-20.0'
stages:
@@ -46,7 +46,7 @@ container-prep:
# minimal set of packages required to build xkeyboard-config.
BASE_PACKAGES: 'xorg-util-macros gettext pkg-config gcc grep m4 python meson ninja git diffutils'
# extra packages we need for various tests
- EXTRA_PACKAGES: 'tree libxml2 bison xorg-xkbcomp python-pytest python-libevdev python-yaml yq libxkbcommon libxslt'
+ EXTRA_PACKAGES: 'tree libxml2 bison xorg-xkbcomp python-pytest python-libevdev python-yaml yq libxkbcommon libxslt python-pycountry'
FDO_DISTRIBUTION_PACKAGES: $BASE_PACKAGES $EXTRA_PACKAGES
diff --git a/tests/test_rules_xml.py b/tests/test_rules_xml.py
index b833a08..85ad717 100644
--- a/tests/test_rules_xml.py
+++ b/tests/test_rules_xml.py
@@ -23,14 +23,72 @@ def xkb_config_root():
return _xkb_config_root()
+def iterate_layouts_variants(rules_xml):
+ '''
+ Return an iterator of type (layout, variant) for each element in the XML
+ file.
+ '''
+ tree = ET.parse(rules_xml)
+ root = tree.getroot()
+ for layout in root.iter('layout'):
+ yield layout, None
+
+ for variant in layout.iter('variant'):
+ yield layout, variant
+
+
def pytest_generate_tests(metafunc):
# for any test_foo function with an argument named rules_xml,
# make it the list of XKB_CONFIG_ROOT/rules/*.xml files.
if 'rules_xml' in metafunc.fixturenames:
-
rules_xml = list(_xkb_config_root().glob('rules/*.xml'))
assert rules_xml
metafunc.parametrize('rules_xml', rules_xml)
+ # for any test_foo function with an argument named layout,
+ # make it a Layout wrapper class for all layout(variant) combinations
+ elif 'layout' in metafunc.fixturenames:
+ rules_xml = list(_xkb_config_root().glob('rules/*.xml'))
+ assert rules_xml
+ layouts = []
+ for f in rules_xml:
+ for l, v in iterate_layouts_variants(f):
+ layouts.append(Layout(f, l, v))
+ metafunc.parametrize('layout', layouts)
+
+
+
+class Layout:
+ '''
+ Wrapper class for layout/variants - both ConfigItems are available but
+ the properties automatically pick the variant (if it exists) or the
+ layout otherwise.
+ '''
+ def __init__(self, rulesfile, layout, variant=None):
+ self.rulesfile = rulesfile
+ self.layout = ConfigItem.from_elem(layout)
+ self.variant = ConfigItem.from_elem(variant) if variant else None
+ if variant:
+ self.name = f"{self.layout.name}({self.variant.name})"
+ else:
+ self.name = f"{self.layout.name}"
+
+ def _fetch(self, name):
+ parent = self.variant or self.layout
+ elements = parent.findall(name)
+ if elements is None:
+ return None
+ elif len(elements) > 1:
+ return elements
+ else:
+ return elements[0]
+
+ @property
+ def iso3166(self):
+ return (self.variant or self.layout).iso3166
+
+ @property
+ def iso639(self):
+ return (self.variant or self.layout).iso639
def prettyxml(element):
@@ -42,6 +100,8 @@ class ConfigItem:
self.name = name
self.shortDescription = shortDescription
self.description = description
+ self.iso639 = []
+ self.iso3166 = []
@classmethod
def _fetch_subelement(cls, parent, name):
@@ -52,6 +112,11 @@ class ConfigItem:
return None
@classmethod
+ def _fetch_subelement_text(cls, parent, name):
+ sub_element = parent.findall(name)
+ return [e.text for e in sub_element]
+
+ @classmethod
def _fetch_text(cls, parent, name):
sub_element = cls._fetch_subelement(parent, name)
if sub_element is None:
@@ -67,7 +132,21 @@ class ConfigItem:
# shortDescription and description are optional
sdesc = cls._fetch_text(ci_element, 'shortDescription')
desc = cls._fetch_text(ci_element, 'description')
- return ConfigItem(name, sdesc, desc)
+ ci = ConfigItem(name, sdesc, desc)
+
+ langlist = cls._fetch_subelement(ci_element, 'languageList')
+ if langlist:
+ ci.iso639 = cls._fetch_subelement_text(langlist, 'iso639Id')
+
+ langlist = cls._fetch_subelement(ci_element, 'languageList')
+ if langlist:
+ ci.iso639 = cls._fetch_subelement_text(langlist, 'iso639Id')
+
+ countrylist = cls._fetch_subelement(ci_element, 'countryList')
+ if countrylist:
+ ci.iso3166 = cls._fetch_subelement_text(countrylist, 'iso3166')
+
+ return ci
except AssertionError as e:
endl = "\n" # f{} cannot contain backslashes
e.args = (f'\nFor element {prettyxml(elem)}\n{endl.join(e.args)}',)
@@ -99,3 +178,28 @@ def test_duplicate_models(rules_xml):
ci = ConfigItem.from_elem(model)
assert ci.name not in models, f'Duplicate model {ci.name}'
models[ci.name] = True
+
+
+def test_iso3166(layout):
+ pycountry = pytest.importorskip('pycountry')
+ country_codes = [c.alpha_2 for c in pycountry.countries]
+ for code in layout.iso3166:
+ assert code in country_codes, \
+ f'{layout.rulesfile}: unknown country code "{code}" in {layout.name}'
+
+
+def test_iso639(layout):
+ pycountry = pytest.importorskip('pycountry')
+
+ # A list of languages not in pycountry, so we need to special-case them
+ special_langs = [
+ 'ber', # Berber languages (collective), https://iso639-3.sil.org/code/ber
+ 'btb', # Beti (Cameroon), https://iso639-3.sil.org/code/btb
+ 'fox', # Formosan languages (collective), https://iso639-3.sil.org/code/fox
+ 'phi', # Philippine languages (collective), https://iso639-3.sil.org/code/phi
+ 'ovd', # Elfdalian, https://iso639-3.sil.org/code/ovd
+ ]
+ language_codes = [c.alpha_3 for c in pycountry.languages] + special_langs
+ for code in layout.iso639:
+ assert code in language_codes, \
+ f'{layout.rulesfile}: unknown language code "{code}" in {layout.name}'