From 297bb51556bf72c26acc26b411cfab8116376d6e Mon Sep 17 00:00:00 2001 From: M Hickford Date: Thu, 27 Jan 2022 21:51:03 +0000 Subject: Test that each layout has at least one country and language associated --- tests/test_rules_xml.py | 91 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/tests/test_rules_xml.py b/tests/test_rules_xml.py index 85ad717..3d36ecf 100644 --- a/tests/test_rules_xml.py +++ b/tests/test_rules_xml.py @@ -36,6 +36,14 @@ def iterate_layouts_variants(rules_xml): for variant in layout.iter('variant'): yield layout, variant +def iterate_config_items(rules_xml): + ''' + Return an iterator of configItem elements + ''' + tree = ET.parse(rules_xml) + root = tree.getroot() + return root.iter('configItem') + def pytest_generate_tests(metafunc): # for any test_foo function with an argument named rules_xml, @@ -54,6 +62,17 @@ def pytest_generate_tests(metafunc): for l, v in iterate_layouts_variants(f): layouts.append(Layout(f, l, v)) metafunc.parametrize('layout', layouts) + elif 'config_item' in metafunc.fixturenames: + rules_xml = list(_xkb_config_root().glob('rules/*.xml')) + assert rules_xml + config_items = [] + for f in rules_xml: + for item in iterate_config_items(f): + item = ConfigItem.from_elem(item) + item.rulesfile = f + config_items.append(item) + metafunc.parametrize('config_item', config_items) + @@ -72,23 +91,29 @@ class Layout: 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 + if self.variant and self.variant.iso3166 is not None: + return self.variant.iso3166 or [] + # inherit from parent + return self.layout.iso3166 or [] @property def iso639(self): - return (self.variant or self.layout).iso639 + if self.variant and self.variant.iso639 is not None: + return self.variant.iso639 or [] + # inherit from parent + return self.layout.iso639 or [] + + @property + def popularity(self): + return self.variant.popularity if self.variant else self.layout.popularity + + @property + def shortDescription(self): + if self.variant and self.variant.shortDescription: + return self.variant.shortDescription + return self.layout.shortDescription def prettyxml(element): @@ -100,8 +125,9 @@ class ConfigItem: self.name = name self.shortDescription = shortDescription self.description = description - self.iso639 = [] - self.iso3166 = [] + self.iso639 = None + self.iso3166 = None + self.popularity = None @classmethod def _fetch_subelement(cls, parent, name): @@ -126,17 +152,14 @@ class ConfigItem: @classmethod def from_elem(cls, elem): try: - ci_element = cls._fetch_subelement(elem, 'configItem') + ci_element = elem if elem.tag == "configItem" else cls._fetch_subelement(elem, 'configItem') name = cls._fetch_text(ci_element, 'name') assert name is not None # shortDescription and description are optional sdesc = cls._fetch_text(ci_element, 'shortDescription') desc = cls._fetch_text(ci_element, 'description') ci = ConfigItem(name, sdesc, desc) - - langlist = cls._fetch_subelement(ci_element, 'languageList') - if langlist: - ci.iso639 = cls._fetch_subelement_text(langlist, 'iso639Id') + ci.popularity = ci_element.attrib.get('popularity') langlist = cls._fetch_subelement(ci_element, 'languageList') if langlist: @@ -144,7 +167,7 @@ class ConfigItem: countrylist = cls._fetch_subelement(ci_element, 'countryList') if countrylist: - ci.iso3166 = cls._fetch_subelement_text(countrylist, 'iso3166') + ci.iso3166 = cls._fetch_subelement_text(countrylist, 'iso3166Id') return ci except AssertionError as e: @@ -180,15 +203,39 @@ def test_duplicate_models(rules_xml): models[ci.name] = True +def test_exotic(config_item): + """All items in extras should be marked exotic""" + if config_item.rulesfile.stem.endswith('extras'): + assert config_item.popularity == "exotic", f"{config_item.rulesfile}: item {config_item.name} does not have popularity exotic" + else: + assert config_item.popularity != "exotic", f"{config_item.rulesfile}: item {config_item.name} has popularity exotic" + + +def test_short_description(layout): + assert layout.shortDescription, f'{layout.rulesfile}: layout {layout.name} missing shortDescription' + + def test_iso3166(layout): + """Typically layouts should specify at least one country code""" pycountry = pytest.importorskip('pycountry') country_codes = [c.alpha_2 for c in pycountry.countries] + expected_without_country = [ + "apl", # programming + "brai", # Braille not specific to any country + "custom", + "epo", # Esperanto not native to any country + "trans", # international + ] + for code in layout.iso3166: assert code in country_codes, \ f'{layout.rulesfile}: unknown country code "{code}" in {layout.name}' + assert layout.iso3166 or layout.layout.name in expected_without_country, f"{layout.rulesfile}: layout {layout.name} has no countries associated" + def test_iso639(layout): + """Typically layouts should should specify at least one language code""" pycountry = pytest.importorskip('pycountry') # A list of languages not in pycountry, so we need to special-case them @@ -200,6 +247,10 @@ def test_iso639(layout): 'ovd', # Elfdalian, https://iso639-3.sil.org/code/ovd ] language_codes = [c.alpha_3 for c in pycountry.languages] + special_langs + expected_without_language = ["brai", "custom", "trans"] + for code in layout.iso639: assert code in language_codes, \ f'{layout.rulesfile}: unknown language code "{code}" in {layout.name}' + + assert layout.iso639 or layout.layout.name in expected_without_language, f"{layout.rulesfile}: layout {layout.name} has no languages associated" -- cgit v1.2.1