From 89a0cf739104ffa63eb3db6d39344d539297ac6a Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 21 Nov 2023 11:13:16 +0100 Subject: rtems: Gather related items --- rtemsspec/rtems.py | 67 ++++++++++++++++++++++++++++++------ rtemsspec/tests/test_packagebuild.py | 24 +++++++++++++ rtemsspec/tests/test_rtems.py | 5 ++- 3 files changed, 85 insertions(+), 11 deletions(-) diff --git a/rtemsspec/rtems.py b/rtemsspec/rtems.py index cd68f036..9bf36b72 100644 --- a/rtemsspec/rtems.py +++ b/rtemsspec/rtems.py @@ -27,7 +27,7 @@ import base64 import hashlib import itertools -from typing import Any, Dict, List, Tuple, Union +from typing import Any, Dict, List, Set, Tuple, Union from rtemsspec.items import create_unique_link, Item, ItemCache, Link from rtemsspec.glossary import augment_glossary_terms @@ -161,7 +161,8 @@ _VALIDATOR = { } -def _validate_tree(item: Item) -> bool: +def _validate_tree(item: Item, related_items: Set[Item]) -> bool: + related_items.add(item) pre_qualified = is_pre_qualified(item) item["_pre_qualified"] = pre_qualified validated = True @@ -169,7 +170,7 @@ def _validate_tree(item: Item) -> bool: for link in itertools.chain(item.links_to_children(_CHILD_ROLES), item.links_to_parents(_PARENT_ROLES)): item_2 = link.item - validated = _validate_tree(item_2) and validated + validated = _validate_tree(item_2, related_items) and validated if link.role == "validation": role = _VALIDATION_METHOD[item_2.type] elif link.role == "requirement-refinement": @@ -229,15 +230,21 @@ def _fixup_pre_qualified(item: Item, types: List[str], item_2["_pre_qualified"] = False -def validate(item: Item) -> None: - """ Validates the item tree starting at the root item. """ - _validate_tree(item) - _validate_containers(item) - _fixup_pre_qualified(item, +def validate(root: Item) -> Set[Item]: + """ + Validates the item tree starting at the root item. + + Returns the set of items related to the root item. + """ + related_items: Set[Item] = set() + _validate_tree(root, related_items) + _validate_containers(root) + _fixup_pre_qualified(root, ["interface/appl-config-group", "interface/group"], ["interface-ingroup", "interface-ingroup-hidden"]) - _fixup_pre_qualified(item, ["interface/header-file"], + _fixup_pre_qualified(root, ["interface/header-file"], "interface-placement") + return related_items _API_INTERFACES = [ @@ -302,7 +309,11 @@ class RTEMSItemCache(BuildItem): _augment_with_interface_domains(self.item_cache) for glossary in ["/glossary-general", "/req/glossary"]: augment_glossary_terms(self.item_cache[glossary], []) - validate(self.item_cache[self["spec-root-uid"]]) + self.related_items = validate(self.item_cache[self["spec-root-uid"]]) + self.related_items_by_type: Dict[str, List[Item]] = {} + for item_2 in self.related_items: + self.related_items_by_type.setdefault(item_2.type, + []).append(item_2) # Calculate the overall item cache hash. Ignore QDP configuration # items and specification type changes. @@ -317,3 +328,39 @@ class RTEMSItemCache(BuildItem): def refresh_link(self, link: Link) -> None: link["hash"] = self._hash + + def get_related_items_by_type(self, types: Union[str, + List[str]]) -> List[Item]: + """ Gets related items by a list of types. """ + if isinstance(types, str): + types = [types] + items: List[Item] = [] + for type_name in types: + items.extend( + item for item in self.related_items_by_type.get(type_name, [])) + return sorted(items) + + def get_related_types_by_prefix( + self, prefix: Union[str, Tuple[str, ...]]) -> List[str]: + """ + Gets the types of the related items having one of the type prefixes. + """ + return [ + type_name for type_name in sorted(self.related_items_by_type) + if type_name.startswith(prefix) + ] + + def get_related_interfaces(self) -> List[Item]: + """ Gets the related interfaces. """ + return self.get_related_items_by_type( + self.get_related_types_by_prefix("interface/")) + + def get_related_requirements(self) -> List[Item]: + """ Gets the related requirements. """ + return self.get_related_items_by_type( + self.get_related_types_by_prefix("requirement/")) + + def get_related_interfaces_and_requirements(self) -> List[Item]: + """ Gets the related interfaces and requirements. """ + return self.get_related_items_by_type( + self.get_related_types_by_prefix(("interface/", "requirement/"))) diff --git a/rtemsspec/tests/test_packagebuild.py b/rtemsspec/tests/test_packagebuild.py index fa0309d9..6ff9c8e4 100644 --- a/rtemsspec/tests/test_packagebuild.py +++ b/rtemsspec/tests/test_packagebuild.py @@ -38,6 +38,7 @@ from rtemsspec.items import EmptyItem, Item, ItemCache, ItemGetValueContext from rtemsspec.packagebuild import BuildItem, BuildItemMapper, \ build_item_input, PackageBuildDirector from rtemsspec.packagebuildfactory import create_build_item_factory +from rtemsspec.rtems import RTEMSItemCache from rtemsspec.specverify import verify import rtemsspec.testrunner from rtemsspec.testrunner import Executable, Report, TestRunner @@ -144,6 +145,29 @@ def test_packagebuild(caplog, tmpdir, monkeypatch): assert "INFO /qdp/steps/b: is disabled" in log assert "INFO /qdp/steps/c: output is disabled: /qdp/output/b" in log + rtems_item_cache = director["/qdp/steps/rtems-item-cache"] + assert isinstance(rtems_item_cache, RTEMSItemCache) + related_items = rtems_item_cache.get_related_items_by_type("test-case") + assert [item.uid for item in related_items] == ["/rtems/test-case"] + related_items = rtems_item_cache.get_related_items_by_type(["test-case"]) + assert [item.uid for item in related_items] == ["/rtems/test-case"] + related_types = rtems_item_cache.get_related_types_by_prefix("requirement") + assert related_types == [ + "requirement/functional/function", "requirement/non-functional/design" + ] + related_items = rtems_item_cache.get_related_interfaces() + assert [item.uid for item in related_items] == [ + "/rtems/domain", "/rtems/group", "/rtems/group-acfg", "/rtems/header", + "/rtems/if" + ] + related_items = rtems_item_cache.get_related_requirements() + assert [item.uid for item in related_items] == ["/req/root", "/rtems/req"] + related_items = rtems_item_cache.get_related_interfaces_and_requirements() + assert [item.uid for item in related_items] == [ + "/req/root", "/rtems/domain", "/rtems/group", "/rtems/group-acfg", + "/rtems/header", "/rtems/if", "/rtems/req" + ] + director.build_package(None, ["/qdp/steps/a"]) log = get_and_clear_log(caplog) assert "INFO /qdp/steps/a: build is forced" in log diff --git a/rtemsspec/tests/test_rtems.py b/rtemsspec/tests/test_rtems.py index ae399f8a..28586612 100644 --- a/rtemsspec/tests/test_rtems.py +++ b/rtemsspec/tests/test_rtems.py @@ -124,7 +124,10 @@ def test_validate(tmpdir): item_cache_config = create_item_cache_config_and_copy_spec( tmpdir, "spec-rtems", with_spec_types=True) item_cache = ItemCache(item_cache_config) - assert not validate(item_cache["/req/root"]) + root = item_cache["/req/root"] + assert "_validated" not in root + validate(root) + assert not root["_validated"] api_items = {} gather_api_items(item_cache, api_items) assert [ -- cgit v1.2.3