diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index ed7090ffd..1b1fd6383 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -141,10 +141,22 @@ jobs: needs: [docs_bzl, bazel_tests] runs-on: ubuntu-latest steps: - - name: Download test reports + # Both Python versions produce Bazel reports at the same paths. Download + # one version for documentation so concurrent artifact extraction cannot + # corrupt overlapping files or duplicate test cases in the documentation. + # Both versions remain available as separate artifacts for inspection. + - name: Download Bazel test reports for documentation uses: actions/download-artifact@v8 with: - pattern: tests-report-* + name: tests-report-bazel-3.12 + path: tests-report + + # The docs.bzl report filenames include their Python version and suite, + # so these artifacts can be merged without overlapping paths. + - name: Download docs.bzl test reports + uses: actions/download-artifact@v8 + with: + pattern: tests-report-docs-bzl-* path: tests-report merge-multiple: true diff --git a/docs/internals/requirements/requirements.rst b/docs/internals/requirements/requirements.rst index 7f267bb61..97a32eda5 100644 --- a/docs/internals/requirements/requirements.rst +++ b/docs/internals/requirements/requirements.rst @@ -631,14 +631,16 @@ Architecture Attributes :id: tool_req__docs_arch_link_safety_to_req :tags: Architecture :implemented: PARTIAL - :version: 2 + :version: 3 :satisfies: gd_req__arch_linkage_requirement[version==1] :parent_covered: YES Docs-as-Code shall enforce that architecture elements of type :need:`tool_req__docs_arch_types` with ``safety != QM`` are linked to at least one - requirements of type :need:`tool_req__docs_req_types` with the exact same ``safety`` - value. + requirement of type :need:`tool_req__docs_req_types` with ``safety != QM``. + For features and components, this shall be checked through incoming + ``satisfied_by`` allocations; for architectural views and interfaces, through + ``fulfils``. Additional QM requirements are permitted. .. note:: implemented as info to enable non-breaking transition @@ -646,13 +648,16 @@ Architecture Attributes :id: tool_req__docs_arch_link_qm_to_safety_req :tags: Architecture :implemented: PARTIAL - :version: 2 + :version: 3 :satisfies: gd_req__arch_linkage_requirement[version==1] :parent_covered: YES Docs-as-Code shall enforce that architecture elements of type :need:`tool_req__docs_arch_types` with ``safety == QM`` are not linked to requirements of type :need:`tool_req__docs_req_types` with ``safety != QM``. + This shall be checked for mandatory ``satisfied_by`` allocations of feature and + component requirements and for optional ``fulfils`` links when present. + QM requirements may be allocated to QM or safety architecture elements. .. tool_req:: Restrict links for safety requirements diff --git a/src/extensions/docs/rst_filebased_testing.md b/src/extensions/docs/rst_filebased_testing.md index 9dc8b2635..0b8fe2716 100644 --- a/src/extensions/docs/rst_filebased_testing.md +++ b/src/extensions/docs/rst_filebased_testing.md @@ -123,6 +123,20 @@ You only need to match a unique substring, not the full message. For example: For `:expect:` use as much as possible of the error message, however for `:expect_not:` use as little as possible to catch all other issues that MIGHT occur and to avoid for typos etc. +### Informational diagnostics + +Checks marked `info_only` report to the Sphinx status stream. Use +`:expect_info:` and `:expect_info_not:` to assert these diagnostics at the +need's own location. The existing `:expect:` and `:expect_not:` options continue +to check warnings, so a fixture can distinguish compatibility errors from +informational allocation-completeness diagnostics. + + .. comp:: Safety component without an allocated safety requirement + :id: comp__example__missing_allocation + :safety: ASIL_B + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :expect_not: must have at least one safety requirement allocated via satisfied_by. + ### Setup needs Prerequisite needs (link targets, shared fixtures) carry no `:expect:` / diff --git a/src/extensions/score_metamodel/metamodel.yaml b/src/extensions/score_metamodel/metamodel.yaml index 12d39f51b..214aa2e67 100644 --- a/src/extensions/score_metamodel/metamodel.yaml +++ b/src/extensions/score_metamodel/metamodel.yaml @@ -1238,20 +1238,46 @@ graph_checks: # req-Id: tool_req__docs_arch_link_qm_to_safety_req tool_req__docs_arch_link_qm_to_safety_req: needs: - include: feat_arc_sta, logic_arc_int, logic_arc_int_op, comp_arc_sta, real_arc_int, real_arc_int_op + include: feat, feat_arc_sta, feat_arc_dyn, logic_arc_int, logic_arc_int_op, comp, comp_arc_sta, comp_arc_dyn, real_arc_int, real_arc_int_op condition: safety == QM check_all: - fulfils: safety != QM - explanation: An QM architecture element cannot implement ASIL requirements. + fulfils: safety == QM + explanation: A QM architecture element cannot fulfil safety requirements. + info_only: true + + # Allocation compatibility is checked from the requirement, where the + # mandatory satisfied_by link identifies every allocated feature/component. + # QM requirements impose no safety restriction on their allocation targets. + # req-Id: tool_req__docs_arch_link_qm_to_safety_req + tool_req__docs_req_allocation_safety: + needs: + include: feat_req, comp_req + condition: safety != QM + check_all: + satisfied_by: safety != QM + explanation: A safety requirement cannot be allocated to a QM architecture element. + info_only: true + + # Features/components receive requirements through incoming satisfied_by + # links. Their optional fulfils links do not replace this allocation. + # req-Id: tool_req__docs_arch_link_safety_to_req + tool_req__docs_allocation_safety_to_req: + needs: + include: feat, comp + condition: safety != QM + check_one: + satisfied_by_back: safety != QM + explanation: A safety relevant feature or component must have at least one safety requirement allocated via satisfied_by. + info_only: true # req-Id: tool_req__docs_arch_link_safety_to_req tool_req__docs_arch_link_safety_to_req: needs: - include: feat_arc_sta, feat_arc_dyn, logic_arc_int, comp, comp_arc_sta, comp_arc_dyn, real_arc_int + include: feat_arc_sta, feat_arc_dyn, logic_arc_int, comp_arc_sta, comp_arc_dyn, real_arc_int condition: safety != QM check_one: - fulfils: safety == self.safety - explanation: A safety relevant architecture element must be linked to at least one requirement with the exact same safety value via fulfils. + fulfils: safety != QM + explanation: A safety relevant architecture element must be linked to at least one safety requirement via fulfils. info_only: true # req-Id: tool_req__docs_req_arch_link_safety_to_arch diff --git a/src/extensions/score_metamodel/tests/__init__.py b/src/extensions/score_metamodel/tests/__init__.py index d436ad88b..96cb3d934 100644 --- a/src/extensions/score_metamodel/tests/__init__.py +++ b/src/extensions/score_metamodel/tests/__init__.py @@ -33,8 +33,9 @@ def __init__(self): self._mock_logger = MagicMock(spec=SphinxLoggerAdapter) self._mock_logger.warning = MagicMock() self._mock_logger.info = MagicMock() - app_path = MagicMock() - super().__init__(self._mock_logger, app_path) + # Direct pytest uses the source-root prefix; Bazel runfiles omit it. + # A concrete root keeps both location formats meaningful to assert. + super().__init__(self._mock_logger, ".") def assert_no_warnings(self): if self.warnings: @@ -75,7 +76,9 @@ def assert_warning(self, expected_substring: str, expect_location: bool = True): assert kwargs["type"] == "score_metamodel" if expect_location: - assert kwargs["location"] == "docname.rst:42", kwargs + assert kwargs["location"] in ("docname.rst:42", "./docname.rst:42"), ( + kwargs + ) def assert_info(self, expected_substring: str, expect_location: bool = True): """ @@ -102,7 +105,9 @@ def assert_info(self, expected_substring: str, expect_location: bool = True): assert kwargs["type"] == "score_metamodel" if expect_location: - assert kwargs["location"] == "docname.rst:42", kwargs + assert kwargs["location"] in ("docname.rst:42", "./docname.rst:42"), ( + kwargs + ) return FakeCheckLogger() diff --git a/src/extensions/score_metamodel/tests/rst/conf.py b/src/extensions/score_metamodel/tests/rst/conf.py index a3de12782..a521b7a3e 100644 --- a/src/extensions/score_metamodel/tests/rst/conf.py +++ b/src/extensions/score_metamodel/tests/rst/conf.py @@ -41,22 +41,6 @@ def setup(app: Sphinx): add_needs_fields: dict[str, Any] = { - "expect_not": { - "description": "Partial string that is not exepcted to be in rst test warnings", - "schema": { - "type": "array", - "items": {"type": "string"}, - }, - "nullable": True, - }, - "expect": { - "description": "String that is exepcted to be in rst test warnings", - "schema": { - "type": "array", - "items": {"type": "string"}, - }, - "nullable": True, - }, "test_type": { "description": "The test type that this test has", "schema": { @@ -71,26 +55,25 @@ def setup(app: Sphinx): }, "nullable": True, }, - "fully_verifies_list": { - "description": "List of requirements that this RST test fully verifies", - "schema": { - "type": "array", - "items": {"type": "string"}, - }, - "nullable": True, - }, - "partially_verifies_list": { - "description": "List of requirements that this RST test partially verifies", - "schema": { - "type": "array", - "items": {"type": "string"}, - }, - "nullable": True, - }, } + for option, description in { + "expect": "Expected substrings in warning diagnostics", + "expect_not": "Forbidden substrings in warning diagnostics", + "expect_info": "Expected substrings in informational diagnostics", + "expect_info_not": "Forbidden substrings in informational diagnostics", + "fully_verifies_list": "Requirements that this RST test fully verifies", + "partially_verifies_list": "Requirements that this RST test partially verifies", + }.items(): + add_needs_fields[option] = { + "description": description, + "schema": {"type": "array", "items": {"type": "string"}}, + "nullable": True, + } add_options_regex_base = { - "expect_not": "^.*$", "expect": "^.*$", + "expect_not": "^.*$", + "expect_info": "^.*$", + "expect_info_not": "^.*$", } add_options_test_metadata = { "derivation_technique": "^.*$", diff --git a/src/extensions/score_metamodel/tests/rst/graph/test_allocation_safety.rst b/src/extensions/score_metamodel/tests/rst/graph/test_allocation_safety.rst new file mode 100644 index 000000000..63f52e1d1 --- /dev/null +++ b/src/extensions/score_metamodel/tests/rst/graph/test_allocation_safety.rst @@ -0,0 +1,870 @@ +.. + # ******************************************************************************* + # Copyright (c) 2026 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + +Safety requirement allocation +============================= + +.. test_metadata:: + :id: test_metadata__allocation_safety + :fully_verifies_list: tool_req__docs_arch_link_safety_to_req[version==3], tool_req__docs_arch_link_qm_to_safety_req[version==3] + :test_type: requirements_based + :derivation_technique: requirements_based + + Verifies safety compatibility and allocation completeness independently. + QM requirements may be allocated to QM or ASIL_B features and components; + ASIL_B requirements must not be allocated to QM elements. Safety elements + need at least one safety requirement, while mixed allocations are accepted. + Features and components use incoming satisfied_by allocations for completeness; + views and interfaces use fulfils. Optional component AoU links do not replace + requirement allocations. Compatibility violations are warnings; missing + safety requirements are informational diagnostics. + +Feature allocations via satisfied_by +------------------------------------ + +.. feat:: QM feat without requirements + :id: feat__safety_alloc__feat_qm_none + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. feat:: QM feat with QM requirements + :id: feat__safety_alloc__feat_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. feat_req:: QM requirement allocated to QM feat + :id: feat_req__safety_alloc__feat_qm_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_qm_qm + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. feat:: QM feat with safety requirements + :id: feat__safety_alloc__feat_qm_asil + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. feat_req:: ASIL_B requirement allocated to QM feat + :id: feat_req__safety_alloc__feat_qm_asil_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_qm_asil + :expect_info: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. feat:: QM feat with mixed requirements + :id: feat__safety_alloc__feat_qm_mixed + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. feat_req:: QM requirement allocated to QM feat + :id: feat_req__safety_alloc__feat_qm_mixed_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_qm_mixed + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. feat_req:: ASIL_B requirement allocated to QM feat + :id: feat_req__safety_alloc__feat_qm_mixed_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_qm_mixed + :expect_info: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. feat:: ASIL_B feat without requirements + :id: feat__safety_alloc__feat_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :expect_not: must have at least one safety requirement allocated via satisfied_by. + :expect_info_not: No linked need in `fulfils` + +.. feat:: ASIL_B feat with QM requirements + :id: feat__safety_alloc__feat_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :expect_not: must have at least one safety requirement allocated via satisfied_by. + :expect_info_not: No linked need in `fulfils` + +.. feat_req:: QM requirement allocated to ASIL_B feat + :id: feat_req__safety_alloc__feat_asil_b_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_asil_b_qm + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. feat:: ASIL_B feat with safety requirements + :id: feat__safety_alloc__feat_asil_b_asil + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. feat_req:: ASIL_B requirement allocated to ASIL_B feat + :id: feat_req__safety_alloc__feat_asil_b_asil_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_asil_b_asil + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. feat:: ASIL_B feat with mixed requirements + :id: feat__safety_alloc__feat_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. feat_req:: QM requirement allocated to ASIL_B feat + :id: feat_req__safety_alloc__feat_asil_b_mixed_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_asil_b_mixed + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. feat_req:: ASIL_B requirement allocated to ASIL_B feat + :id: feat_req__safety_alloc__feat_asil_b_mixed_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_asil_b_mixed + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +Component allocations via satisfied_by +-------------------------------------- + +.. comp:: QM comp without requirements + :id: comp__safety_alloc__comp_qm_none + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. comp:: QM comp with QM requirements + :id: comp__safety_alloc__comp_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. comp_req:: QM requirement allocated to QM comp + :id: comp_req__safety_alloc__comp_qm_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_qm_qm + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. comp:: QM comp with safety requirements + :id: comp__safety_alloc__comp_qm_asil + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. comp_req:: ASIL_B requirement allocated to QM comp + :id: comp_req__safety_alloc__comp_qm_asil_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_qm_asil + :expect_info: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. comp:: QM comp with mixed requirements + :id: comp__safety_alloc__comp_qm_mixed + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. comp_req:: QM requirement allocated to QM comp + :id: comp_req__safety_alloc__comp_qm_mixed_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_qm_mixed + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. comp_req:: ASIL_B requirement allocated to QM comp + :id: comp_req__safety_alloc__comp_qm_mixed_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_qm_mixed + :expect_info: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. comp:: ASIL_B comp without requirements + :id: comp__safety_alloc__comp_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :expect_not: must have at least one safety requirement allocated via satisfied_by. + :expect_info_not: No linked need in `fulfils` + +.. comp:: ASIL_B comp with QM requirements + :id: comp__safety_alloc__comp_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :expect_not: must have at least one safety requirement allocated via satisfied_by. + :expect_info_not: No linked need in `fulfils` + +.. comp_req:: QM requirement allocated to ASIL_B comp + :id: comp_req__safety_alloc__comp_asil_b_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_asil_b_qm + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. comp:: ASIL_B comp with safety requirements + :id: comp__safety_alloc__comp_asil_b_asil + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. comp_req:: ASIL_B requirement allocated to ASIL_B comp + :id: comp_req__safety_alloc__comp_asil_b_asil_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_asil_b_asil + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. comp:: ASIL_B comp with mixed requirements + :id: comp__safety_alloc__comp_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: must have at least one safety requirement allocated via satisfied_by., No linked need in `fulfils` + :expect_not: must have at least one safety requirement allocated via satisfied_by. + +.. comp_req:: QM requirement allocated to ASIL_B comp + :id: comp_req__safety_alloc__comp_asil_b_mixed_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_asil_b_mixed + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +.. comp_req:: ASIL_B requirement allocated to ASIL_B comp + :id: comp_req__safety_alloc__comp_asil_b_mixed_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_asil_b_mixed + :expect_info_not: A safety requirement cannot be allocated to a QM architecture element. + + Requirement description. + +Optional fulfils links +---------------------- + +.. feat_req:: QM fulfils target + :id: feat_req__safety_alloc__fulfils_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + + Requirement description. + +.. feat_req:: ASIL_B fulfils target + :id: feat_req__safety_alloc__fulfils_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + + Requirement description. + +.. comp_req:: QM fulfils target + :id: comp_req__safety_alloc__fulfils_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + + Requirement description. + +.. comp_req:: ASIL_B fulfils target + :id: comp_req__safety_alloc__fulfils_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + + Requirement description. + +.. aou_req:: QM fulfils target + :id: aou_req__safety_alloc__fulfils_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + + Requirement description. + +.. aou_req:: ASIL_B fulfils target + :id: aou_req__safety_alloc__fulfils_asil_b + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + + Requirement description. + +.. feat_arc_sta:: QM feat_arc_sta without fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_qm_none + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. feat_arc_sta:: QM feat_arc_sta with QM fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + :fulfils: feat_req__safety_alloc__fulfils_qm + +.. feat_arc_sta:: QM feat_arc_sta with safety fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_qm_asil + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info: A QM architecture element cannot fulfil safety requirements. + :expect_info_not: must be linked to at least one safety requirement via fulfils. + :fulfils: feat_req__safety_alloc__fulfils_asil_b + +.. feat_arc_sta:: QM feat_arc_sta with mixed fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_qm_mixed + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info: A QM architecture element cannot fulfil safety requirements. + :expect_info_not: must be linked to at least one safety requirement via fulfils. + :fulfils: feat_req__safety_alloc__fulfils_qm, feat_req__safety_alloc__fulfils_asil_b + +.. feat_arc_sta:: ASIL_B feat_arc_sta without fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. feat_arc_sta:: ASIL_B feat_arc_sta with QM fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + :fulfils: feat_req__safety_alloc__fulfils_qm + +.. feat_arc_sta:: ASIL_B feat_arc_sta with safety fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_asil_b_asil + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + :fulfils: feat_req__safety_alloc__fulfils_asil_b + +.. feat_arc_sta:: ASIL_B feat_arc_sta with mixed fulfils targets + :id: feat_arc_sta__safety_alloc__fulfils_feat_arc_sta_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + :fulfils: feat_req__safety_alloc__fulfils_qm, feat_req__safety_alloc__fulfils_asil_b + +.. comp:: QM comp without fulfils targets + :id: comp__safety_alloc__fulfils_comp_qm_none + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., No linked need in `fulfils` + +.. comp:: QM comp with QM fulfils targets + :id: comp__safety_alloc__fulfils_comp_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., No linked need in `fulfils` + :fulfils: aou_req__safety_alloc__fulfils_qm + +.. comp:: QM comp with safety fulfils targets + :id: comp__safety_alloc__fulfils_comp_qm_asil + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info: A QM architecture element cannot fulfil safety requirements. + :expect_info_not: No linked need in `fulfils` + :fulfils: aou_req__safety_alloc__fulfils_asil_b + +.. comp:: QM comp with mixed fulfils targets + :id: comp__safety_alloc__fulfils_comp_qm_mixed + :safety: QM + :security: NO + :status: valid + :version: 1 + :expect_info: A QM architecture element cannot fulfil safety requirements. + :expect_info_not: No linked need in `fulfils` + :fulfils: aou_req__safety_alloc__fulfils_qm, aou_req__safety_alloc__fulfils_asil_b + +.. comp:: ASIL_B comp without fulfils targets + :id: comp__safety_alloc__fulfils_comp_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., No linked need in `fulfils` + :expect_info: must have at least one safety requirement allocated via satisfied_by. + +.. comp:: ASIL_B comp with QM fulfils targets + :id: comp__safety_alloc__fulfils_comp_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., No linked need in `fulfils` + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :fulfils: aou_req__safety_alloc__fulfils_qm + +.. comp:: ASIL_B comp with safety fulfils targets + :id: comp__safety_alloc__fulfils_comp_asil_b_asil + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., No linked need in `fulfils` + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :fulfils: aou_req__safety_alloc__fulfils_asil_b + +.. comp:: ASIL_B comp with mixed fulfils targets + :id: comp__safety_alloc__fulfils_comp_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements., No linked need in `fulfils` + :expect_info: must have at least one safety requirement allocated via satisfied_by. + :fulfils: aou_req__safety_alloc__fulfils_qm, aou_req__safety_alloc__fulfils_asil_b + +.. feat_arc_dyn:: QM feat_arc_dyn cannot fulfil a safety requirement + :id: feat_arc_dyn__safety_alloc__fulfils_feat_arc_dyn_unsafe + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_asil_b + :expect_info: A QM architecture element cannot fulfil safety requirements. + +.. logic_arc_int:: QM logic_arc_int cannot fulfil a safety requirement + :id: logic_arc_int__safety_alloc__fulfils_logic_arc_int_unsafe + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_asil_b + :expect_info: A QM architecture element cannot fulfil safety requirements. + +.. comp_arc_sta:: QM comp_arc_sta cannot fulfil a safety requirement + :id: comp_arc_sta__safety_alloc__fulfils_comp_arc_sta_unsafe + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_asil_b + :expect_info: A QM architecture element cannot fulfil safety requirements. + +.. comp_arc_dyn:: QM comp_arc_dyn cannot fulfil a safety requirement + :id: comp_arc_dyn__safety_alloc__fulfils_comp_arc_dyn_unsafe + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_asil_b + :expect_info: A QM architecture element cannot fulfil safety requirements. + +.. real_arc_int:: QM real_arc_int cannot fulfil a safety requirement + :id: real_arc_int__safety_alloc__fulfils_real_arc_int_unsafe + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_asil_b + :expect_info: A QM architecture element cannot fulfil safety requirements. + +Completeness of other views and interfaces +------------------------------------------ + +.. feat_arc_dyn:: QM feat_arc_dyn with qm requirements + :id: feat_arc_dyn__safety_alloc__completeness_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. feat_arc_dyn:: ASIL_B feat_arc_dyn with none requirements + :id: feat_arc_dyn__safety_alloc__completeness_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. feat_arc_dyn:: ASIL_B feat_arc_dyn with qm requirements + :id: feat_arc_dyn__safety_alloc__completeness_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. feat_arc_dyn:: ASIL_B feat_arc_dyn with mixed requirements + :id: feat_arc_dyn__safety_alloc__completeness_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_qm, feat_req__safety_alloc__fulfils_asil_b + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. logic_arc_int:: QM logic_arc_int with qm requirements + :id: logic_arc_int__safety_alloc__completeness_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. logic_arc_int:: ASIL_B logic_arc_int with none requirements + :id: logic_arc_int__safety_alloc__completeness_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. logic_arc_int:: ASIL_B logic_arc_int with qm requirements + :id: logic_arc_int__safety_alloc__completeness_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. logic_arc_int:: ASIL_B logic_arc_int with mixed requirements + :id: logic_arc_int__safety_alloc__completeness_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: feat_req__safety_alloc__fulfils_qm, feat_req__safety_alloc__fulfils_asil_b + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. comp_arc_sta:: QM comp_arc_sta with qm requirements + :id: comp_arc_sta__safety_alloc__completeness_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. comp_arc_sta:: ASIL_B comp_arc_sta with none requirements + :id: comp_arc_sta__safety_alloc__completeness_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. comp_arc_sta:: ASIL_B comp_arc_sta with qm requirements + :id: comp_arc_sta__safety_alloc__completeness_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. comp_arc_sta:: ASIL_B comp_arc_sta with mixed requirements + :id: comp_arc_sta__safety_alloc__completeness_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_qm, comp_req__safety_alloc__fulfils_asil_b + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. comp_arc_dyn:: QM comp_arc_dyn with qm requirements + :id: comp_arc_dyn__safety_alloc__completeness_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. comp_arc_dyn:: ASIL_B comp_arc_dyn with none requirements + :id: comp_arc_dyn__safety_alloc__completeness_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. comp_arc_dyn:: ASIL_B comp_arc_dyn with qm requirements + :id: comp_arc_dyn__safety_alloc__completeness_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. comp_arc_dyn:: ASIL_B comp_arc_dyn with mixed requirements + :id: comp_arc_dyn__safety_alloc__completeness_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :fulfils: comp_req__safety_alloc__fulfils_qm, comp_req__safety_alloc__fulfils_asil_b + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. real_arc_int:: QM real_arc_int with qm requirements + :id: real_arc_int__safety_alloc__completeness_qm_qm + :safety: QM + :security: NO + :status: valid + :version: 1 + :language: cpp + :fulfils: comp_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +.. real_arc_int:: ASIL_B real_arc_int with none requirements + :id: real_arc_int__safety_alloc__completeness_asil_b_none + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :language: cpp + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. real_arc_int:: ASIL_B real_arc_int with qm requirements + :id: real_arc_int__safety_alloc__completeness_asil_b_qm + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :language: cpp + :fulfils: comp_req__safety_alloc__fulfils_qm + :expect_info_not: A QM architecture element cannot fulfil safety requirements. + :expect_info: must be linked to at least one safety requirement via fulfils. + +.. real_arc_int:: ASIL_B real_arc_int with mixed requirements + :id: real_arc_int__safety_alloc__completeness_asil_b_mixed + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :language: cpp + :fulfils: comp_req__safety_alloc__fulfils_qm, comp_req__safety_alloc__fulfils_asil_b + :expect_info_not: A QM architecture element cannot fulfil safety requirements., must be linked to at least one safety requirement via fulfils. + +Safety requirements allocated to multiple elements +-------------------------------------------------- + +An ASIL_B requirement must reject every QM allocation target, even when another +allocation target is ASIL_B. + +.. feat_req:: Safety requirement allocated to both QM and ASIL_B feat + :id: feat_req__safety_alloc__multiple_feat_targets + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :valid_from: v1.0 + :satisfied_by: feat__safety_alloc__feat_asil_b_asil, feat__safety_alloc__feat_qm_asil + :expect_not: A safety requirement cannot be allocated to a QM architecture element. + :expect_info: Parent need `feat__safety_alloc__feat_qm_asil` does not fulfill condition `safety != QM`. + + Every allocated element must support the requirement's safety classification. + +.. comp_req:: Safety requirement allocated to both QM and ASIL_B comp + :id: comp_req__safety_alloc__multiple_comp_targets + :safety: ASIL_B + :security: NO + :status: valid + :version: 1 + :reqtype: Functional + :satisfied_by: comp__safety_alloc__comp_asil_b_asil, comp__safety_alloc__comp_qm_asil + :expect_not: A safety requirement cannot be allocated to a QM architecture element. + :expect_info: Parent need `comp__safety_alloc__comp_qm_asil` does not fulfill condition `safety != QM`. + + Every allocated element must support the requirement's safety classification. diff --git a/src/extensions/score_metamodel/tests/test_rules_file_based.py b/src/extensions/score_metamodel/tests/test_rules_file_based.py index 28bc1b37a..4e4ac2041 100644 --- a/src/extensions/score_metamodel/tests/test_rules_file_based.py +++ b/src/extensions/score_metamodel/tests/test_rules_file_based.py @@ -141,32 +141,33 @@ def test_count_need_objects_ignores_nested_non_need_directives( assert rst_data.found_objects == [1, 11] -def filter_warnings_by_position( +def filter_messages_by_position( rst_data: RstData, line_nr: int, - warnings: list[str], + messages: list[str], + level: str = "WARNING", ) -> list[str]: + """Select diagnostics for this need and strip the location and severity. + + Sphinx emits info messages without a severity prefix. Matching the need's + location keeps expectations from matching diagnostics for another need. """ - Filtering only warnings that belong to this file & line. But also deleting the prefix. - Filter out the filepath:linenr prefix from warning. So that the 'expect-not' can be generic - Without having to pay attention to the filename for example 'EXPECT-NOT: test' then matching - a random warning because 'test' is in the filename of 'graph/test_graph_checks.rst' - """ - prefix = f"{rst_data.filename}:{line_nr}: WARNING:" - return [warning.removeprefix(prefix) for warning in warnings if prefix in warning] + severity = f" {level}:" if level else "" + prefix = f"{rst_data.filename}:{line_nr}:{severity}" + return [message.removeprefix(prefix) for message in messages if prefix in message] -def warning_matches( +def message_matches( rst_data: RstData, line_nr: int, expected_message: str, - warnings: list[str], + messages: list[str], + level: str = "WARNING", ) -> str | None: - ### Checks if any element of the warning list includes the given warning info. - # It returns the matched warning or None if no match is found. - for warning in filter_warnings_by_position(rst_data, line_nr, warnings): - if expected_message in warning: - return warning + """Return a matching diagnostic at the need's location, if present.""" + for message in filter_messages_by_position(rst_data, line_nr, messages, level): + if expected_message in message: + return message return None @@ -271,10 +272,18 @@ def _get_expectations(need: NeedItem | NeedsExtendType, option_name: str) -> lis return [] -def _check_need_warnings( - rst_data: RstData, need: NeedItem | NeedsExtendType, warnings: list[str] +def _check_need_messages( + rst_data: RstData, + need: NeedItem | NeedsExtendType, + messages: list[str], + *, + info: bool = False, ) -> None: - ### Verify a need's 'expect' / 'expect_not' annotations against the warnings. + """Check location-scoped expectations in the warning or info stream.""" + level = "" if info else "WARNING" + label = "info" if info else "warning" + expected_option = "expect_info" if info else "expect" + unexpected_option = "expect_info_not" if info else "expect_not" # needextend-affected needs (those with 'modifications') are skipped here; # their expectations are checked on the extending need itself. if need.get("modifications"): @@ -282,29 +291,29 @@ def _check_need_warnings( line_nr = need.get("lineno") - for raw in _get_expectations(need, "expect"): + for raw in _get_expectations(need, expected_option): expected = raw.strip() - if warning_matches(rst_data, line_nr, expected, warnings): + if message_matches(rst_data, line_nr, expected, messages, level): continue - actual = filter_warnings_by_position(rst_data, line_nr, warnings) + actual = filter_messages_by_position(rst_data, line_nr, messages, level) actual_block = "".join(f" - {a}\n" for a in actual) pytest.fail( - f"{rst_data.filename}:{line_nr} Expected warning not found:\n" - f" Expected warning in line: '{line_nr}'\n" - f" Expected warning string: '{expected}'\n" - f" Actual warning:\n{actual_block}", + f"{rst_data.filename}:{line_nr} Expected {label} not found:\n" + f" Expected {label} in line: '{line_nr}'\n" + f" Expected {label} string: '{expected}'\n" + f" Actual {label}:\n{actual_block}", pytrace=False, ) - for raw in _get_expectations(need, "expect_not"): + for raw in _get_expectations(need, unexpected_option): not_expected = raw.strip() - unexpected = warning_matches(rst_data, line_nr, not_expected, warnings) + unexpected = message_matches(rst_data, line_nr, not_expected, messages, level) if not unexpected: continue pytest.fail( - f"{rst_data.filename}:{line_nr} Unexpected warning found:\n" - f" Not expected warning found on line'{line_nr}'\n" - f" Warning Text NOT expected: '{not_expected}'\n" + f"{rst_data.filename}:{line_nr} Unexpected {label} found:\n" + f" Not expected {label} found on line'{line_nr}'\n" + f" {label.capitalize()} text NOT expected: '{not_expected}'\n" f" Actual: '{unexpected}'\n", pytrace=False, ) @@ -361,5 +370,7 @@ def test_rst_files( # Collect warnings and verify each need's expectations warnings = _collect_warnings(app) + infos = [strip_ansi_codes(line) for line in app.status.getvalue().splitlines()] for need in all_needs: - _check_need_warnings(rst_data, need, warnings) + _check_need_messages(rst_data, need, warnings) + _check_need_messages(rst_data, need, infos, info=True) diff --git a/src/tests/docs_bzl/scenarios/reference_integration/_expected/docs/modules/modern_module/components/unlinked_component/generated_metamodel/index.html b/src/tests/docs_bzl/scenarios/reference_integration/_expected/docs/modules/modern_module/components/unlinked_component/generated_metamodel/index.html index 6c142df79..2bfdc8081 100644 --- a/src/tests/docs_bzl/scenarios/reference_integration/_expected/docs/modules/modern_module/components/unlinked_component/generated_metamodel/index.html +++ b/src/tests/docs_bzl/scenarios/reference_integration/_expected/docs/modules/modern_module/components/unlinked_component/generated_metamodel/index.html @@ -450,7 +450,7 @@
The source metamodel contains 1308 lines.
+The source metamodel contains 1334 lines.